Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pybind11
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
pybind11
Commits
d400f60c
Commit
d400f60c
authored
Apr 05, 2017
by
Cris Luengo
Committed by
Dean Moldovan
May 08, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Python buffer objects can have negative strides.
parent
2b941b38
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
40 additions
and
34 deletions
+40
-34
docs/advanced/pycpp/numpy.rst
+5
-5
include/pybind11/numpy.h
+26
-24
tests/test_buffers.cpp
+2
-2
tests/test_numpy_array.cpp
+2
-2
tests/test_numpy_array.py
+4
-0
tests/test_numpy_dtypes.cpp
+1
-1
No files found.
docs/advanced/pycpp/numpy.rst
View file @
d400f60c
...
@@ -41,8 +41,8 @@ completely avoid copy operations with Python expressions like
...
@@ -41,8 +41,8 @@ completely avoid copy operations with Python expressions like
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
2, /* Number of dimensions */
2, /* Number of dimensions */
{ m.rows(), m.cols() }, /* Buffer dimensions */
{ m.rows(), m.cols() }, /* Buffer dimensions */
{
sizeof(float) * m.rows(),
/* Strides (in bytes) for each index */
{
(ssize_t)( sizeof(float) * m.rows() ),
/* Strides (in bytes) for each index */
sizeof(float
) }
(ssize_t)( sizeof(float)
) }
);
);
});
});
...
@@ -61,7 +61,7 @@ specification.
...
@@ -61,7 +61,7 @@ specification.
std::string format;
std::string format;
int ndim;
int ndim;
std::vector<size_t> shape;
std::vector<size_t> shape;
std::vector<size_t> strides;
std::vector<s
s
ize_t> strides;
};
};
To create a C++ function that can take a Python buffer object as an argument,
To create a C++ function that can take a Python buffer object as an argument,
...
@@ -121,8 +121,8 @@ as follows:
...
@@ -121,8 +121,8 @@ as follows:
{ (size_t) m.rows(),
{ (size_t) m.rows(),
(size_t) m.cols() },
(size_t) m.cols() },
/* Strides (in bytes) for each index */
/* Strides (in bytes) for each index */
{
sizeof(Scalar) * (rowMajor ? m.cols() : 1
),
{
(ssize_t)( sizeof(Scalar) * (rowMajor ? m.cols() : 1)
),
sizeof(Scalar) * (rowMajor ? 1 : m.rows()
) }
(ssize_t)( sizeof(Scalar) * (rowMajor ? 1 : m.rows())
) }
);
);
})
})
...
...
include/pybind11/numpy.h
View file @
d400f60c
...
@@ -265,14 +265,14 @@ protected:
...
@@ -265,14 +265,14 @@ protected:
const
unsigned
char
*
data_
;
const
unsigned
char
*
data_
;
// Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
// 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
// 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
>>
conditional_t
<
Dynamic
,
const
size_t
*
,
std
::
array
<
size_t
,
(
size_t
)
Dims
>>
shape_
;
shape_
,
strides_
;
conditional_t
<
Dynamic
,
const
ssize_t
*
,
std
::
array
<
ssize_t
,
(
size_t
)
Dims
>>
strides_
;
const
size_t
dims_
;
const
size_t
dims_
;
friend
class
pybind11
::
array
;
friend
class
pybind11
::
array
;
// Constructor for compile-time dimensions:
// Constructor for compile-time dimensions:
template
<
bool
Dyn
=
Dynamic
>
template
<
bool
Dyn
=
Dynamic
>
unchecked_reference
(
const
void
*
data
,
const
size_t
*
shape
,
const
size_t
*
strides
,
enable_if_t
<!
Dyn
,
size_t
>
)
unchecked_reference
(
const
void
*
data
,
const
size_t
*
shape
,
const
s
s
ize_t
*
strides
,
enable_if_t
<!
Dyn
,
size_t
>
)
:
data_
{
reinterpret_cast
<
const
unsigned
char
*>
(
data
)},
dims_
{
Dims
}
{
:
data_
{
reinterpret_cast
<
const
unsigned
char
*>
(
data
)},
dims_
{
Dims
}
{
for
(
size_t
i
=
0
;
i
<
dims_
;
i
++
)
{
for
(
size_t
i
=
0
;
i
<
dims_
;
i
++
)
{
shape_
[
i
]
=
shape
[
i
];
shape_
[
i
]
=
shape
[
i
];
...
@@ -281,7 +281,7 @@ protected:
...
@@ -281,7 +281,7 @@ protected:
}
}
// Constructor for runtime dimensions:
// Constructor for runtime dimensions:
template
<
bool
Dyn
=
Dynamic
>
template
<
bool
Dyn
=
Dynamic
>
unchecked_reference
(
const
void
*
data
,
const
size_t
*
shape
,
const
size_t
*
strides
,
enable_if_t
<
Dyn
,
size_t
>
dims
)
unchecked_reference
(
const
void
*
data
,
const
size_t
*
shape
,
const
s
s
ize_t
*
strides
,
enable_if_t
<
Dyn
,
size_t
>
dims
)
:
data_
{
reinterpret_cast
<
const
unsigned
char
*>
(
data
)},
shape_
{
shape
},
strides_
{
strides
},
dims_
{
dims
}
{}
:
data_
{
reinterpret_cast
<
const
unsigned
char
*>
(
data
)},
shape_
{
shape
},
strides_
{
strides
},
dims_
{
dims
}
{}
public
:
public
:
...
@@ -573,12 +573,12 @@ public:
...
@@ -573,12 +573,12 @@ public:
}
}
/// Strides of the array
/// Strides of the array
const
size_t
*
strides
()
const
{
const
s
s
ize_t
*
strides
()
const
{
return
reinterpret_cast
<
const
size_t
*>
(
detail
::
array_proxy
(
m_ptr
)
->
strides
);
return
reinterpret_cast
<
const
s
s
ize_t
*>
(
detail
::
array_proxy
(
m_ptr
)
->
strides
);
}
}
/// Stride along a given axis
/// Stride along a given axis
size_t
strides
(
size_t
dim
)
const
{
s
s
ize_t
strides
(
size_t
dim
)
const
{
if
(
dim
>=
ndim
())
if
(
dim
>=
ndim
())
fail_dim_check
(
dim
,
"invalid axis"
);
fail_dim_check
(
dim
,
"invalid axis"
);
return
strides
()[
dim
];
return
strides
()[
dim
];
...
@@ -702,9 +702,9 @@ protected:
...
@@ -702,9 +702,9 @@ protected:
throw
std
::
domain_error
(
"array is not writeable"
);
throw
std
::
domain_error
(
"array is not writeable"
);
}
}
static
std
::
vector
<
Py_intptr_t
>
default_strides
(
const
std
::
vector
<
Py_intptr
_t
>&
shape
,
size_t
itemsize
)
{
static
std
::
vector
<
ssize_t
>
default_strides
(
const
std
::
vector
<
size
_t
>&
shape
,
size_t
itemsize
)
{
auto
ndim
=
shape
.
size
();
auto
ndim
=
shape
.
size
();
std
::
vector
<
Py_intptr
_t
>
strides
(
ndim
);
std
::
vector
<
ssize
_t
>
strides
(
ndim
);
if
(
ndim
)
{
if
(
ndim
)
{
std
::
fill
(
strides
.
begin
(),
strides
.
end
(),
itemsize
);
std
::
fill
(
strides
.
begin
(),
strides
.
end
(),
itemsize
);
for
(
size_t
i
=
0
;
i
<
ndim
-
1
;
i
++
)
for
(
size_t
i
=
0
;
i
<
ndim
-
1
;
i
++
)
...
@@ -1133,7 +1133,7 @@ array_iterator<T> array_end(const buffer_info& buffer) {
...
@@ -1133,7 +1133,7 @@ array_iterator<T> array_end(const buffer_info& buffer) {
class
common_iterator
{
class
common_iterator
{
public
:
public
:
using
container_type
=
std
::
vector
<
size_t
>
;
using
container_type
=
std
::
vector
<
s
s
ize_t
>
;
using
value_type
=
container_type
::
value_type
;
using
value_type
=
container_type
::
value_type
;
using
size_type
=
container_type
::
size_type
;
using
size_type
=
container_type
::
size_type
;
...
@@ -1175,7 +1175,7 @@ public:
...
@@ -1175,7 +1175,7 @@ public:
for
(
size_t
i
=
0
;
i
<
shape
.
size
();
++
i
)
for
(
size_t
i
=
0
;
i
<
shape
.
size
();
++
i
)
m_shape
[
i
]
=
static_cast
<
container_type
::
value_type
>
(
shape
[
i
]);
m_shape
[
i
]
=
static_cast
<
container_type
::
value_type
>
(
shape
[
i
]);
container_type
strides
(
shape
.
size
());
std
::
vector
<
ssize_t
>
strides
(
shape
.
size
());
for
(
size_t
i
=
0
;
i
<
N
;
++
i
)
for
(
size_t
i
=
0
;
i
<
N
;
++
i
)
init_common_iterator
(
buffers
[
i
],
shape
,
m_common_iterator
[
i
],
strides
);
init_common_iterator
(
buffers
[
i
],
shape
,
m_common_iterator
[
i
],
strides
);
}
}
...
@@ -1203,7 +1203,7 @@ private:
...
@@ -1203,7 +1203,7 @@ private:
void
init_common_iterator
(
const
buffer_info
&
buffer
,
void
init_common_iterator
(
const
buffer_info
&
buffer
,
const
std
::
vector
<
size_t
>
&
shape
,
const
std
::
vector
<
size_t
>
&
shape
,
common_iter
&
iterator
,
container_type
&
strides
)
{
common_iter
&
iterator
,
std
::
vector
<
ssize_t
>
&
strides
)
{
auto
buffer_shape_iter
=
buffer
.
shape
.
rbegin
();
auto
buffer_shape_iter
=
buffer
.
shape
.
rbegin
();
auto
buffer_strides_iter
=
buffer
.
strides
.
rbegin
();
auto
buffer_strides_iter
=
buffer
.
strides
.
rbegin
();
auto
shape_iter
=
shape
.
rbegin
();
auto
shape_iter
=
shape
.
rbegin
();
...
@@ -1211,7 +1211,7 @@ private:
...
@@ -1211,7 +1211,7 @@ private:
while
(
buffer_shape_iter
!=
buffer
.
shape
.
rend
())
{
while
(
buffer_shape_iter
!=
buffer
.
shape
.
rend
())
{
if
(
*
shape_iter
==
*
buffer_shape_iter
)
if
(
*
shape_iter
==
*
buffer_shape_iter
)
*
strides_iter
=
static_cast
<
size_t
>
(
*
buffer_strides_iter
)
;
*
strides_iter
=
*
buffer_strides_iter
;
else
else
*
strides_iter
=
0
;
*
strides_iter
=
0
;
...
@@ -1283,10 +1283,11 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
...
@@ -1283,10 +1283,11 @@ 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)
// Check for C contiguity (but only if previous inputs were also C contiguous)
if
(
trivial_broadcast_c
)
{
if
(
trivial_broadcast_c
)
{
s
ize_t
expect_stride
=
buffers
[
i
].
itemsize
;
s
size_t
expect_stride
=
static_cast
<
ssize_t
>
(
buffers
[
i
].
itemsize
)
;
auto
end
=
buffers
[
i
].
shape
.
crend
();
auto
end
=
buffers
[
i
].
shape
.
crend
();
for
(
auto
shape_iter
=
buffers
[
i
].
shape
.
crbegin
(),
stride_iter
=
buffers
[
i
].
strides
.
crbegin
();
auto
shape_iter
=
buffers
[
i
].
shape
.
crbegin
();
trivial_broadcast_c
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
auto
stride_iter
=
buffers
[
i
].
strides
.
crbegin
();
for
(;
trivial_broadcast_c
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
if
(
expect_stride
==
*
stride_iter
)
if
(
expect_stride
==
*
stride_iter
)
expect_stride
*=
*
shape_iter
;
expect_stride
*=
*
shape_iter
;
else
else
...
@@ -1296,10 +1297,11 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
...
@@ -1296,10 +1297,11 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
// Check for Fortran contiguity (if previous inputs were also F contiguous)
// Check for Fortran contiguity (if previous inputs were also F contiguous)
if
(
trivial_broadcast_f
)
{
if
(
trivial_broadcast_f
)
{
s
ize_t
expect_stride
=
buffers
[
i
].
itemsize
;
s
size_t
expect_stride
=
static_cast
<
ssize_t
>
(
buffers
[
i
].
itemsize
)
;
auto
end
=
buffers
[
i
].
shape
.
cend
();
auto
end
=
buffers
[
i
].
shape
.
cend
();
for
(
auto
shape_iter
=
buffers
[
i
].
shape
.
cbegin
(),
stride_iter
=
buffers
[
i
].
strides
.
cbegin
();
auto
shape_iter
=
buffers
[
i
].
shape
.
cbegin
();
trivial_broadcast_f
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
auto
stride_iter
=
buffers
[
i
].
strides
.
cbegin
();
for
(;
trivial_broadcast_f
&&
shape_iter
!=
end
;
++
shape_iter
,
++
stride_iter
)
{
if
(
expect_stride
==
*
stride_iter
)
if
(
expect_stride
==
*
stride_iter
)
expect_stride
*=
*
shape_iter
;
expect_stride
*=
*
shape_iter
;
else
else
...
@@ -1336,20 +1338,20 @@ struct vectorize_helper {
...
@@ -1336,20 +1338,20 @@ struct vectorize_helper {
auto
trivial
=
broadcast
(
buffers
,
ndim
,
shape
);
auto
trivial
=
broadcast
(
buffers
,
ndim
,
shape
);
size_t
size
=
1
;
size_t
size
=
1
;
std
::
vector
<
size_t
>
strides
(
ndim
);
std
::
vector
<
s
s
ize_t
>
strides
(
ndim
);
if
(
ndim
>
0
)
{
if
(
ndim
>
0
)
{
if
(
trivial
==
broadcast_trivial
::
f_trivial
)
{
if
(
trivial
==
broadcast_trivial
::
f_trivial
)
{
strides
[
0
]
=
s
izeof
(
Return
);
strides
[
0
]
=
s
tatic_cast
<
ssize_t
>
(
sizeof
(
Return
)
);
for
(
size_t
i
=
1
;
i
<
ndim
;
++
i
)
{
for
(
size_t
i
=
1
;
i
<
ndim
;
++
i
)
{
strides
[
i
]
=
strides
[
i
-
1
]
*
s
hape
[
i
-
1
]
;
strides
[
i
]
=
strides
[
i
-
1
]
*
s
tatic_cast
<
ssize_t
>
(
shape
[
i
-
1
])
;
size
*=
shape
[
i
-
1
];
size
*=
shape
[
i
-
1
];
}
}
size
*=
shape
[
ndim
-
1
];
size
*=
shape
[
ndim
-
1
];
}
}
else
{
else
{
strides
[
ndim
-
1
]
=
s
izeof
(
Return
);
strides
[
ndim
-
1
]
=
s
tatic_cast
<
ssize_t
>
(
sizeof
(
Return
)
);
for
(
size_t
i
=
ndim
-
1
;
i
>
0
;
--
i
)
{
for
(
size_t
i
=
ndim
-
1
;
i
>
0
;
--
i
)
{
strides
[
i
-
1
]
=
strides
[
i
]
*
s
hape
[
i
]
;
strides
[
i
-
1
]
=
strides
[
i
]
*
s
tatic_cast
<
ssize_t
>
(
shape
[
i
])
;
size
*=
shape
[
i
];
size
*=
shape
[
i
];
}
}
size
*=
shape
[
0
];
size
*=
shape
[
0
];
...
...
tests/test_buffers.cpp
View file @
d400f60c
...
@@ -109,8 +109,8 @@ test_initializer buffers([](py::module &m) {
...
@@ -109,8 +109,8 @@ test_initializer buffers([](py::module &m) {
py
::
format_descriptor
<
float
>::
format
(),
/* Python struct-style format descriptor */
py
::
format_descriptor
<
float
>::
format
(),
/* Python struct-style format descriptor */
2
,
/* Number of dimensions */
2
,
/* Number of dimensions */
{
m
.
rows
(),
m
.
cols
()
},
/* Buffer dimensions */
{
m
.
rows
(),
m
.
cols
()
},
/* Buffer dimensions */
{
s
izeof
(
float
)
*
m
.
rows
(),
/* Strides (in bytes) for each index */
{
s
tatic_cast
<
ssize_t
>
(
sizeof
(
float
)
*
m
.
rows
()),
/* Strides (in bytes) for each index */
s
izeof
(
float
)
}
s
tatic_cast
<
ssize_t
>
(
sizeof
(
float
)
)
}
);
);
})
})
;
;
...
...
tests/test_numpy_array.cpp
View file @
d400f60c
...
@@ -13,6 +13,7 @@
...
@@ -13,6 +13,7 @@
#include <pybind11/stl.h>
#include <pybind11/stl.h>
#include <cstdint>
#include <cstdint>
#include <vector>
using
arr
=
py
::
array
;
using
arr
=
py
::
array
;
using
arr_t
=
py
::
array_t
<
uint16_t
,
0
>
;
using
arr_t
=
py
::
array_t
<
uint16_t
,
0
>
;
...
@@ -294,4 +295,4 @@ test_initializer numpy_array([](py::module &m) {
...
@@ -294,4 +295,4 @@ test_initializer numpy_array([](py::module &m) {
std
::
fill
(
a
.
mutable_data
(),
a
.
mutable_data
()
+
a
.
size
(),
42.
);
std
::
fill
(
a
.
mutable_data
(),
a
.
mutable_data
()
+
a
.
size
(),
42.
);
return
a
;
return
a
;
});
});
});
});
\ No newline at end of file
tests/test_numpy_array.py
View file @
d400f60c
...
@@ -203,6 +203,10 @@ def test_wrap():
...
@@ -203,6 +203,10 @@ def test_wrap():
a2
=
wrap
(
a1d
)
a2
=
wrap
(
a1d
)
assert_references
(
a1d
,
a2
,
a1
)
assert_references
(
a1d
,
a2
,
a1
)
a1m
=
a1
[::
-
1
,
::
-
1
,
::
-
1
]
a2
=
wrap
(
a1m
)
assert_references
(
a1m
,
a2
,
a1
)
def
test_numpy_view
(
capture
):
def
test_numpy_view
(
capture
):
from
pybind11_tests.array
import
ArrayClass
from
pybind11_tests.array
import
ArrayClass
...
...
tests/test_numpy_dtypes.cpp
View file @
d400f60c
...
@@ -226,7 +226,7 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
...
@@ -226,7 +226,7 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
std
::
vector
<
int32_t
>
data
{
1
,
2
,
3
,
4
,
5
,
6
};
std
::
vector
<
int32_t
>
data
{
1
,
2
,
3
,
4
,
5
,
6
};
std
::
vector
<
size_t
>
shape
{
3
,
2
};
std
::
vector
<
size_t
>
shape
{
3
,
2
};
std
::
vector
<
size_t
>
strides
{
8
,
4
};
std
::
vector
<
s
s
ize_t
>
strides
{
8
,
4
};
auto
ptr
=
data
.
data
();
auto
ptr
=
data
.
data
();
auto
vptr
=
(
void
*
)
ptr
;
auto
vptr
=
(
void
*
)
ptr
;
...
...
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