test_numpy_vectorize.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. # -*- coding: utf-8 -*-
  2. import pytest
  3. from pybind11_tests import numpy_vectorize as m
  4. np = pytest.importorskip("numpy")
  5. def test_vectorize(capture):
  6. assert np.isclose(m.vectorized_func3(np.array(3 + 7j)), [6 + 14j])
  7. for f in [m.vectorized_func, m.vectorized_func2]:
  8. with capture:
  9. assert np.isclose(f(1, 2, 3), 6)
  10. assert capture == "my_func(x:int=1, y:float=2, z:float=3)"
  11. with capture:
  12. assert np.isclose(f(np.array(1), np.array(2), 3), 6)
  13. assert capture == "my_func(x:int=1, y:float=2, z:float=3)"
  14. with capture:
  15. assert np.allclose(f(np.array([1, 3]), np.array([2, 4]), 3), [6, 36])
  16. assert (
  17. capture
  18. == """
  19. my_func(x:int=1, y:float=2, z:float=3)
  20. my_func(x:int=3, y:float=4, z:float=3)
  21. """
  22. )
  23. with capture:
  24. a = np.array([[1, 2], [3, 4]], order="F")
  25. b = np.array([[10, 20], [30, 40]], order="F")
  26. c = 3
  27. result = f(a, b, c)
  28. assert np.allclose(result, a * b * c)
  29. assert result.flags.f_contiguous
  30. # All inputs are F order and full or singletons, so we the result is in col-major order:
  31. assert (
  32. capture
  33. == """
  34. my_func(x:int=1, y:float=10, z:float=3)
  35. my_func(x:int=3, y:float=30, z:float=3)
  36. my_func(x:int=2, y:float=20, z:float=3)
  37. my_func(x:int=4, y:float=40, z:float=3)
  38. """
  39. )
  40. with capture:
  41. a, b, c = (
  42. np.array([[1, 3, 5], [7, 9, 11]]),
  43. np.array([[2, 4, 6], [8, 10, 12]]),
  44. 3,
  45. )
  46. assert np.allclose(f(a, b, c), a * b * c)
  47. assert (
  48. capture
  49. == """
  50. my_func(x:int=1, y:float=2, z:float=3)
  51. my_func(x:int=3, y:float=4, z:float=3)
  52. my_func(x:int=5, y:float=6, z:float=3)
  53. my_func(x:int=7, y:float=8, z:float=3)
  54. my_func(x:int=9, y:float=10, z:float=3)
  55. my_func(x:int=11, y:float=12, z:float=3)
  56. """
  57. )
  58. with capture:
  59. a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2
  60. assert np.allclose(f(a, b, c), a * b * c)
  61. assert (
  62. capture
  63. == """
  64. my_func(x:int=1, y:float=2, z:float=2)
  65. my_func(x:int=2, y:float=3, z:float=2)
  66. my_func(x:int=3, y:float=4, z:float=2)
  67. my_func(x:int=4, y:float=2, z:float=2)
  68. my_func(x:int=5, y:float=3, z:float=2)
  69. my_func(x:int=6, y:float=4, z:float=2)
  70. """
  71. )
  72. with capture:
  73. a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2
  74. assert np.allclose(f(a, b, c), a * b * c)
  75. assert (
  76. capture
  77. == """
  78. my_func(x:int=1, y:float=2, z:float=2)
  79. my_func(x:int=2, y:float=2, z:float=2)
  80. my_func(x:int=3, y:float=2, z:float=2)
  81. my_func(x:int=4, y:float=3, z:float=2)
  82. my_func(x:int=5, y:float=3, z:float=2)
  83. my_func(x:int=6, y:float=3, z:float=2)
  84. """
  85. )
  86. with capture:
  87. a, b, c = (
  88. np.array([[1, 2, 3], [4, 5, 6]], order="F"),
  89. np.array([[2], [3]]),
  90. 2,
  91. )
  92. assert np.allclose(f(a, b, c), a * b * c)
  93. assert (
  94. capture
  95. == """
  96. my_func(x:int=1, y:float=2, z:float=2)
  97. my_func(x:int=2, y:float=2, z:float=2)
  98. my_func(x:int=3, y:float=2, z:float=2)
  99. my_func(x:int=4, y:float=3, z:float=2)
  100. my_func(x:int=5, y:float=3, z:float=2)
  101. my_func(x:int=6, y:float=3, z:float=2)
  102. """
  103. )
  104. with capture:
  105. a, b, c = np.array([[1, 2, 3], [4, 5, 6]])[::, ::2], np.array([[2], [3]]), 2
  106. assert np.allclose(f(a, b, c), a * b * c)
  107. assert (
  108. capture
  109. == """
  110. my_func(x:int=1, y:float=2, z:float=2)
  111. my_func(x:int=3, y:float=2, z:float=2)
  112. my_func(x:int=4, y:float=3, z:float=2)
  113. my_func(x:int=6, y:float=3, z:float=2)
  114. """
  115. )
  116. with capture:
  117. a, b, c = (
  118. np.array([[1, 2, 3], [4, 5, 6]], order="F")[::, ::2],
  119. np.array([[2], [3]]),
  120. 2,
  121. )
  122. assert np.allclose(f(a, b, c), a * b * c)
  123. assert (
  124. capture
  125. == """
  126. my_func(x:int=1, y:float=2, z:float=2)
  127. my_func(x:int=3, y:float=2, z:float=2)
  128. my_func(x:int=4, y:float=3, z:float=2)
  129. my_func(x:int=6, y:float=3, z:float=2)
  130. """
  131. )
  132. def test_type_selection():
  133. assert m.selective_func(np.array([1], dtype=np.int32)) == "Int branch taken."
  134. assert m.selective_func(np.array([1.0], dtype=np.float32)) == "Float branch taken."
  135. assert (
  136. m.selective_func(np.array([1.0j], dtype=np.complex64))
  137. == "Complex float branch taken."
  138. )
  139. def test_docs(doc):
  140. assert (
  141. doc(m.vectorized_func)
  142. == """
  143. vectorized_func(arg0: numpy.ndarray[numpy.int32], arg1: numpy.ndarray[numpy.float32], arg2: numpy.ndarray[numpy.float64]) -> object
  144. """ # noqa: E501 line too long
  145. )
  146. def test_trivial_broadcasting():
  147. trivial, vectorized_is_trivial = m.trivial, m.vectorized_is_trivial
  148. assert vectorized_is_trivial(1, 2, 3) == trivial.c_trivial
  149. assert vectorized_is_trivial(np.array(1), np.array(2), 3) == trivial.c_trivial
  150. assert (
  151. vectorized_is_trivial(np.array([1, 3]), np.array([2, 4]), 3)
  152. == trivial.c_trivial
  153. )
  154. assert trivial.c_trivial == vectorized_is_trivial(
  155. np.array([[1, 3, 5], [7, 9, 11]]), np.array([[2, 4, 6], [8, 10, 12]]), 3
  156. )
  157. assert (
  158. vectorized_is_trivial(np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2)
  159. == trivial.non_trivial
  160. )
  161. assert (
  162. vectorized_is_trivial(np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2)
  163. == trivial.non_trivial
  164. )
  165. z1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype="int32")
  166. z2 = np.array(z1, dtype="float32")
  167. z3 = np.array(z1, dtype="float64")
  168. assert vectorized_is_trivial(z1, z2, z3) == trivial.c_trivial
  169. assert vectorized_is_trivial(1, z2, z3) == trivial.c_trivial
  170. assert vectorized_is_trivial(z1, 1, z3) == trivial.c_trivial
  171. assert vectorized_is_trivial(z1, z2, 1) == trivial.c_trivial
  172. assert vectorized_is_trivial(z1[::2, ::2], 1, 1) == trivial.non_trivial
  173. assert vectorized_is_trivial(1, 1, z1[::2, ::2]) == trivial.c_trivial
  174. assert vectorized_is_trivial(1, 1, z3[::2, ::2]) == trivial.non_trivial
  175. assert vectorized_is_trivial(z1, 1, z3[1::4, 1::4]) == trivial.c_trivial
  176. y1 = np.array(z1, order="F")
  177. y2 = np.array(y1)
  178. y3 = np.array(y1)
  179. assert vectorized_is_trivial(y1, y2, y3) == trivial.f_trivial
  180. assert vectorized_is_trivial(y1, 1, 1) == trivial.f_trivial
  181. assert vectorized_is_trivial(1, y2, 1) == trivial.f_trivial
  182. assert vectorized_is_trivial(1, 1, y3) == trivial.f_trivial
  183. assert vectorized_is_trivial(y1, z2, 1) == trivial.non_trivial
  184. assert vectorized_is_trivial(z1[1::4, 1::4], y2, 1) == trivial.f_trivial
  185. assert vectorized_is_trivial(y1[1::4, 1::4], z2, 1) == trivial.c_trivial
  186. assert m.vectorized_func(z1, z2, z3).flags.c_contiguous
  187. assert m.vectorized_func(y1, y2, y3).flags.f_contiguous
  188. assert m.vectorized_func(z1, 1, 1).flags.c_contiguous
  189. assert m.vectorized_func(1, y2, 1).flags.f_contiguous
  190. assert m.vectorized_func(z1[1::4, 1::4], y2, 1).flags.f_contiguous
  191. assert m.vectorized_func(y1[1::4, 1::4], z2, 1).flags.c_contiguous
  192. def test_passthrough_arguments(doc):
  193. assert doc(m.vec_passthrough) == (
  194. "vec_passthrough("
  195. + ", ".join(
  196. [
  197. "arg0: float",
  198. "arg1: numpy.ndarray[numpy.float64]",
  199. "arg2: numpy.ndarray[numpy.float64]",
  200. "arg3: numpy.ndarray[numpy.int32]",
  201. "arg4: int",
  202. "arg5: m.numpy_vectorize.NonPODClass",
  203. "arg6: numpy.ndarray[numpy.float64]",
  204. ]
  205. )
  206. + ") -> object"
  207. )
  208. b = np.array([[10, 20, 30]], dtype="float64")
  209. c = np.array([100, 200]) # NOT a vectorized argument
  210. d = np.array([[1000], [2000], [3000]], dtype="int")
  211. g = np.array([[1000000, 2000000, 3000000]], dtype="int") # requires casting
  212. assert np.all(
  213. m.vec_passthrough(1, b, c, d, 10000, m.NonPODClass(100000), g)
  214. == np.array(
  215. [
  216. [1111111, 2111121, 3111131],
  217. [1112111, 2112121, 3112131],
  218. [1113111, 2113121, 3113131],
  219. ]
  220. )
  221. )
  222. def test_method_vectorization():
  223. o = m.VectorizeTestClass(3)
  224. x = np.array([1, 2], dtype="int")
  225. y = np.array([[10], [20]], dtype="float32")
  226. assert np.all(o.method(x, y) == [[14, 15], [24, 25]])
  227. def test_array_collapse():
  228. assert not isinstance(m.vectorized_func(1, 2, 3), np.ndarray)
  229. assert not isinstance(m.vectorized_func(np.array(1), 2, 3), np.ndarray)
  230. z = m.vectorized_func([1], 2, 3)
  231. assert isinstance(z, np.ndarray)
  232. assert z.shape == (1,)
  233. z = m.vectorized_func(1, [[[2]]], 3)
  234. assert isinstance(z, np.ndarray)
  235. assert z.shape == (1, 1, 1)
  236. def test_vectorized_noreturn():
  237. x = m.NonPODClass(0)
  238. assert x.value == 0
  239. m.add_to(x, [1, 2, 3, 4])
  240. assert x.value == 10
  241. m.add_to(x, 1)
  242. assert x.value == 11
  243. m.add_to(x, [[1, 1], [2, 3]])
  244. assert x.value == 18