feectools 0.5.0__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. {feectools-0.5.0/feectools.egg-info → feectools-0.6.0}/PKG-INFO +1 -1
  2. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/basic.py +3 -1
  3. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/block.py +5 -2
  4. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/direct_solvers.py +110 -22
  5. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/fft.py +3 -0
  6. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_dot_1d/stencil_dot_1d_cuda.cu +10 -7
  7. feectools-0.6.0/feectools/linalg/kernels/stencil_dot_1d/stencil_dot_1d_kernels.py +38 -0
  8. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_dot_2d/stencil_dot_2d_cuda.cu +12 -9
  9. feectools-0.6.0/feectools/linalg/kernels/stencil_dot_2d/stencil_dot_2d_kernels.py +47 -0
  10. feectools-0.6.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_cuda.cu +52 -0
  11. feectools-0.6.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_kernels.py +63 -0
  12. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_transpose_1d/stencil_transpose_1d_cuda.cu +12 -12
  13. feectools-0.6.0/feectools/linalg/kernels/stencil_transpose_1d/stencil_transpose_1d_kernels.py +38 -0
  14. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_transpose_2d/stencil_transpose_2d_cuda.cu +11 -12
  15. feectools-0.6.0/feectools/linalg/kernels/stencil_transpose_2d/stencil_transpose_2d_kernels.py +52 -0
  16. feectools-0.6.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_cuda.cu +57 -0
  17. feectools-0.6.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_kernels.py +69 -0
  18. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kron.py +45 -0
  19. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/solvers.py +62 -32
  20. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/stencil.py +48 -27
  21. feectools-0.6.0/feectools/linalg/tests/inner_on_device_child.py +149 -0
  22. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/kernel_test_args.py +34 -17
  23. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_cuda_parity.py +1 -1
  24. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_device_matvec.py +63 -20
  25. feectools-0.6.0/feectools/linalg/tests/test_inner_on_device.py +160 -0
  26. feectools-0.6.0/feectools/linalg/tests/test_kron_device_solve.py +284 -0
  27. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_mpi_device.py +62 -0
  28. {feectools-0.5.0 → feectools-0.6.0/feectools.egg-info}/PKG-INFO +1 -1
  29. {feectools-0.5.0 → feectools-0.6.0}/feectools.egg-info/SOURCES.txt +3 -0
  30. {feectools-0.5.0 → feectools-0.6.0}/pyproject.toml +1 -1
  31. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_1d/stencil_dot_1d_kernels.py +0 -35
  32. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_2d/stencil_dot_2d_kernels.py +0 -99
  33. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_cuda.cu +0 -64
  34. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_kernels.py +0 -206
  35. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_1d/stencil_transpose_1d_kernels.py +0 -37
  36. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_2d/stencil_transpose_2d_kernels.py +0 -116
  37. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_cuda.cu +0 -76
  38. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_kernels.py +0 -252
  39. {feectools-0.5.0 → feectools-0.6.0}/AUTHORS +0 -0
  40. {feectools-0.5.0 → feectools-0.6.0}/LICENSE +0 -0
  41. {feectools-0.5.0 → feectools-0.6.0}/README.md +0 -0
  42. {feectools-0.5.0 → feectools-0.6.0}/feectools/__init__.py +0 -0
  43. {feectools-0.5.0 → feectools-0.6.0}/feectools/accelerate/__init__.py +0 -0
  44. {feectools-0.5.0 → feectools-0.6.0}/feectools/accelerate/accelerate.py +0 -0
  45. {feectools-0.5.0 → feectools-0.6.0}/feectools/accelerate/compile_psydac.mk +0 -0
  46. {feectools-0.5.0 → feectools-0.6.0}/feectools/api/__init__.py +0 -0
  47. {feectools-0.5.0 → feectools-0.6.0}/feectools/api/essential_bc.py +0 -0
  48. {feectools-0.5.0 → feectools-0.6.0}/feectools/api/fem_bilinear_form.py +0 -0
  49. {feectools-0.5.0 → feectools-0.6.0}/feectools/api/fem_common.py +0 -0
  50. {feectools-0.5.0 → feectools-0.6.0}/feectools/api/fem_sum_form.py +0 -0
  51. {feectools-0.5.0 → feectools-0.6.0}/feectools/api/settings.py +0 -0
  52. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/__init__.py +0 -0
  53. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/bsplines.py +0 -0
  54. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/bsplines_kernels.py +0 -0
  55. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/field_evaluation_kernels.py +0 -0
  56. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/tests/__init__.py +0 -0
  57. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/tests/test_bsplines.py +0 -0
  58. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/tests/test_bsplines_kernel.py +0 -0
  59. {feectools-0.5.0 → feectools-0.6.0}/feectools/core/tests/test_bsplines_pyccel.py +0 -0
  60. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/__init__.py +0 -0
  61. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/basic.py +0 -0
  62. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/blocking_data_exchanger.py +0 -0
  63. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/cart.py +0 -0
  64. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/interface_data_exchanger.py +0 -0
  65. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/nonblocking_data_exchanger.py +0 -0
  66. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/partition.py +0 -0
  67. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/petsc.py +0 -0
  68. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/__init__.py +0 -0
  69. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/test_cart_1d.py +0 -0
  70. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/test_cart_2d.py +0 -0
  71. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/test_cart_3d.py +0 -0
  72. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/test_coarsen.py +0 -0
  73. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/test_device_binding.py +0 -0
  74. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/test_multicart_2d.py +0 -0
  75. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/tests/test_partition.py +0 -0
  76. {feectools-0.5.0 → feectools-0.6.0}/feectools/ddm/utilities.py +0 -0
  77. {feectools-0.5.0 → feectools-0.6.0}/feectools/feec/__init__.py +0 -0
  78. {feectools-0.5.0 → feectools-0.6.0}/feectools/feec/derivatives.py +0 -0
  79. {feectools-0.5.0 → feectools-0.6.0}/feectools/feec/dof_kernels.py +0 -0
  80. {feectools-0.5.0 → feectools-0.6.0}/feectools/feec/global_geometric_projectors.py +0 -0
  81. {feectools-0.5.0 → feectools-0.6.0}/feectools/feec/hodge.py +0 -0
  82. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/__init__.py +0 -0
  83. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/basic.py +0 -0
  84. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/grid.py +0 -0
  85. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/lst_preconditioner.py +0 -0
  86. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/partitioning.py +0 -0
  87. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/projectors.py +0 -0
  88. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/splines.py +0 -0
  89. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tensor.py +0 -0
  90. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/__init__.py +0 -0
  91. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/analytical_profiles_1d.py +0 -0
  92. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/analytical_profiles_base.py +0 -0
  93. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/splines_error_bounds.py +0 -0
  94. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/test_dirichlet_projectors.py +0 -0
  95. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/test_spline_histopolation.py +0 -0
  96. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/test_spline_interpolation.py +0 -0
  97. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/test_splines.py +0 -0
  98. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/test_splines_par.py +0 -0
  99. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/test_tensor.py +0 -0
  100. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/test_vector_spaces.py +0 -0
  101. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/tests/utilities.py +0 -0
  102. {feectools-0.5.0 → feectools-0.6.0}/feectools/fem/vector.py +0 -0
  103. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/__init__.py +0 -0
  104. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/__init__.py +0 -0
  105. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/matvec_kernels.py +0 -0
  106. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil2IJV_kernels.py +0 -0
  107. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil2coo_kernels.py +0 -0
  108. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_1d/__init__.py +0 -0
  109. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_1d/stencil_axpy_1d_cuda.cu +0 -0
  110. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_1d/stencil_axpy_1d_kernels.py +0 -0
  111. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_2d/__init__.py +0 -0
  112. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_2d/stencil_axpy_2d_cuda.cu +0 -0
  113. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_2d/stencil_axpy_2d_kernels.py +0 -0
  114. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_3d/__init__.py +0 -0
  115. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_3d/stencil_axpy_3d_cuda.cu +0 -0
  116. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_axpy_3d/stencil_axpy_3d_kernels.py +0 -0
  117. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_dot_1d/__init__.py +0 -0
  118. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_dot_2d/__init__.py +0 -0
  119. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_dot_3d/__init__.py +0 -0
  120. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_1d/__init__.py +0 -0
  121. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_1d/stencil_inner_1d_cuda.cu +0 -0
  122. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_1d/stencil_inner_1d_kernels.py +0 -0
  123. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_2d/__init__.py +0 -0
  124. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_2d/stencil_inner_2d_cuda.cu +0 -0
  125. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_2d/stencil_inner_2d_kernels.py +0 -0
  126. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_3d/__init__.py +0 -0
  127. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_3d/stencil_inner_3d_cuda.cu +0 -0
  128. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_inner_3d/stencil_inner_3d_kernels.py +0 -0
  129. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_transpose_1d/__init__.py +0 -0
  130. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_transpose_2d/__init__.py +0 -0
  131. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/stencil_transpose_3d/__init__.py +0 -0
  132. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/kernels/transpose_kernels.py +0 -0
  133. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/memory.py +0 -0
  134. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/sparse.py +0 -0
  135. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/__init__.py +0 -0
  136. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/cuda_parity_cases.py +0 -0
  137. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_axpy_ghost_sync.py +0 -0
  138. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_block.py +0 -0
  139. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_cuda_emulation.py +0 -0
  140. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_fft.py +0 -0
  141. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_kron_stencil_matrix.py +0 -0
  142. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_linalg.py +0 -0
  143. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_matrix_free.py +0 -0
  144. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_solvers.py +0 -0
  145. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_stencil_interface_matrix.py +0 -0
  146. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_stencil_vector.py +0 -0
  147. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_stencil_vector_space.py +0 -0
  148. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/test_toarray.py +0 -0
  149. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/tests/utilities.py +0 -0
  150. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/topetsc.py +0 -0
  151. {feectools-0.5.0 → feectools-0.6.0}/feectools/linalg/utilities.py +0 -0
  152. {feectools-0.5.0 → feectools-0.6.0}/feectools/utilities/__init__.py +0 -0
  153. {feectools-0.5.0 → feectools-0.6.0}/feectools/utilities/quadratures.py +0 -0
  154. {feectools-0.5.0 → feectools-0.6.0}/feectools/utilities/utils.py +0 -0
  155. {feectools-0.5.0 → feectools-0.6.0}/feectools/version.py +0 -0
  156. {feectools-0.5.0 → feectools-0.6.0}/feectools.egg-info/dependency_links.txt +0 -0
  157. {feectools-0.5.0 → feectools-0.6.0}/feectools.egg-info/entry_points.txt +0 -0
  158. {feectools-0.5.0 → feectools-0.6.0}/feectools.egg-info/requires.txt +0 -0
  159. {feectools-0.5.0 → feectools-0.6.0}/feectools.egg-info/top_level.txt +0 -0
  160. {feectools-0.5.0 → feectools-0.6.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: feectools
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Slimmed-down fork of Psydac (https://github.com/pyccel/psydac) with less functionality and fewer dependencies.
5
5
  Author-email: Psydac development team <psydac@googlegroups.com>
6
6
  Maintainer-email: Stefan Possanner <stefan.possanner@ipp.mpg.de>, Max Lindqvist <max.lindqvist@ipp.mpg.de>, Yaman Güçlü <yaman.guclu@gmail.com>, Martin Campos Pinto <martin.campos-pinto@ipp.mpg.de>, Ahmed Ratnani <ratnaniahmed@gmail.com>
@@ -93,9 +93,11 @@ class VectorSpace(ABC):
93
93
 
94
94
  Returns
95
95
  -------
96
- float | complex
96
+ float | complex | cupy.ndarray
97
97
  The scalar product of the two vectors. Note that inner(x, x) is
98
98
  a non-negative real number which is zero if and only if x = 0.
99
+ For vectors with device (CuPy) data, a 0-d device array: the
100
+ result stays on the device.
99
101
 
100
102
  """
101
103
 
@@ -112,9 +112,11 @@ class BlockVectorSpace(VectorSpace):
112
112
 
113
113
  Returns
114
114
  -------
115
- float | complex
115
+ float | complex | cupy.ndarray
116
116
  The scalar product of the two vectors. Note that inner(x, x) is
117
117
  a non-negative real number which is zero if and only if x = 0.
118
+ For vectors with device (CuPy) data, a 0-d device array: the
119
+ result stays on the device.
118
120
 
119
121
  """
120
122
 
@@ -134,7 +136,8 @@ class BlockVectorSpace(VectorSpace):
134
136
  Parameters
135
137
  ----------
136
138
  a : scalar
137
- The scaling coefficient needed for the operation.
139
+ The scaling coefficient needed for the operation (a 0-d device
140
+ array is accepted, see `StencilVectorSpace.axpy`).
138
141
 
139
142
  x : BlockVector
140
143
  The vector which is not modified by this function.
@@ -4,13 +4,14 @@
4
4
  # for full license details. #
5
5
  #---------------------------------------------------------------------------#
6
6
  from abc import abstractmethod
7
+ import numpy as np
7
8
  import cunumpy as xp
8
9
  from cunumpy.xp import array_backend
9
10
  from scipy.sparse import spmatrix, dia_matrix
10
11
 
11
12
  from feectools.linalg.basic import LinearSolver
12
13
 
13
- __all__ = ('to_bnd', 'BandedSolver', 'SparseSolver')
14
+ __all__ = ('to_bnd', 'DenseInverse', 'BandedSolver', 'SparseSolver')
14
15
 
15
16
  #===============================================================================
16
17
  def to_bnd(A):
@@ -28,6 +29,89 @@ def to_bnd(A):
28
29
 
29
30
  return A_bnd, la, ua
30
31
 
32
+ #===============================================================================
33
+ class DenseInverse:
34
+ """
35
+ The inverse of a 1D solver's matrix as a dense matrix, for solves on the device.
36
+
37
+ LAPACK and SuperLU run on the host only. For device data the matrix is inverted
38
+ once on the host, by the solver itself (it solves for the identity), and the inverse
39
+ is applied with one matrix product per solve (a cuBLAS GEMM on CuPy). A device solve
40
+ then makes no host/device copy. The 1D matrices of Kronecker solvers are small
41
+ (n ~ tens to hundreds), so the n x n inverse is cheap to store, and one GEMM over all
42
+ right-hand sides is faster on a GPU than banded triangular solves.
43
+
44
+ Right-hand sides are rows, as in `BandedSolver.solve`: for ``rhs`` of shape
45
+ ``(m, n)`` the solution is ``rhs @ M`` with ``M = solver.solve(I)``, whose row k is
46
+ the solution for the k-th unit vector, i.e. ``M = op(A)^{-T}``. This holds for any
47
+ linear 1D solver with the row convention, transposed or not.
48
+
49
+ Parameters
50
+ ----------
51
+ solver : LinearSolver
52
+ A 1D solver whose ``solve`` accepts host (NumPy) arrays of shape ``(m, n)``.
53
+
54
+ n : int
55
+ The size of the 1D matrix.
56
+
57
+ dtype : dtype
58
+ The dtype of the right-hand sides.
59
+ """
60
+ def __init__(self, solver, n, dtype):
61
+ eye = np.eye(int(n), dtype=dtype)
62
+ # host bookkeeping at setup: the host solver applied to the identity
63
+ self._host = np.ascontiguousarray(xp.to_numpy(solver.solve(eye)))
64
+ self._device = None
65
+
66
+ @property
67
+ def shape(self):
68
+ return self._host.shape
69
+
70
+ @property
71
+ def host_matrix(self):
72
+ """``M`` on the host."""
73
+ return self._host
74
+
75
+ def device_matrix(self):
76
+ """``M`` on the device, copied there once (on the first call)."""
77
+ if self._device is None:
78
+ self._device = xp.to_cupy(self._host)
79
+ return self._device
80
+
81
+ def matrix_for(self, array):
82
+ """``M`` on the device for a device array, on the host otherwise."""
83
+ return self.device_matrix() if xp.is_gpu(array) else self._host
84
+
85
+ def solve(self, rhs, out=None):
86
+ """
87
+ Solves for the right-hand sides ``rhs`` (rows) where they live, with one matrix product.
88
+
89
+ The right-hand sides are the rows of ``rhs``, as in `BandedSolver.solve`, so the
90
+ solutions are the rows of ``rhs @ op(A)^{-T}``; ``M`` stores ``op(A)^{-T}`` (no
91
+ symmetry is assumed). Multiplying from the right keeps the row-contiguous work
92
+ arrays of `KroneckerLinearSolver` as they are: one GEMM, no transpose copy.
93
+
94
+ ``out`` may be ``rhs`` (in-place solve).
95
+ """
96
+ assert rhs.shape[-1] == self._host.shape[0]
97
+ result = rhs @ self.matrix_for(rhs)
98
+ if out is None:
99
+ return result
100
+ assert out.shape == rhs.shape
101
+ out[...] = result
102
+ return out
103
+
104
+ #===============================================================================
105
+ def _device_inverse(solver, n, dtype):
106
+ """The `DenseInverse` of a 1D solver for device solves, built on first use and cached per dtype."""
107
+ cache = getattr(solver, '_dense_inverses', None)
108
+ if cache is None:
109
+ cache = solver._dense_inverses = {}
110
+ key = np.dtype(dtype)
111
+ if key not in cache:
112
+ cache[key] = DenseInverse(solver, n, key)
113
+ return cache[key]
114
+
31
115
  #===============================================================================
32
116
  class BandedSolver(LinearSolver):
33
117
  """
@@ -117,6 +201,7 @@ class BandedSolver(LinearSolver):
117
201
  obj._space = self._space
118
202
  obj._dtype = self._dtype
119
203
  obj._transposed = not self._transposed
204
+ obj._dense_inverses = {}
120
205
 
121
206
  return obj
122
207
 
@@ -140,11 +225,19 @@ class BandedSolver(LinearSolver):
140
225
 
141
226
  transposed = self._transposed
142
227
 
228
+ # LAPACK is host-only: device data is solved with the dense inverse on the
229
+ # device (see DenseInverse). Decided by the array itself, not the global
230
+ # backend, since host arrays may be passed on the CuPy backend too.
231
+ if xp.is_gpu(rhs):
232
+ if out is not None:
233
+ assert out.shape == rhs.shape
234
+ assert out.dtype == rhs.dtype
235
+ return _device_inverse(self, self._bmat.shape[1], rhs.dtype).solve(rhs, out=out)
236
+
143
237
  if out is None:
144
- # LAPACK is host-only: solve on the host, return on the caller's backend.
145
- preout, self._sinfo = self._solver_function(self._bmat, self._l, self._u, xp.to_numpy(rhs).T,
238
+ preout, self._sinfo = self._solver_function(self._bmat, self._l, self._u, rhs.T,
146
239
  self._ipiv, trans=transposed)
147
- out = xp.asarray(preout.T) if xp.is_gpu(rhs) else preout.T
240
+ out = preout.T
148
241
 
149
242
  else:
150
243
  assert out.shape == rhs.shape
@@ -157,16 +250,7 @@ class BandedSolver(LinearSolver):
157
250
  # TODO: handle non-contiguous views?
158
251
 
159
252
  # we want FORTRAN-contiguous data (default is assumed to be C contiguous).
160
- # LAPACK is host-only: a device array is solved in a host copy. Decided by
161
- # the array itself, not the global backend, since host arrays may be passed
162
- # on the CuPy backend too.
163
- if xp.is_gpu(out):
164
- out_cpu = xp.to_numpy(out)
165
- _, self._sinfo = self._solver_function(self._bmat, self._l, self._u, out_cpu.T, self._ipiv, overwrite_b=True,
166
- trans=transposed)
167
- out[...] = xp.asarray(out_cpu)
168
- else:
169
- _, self._sinfo = self._solver_function(self._bmat, self._l, self._u, out.T, self._ipiv, overwrite_b=True,
253
+ _, self._sinfo = self._solver_function(self._bmat, self._l, self._u, out.T, self._ipiv, overwrite_b=True,
170
254
  trans=transposed)
171
255
 
172
256
  return out
@@ -206,6 +290,7 @@ class SparseSolver (LinearSolver):
206
290
  obj._space = self._space
207
291
  obj._splu = self._splu
208
292
  obj._transposed = not self._transposed
293
+ obj._dense_inverses = {}
209
294
 
210
295
  return obj
211
296
 
@@ -229,19 +314,22 @@ class SparseSolver (LinearSolver):
229
314
  assert rhs.T.shape[0] == self._splu.shape[1]
230
315
  transposed = self._transposed
231
316
 
317
+ # SuperLU is host-only: device data is solved with the dense inverse on the
318
+ # device (see DenseInverse); decided by the array, not the global backend.
319
+ if xp.is_gpu(rhs):
320
+ if out is not None:
321
+ assert out.shape == rhs.shape
322
+ assert out.dtype == rhs.dtype
323
+ return _device_inverse(self, self._splu.shape[1], rhs.dtype).solve(rhs, out=out)
324
+
232
325
  if out is None:
233
- # SuperLU is host-only: solve on the host, return on the caller's backend.
234
- out = self._splu.solve(xp.to_numpy(rhs).T, trans='T' if transposed else 'N').T
235
- if xp.is_gpu(rhs):
236
- out = xp.asarray(out)
326
+ out = self._splu.solve(rhs.T, trans='T' if transposed else 'N').T
237
327
 
238
328
  else:
239
329
  assert out.shape == rhs.shape
240
330
  assert out.dtype == rhs.dtype
241
331
 
242
- # currently no in-place solve exposed. SuperLU is host-only; decided by
243
- # the arrays themselves, not the global backend.
244
- result = self._splu.solve(xp.to_numpy(rhs).T, trans='T' if transposed else 'N').T
245
- out[:] = xp.asarray(result) if xp.is_gpu(out) else result
332
+ # currently no in-place solve exposed.
333
+ out[:] = self._splu.solve(rhs.T, trans='T' if transposed else 'N').T
246
334
 
247
335
  return out
@@ -36,6 +36,9 @@ class DistributedFFTBase(LinearOperator):
36
36
  function : Callable
37
37
  The given function.
38
38
  """
39
+ # KroneckerLinearSolver: keep applying the function (not its dense matrix) to device data
40
+ dense_on_device = False
41
+
39
42
  def __init__(self, function):
40
43
  self._function = function
41
44
 
@@ -7,15 +7,16 @@
7
7
  *
8
8
  * One thread per entry of `out` (n_threads = out.size, see __init__.py). A thread outside the owned rows
9
9
  * (local row index i1_loc outside [0, e_out - s_out]) returns without writing, so the padding of `out` is
10
- * left as it is, as in pyccel. Interior rows use 2 * p_in + 1 diagonals, the last owned row (i1 == e_out)
11
- * uses 2 * p_in + add, which is how a rectangular matrix is handled.
10
+ * left as it is, as in pyccel. The matrix has n = mat.shape[1] diagonals (its pads are q = (n - 1) / 2) and
11
+ * diagonal d1 of row i1 is the column i1 - q + d1. Interior rows use all n diagonals, the last owned row
12
+ * (i1 == e_out) uses n - 1 + add, which is how a rectangular matrix is handled.
12
13
  *
13
- * @param mat matrix data, shape (rows of `out`, 2 * p_in + 1)
14
+ * @param mat matrix data, shape (rows of `out`, diagonals)
14
15
  * @param x data of the domain vector, ghost regions included
15
16
  * @param out data of the codomain vector; the owned rows are written
16
17
  * @param s_in global start of the domain of this process
17
18
  * @param p_in padding of the domain
18
- * @param add 1 if the last row uses all 2 * p_in + 1 diagonals, else 0
19
+ * @param add 1 if the last row uses all diagonals, else 0
19
20
  * @param s_out global start of the codomain of this process
20
21
  * @param e_out global end (inclusive) of the codomain of this process
21
22
  * @param p_out padding of the codomain: the owned rows start at index p_out of `mat` and `out`
@@ -30,11 +31,13 @@ extern "C" __global__ void stencil_dot_1d(Array2D<double> mat, Array1D<double> x
30
31
  if (i1_loc < 0 || i1_loc > e_out - s_out) return;
31
32
  const long long i1 = s_out + i1_loc; // global row index
32
33
 
33
- const long long n_diags1 = (i1 == e_out) ? 2 * p_in + add : 2 * p_in + 1;
34
+ const long long n_diags1 = mat.shape[1];
35
+ const long long nd1 = (i1 == e_out) ? n_diags1 - 1 + add : n_diags1;
36
+ const long long off1 = p_in - (n_diags1 - 1) / 2 - s_in; // x index of diagonal 0 minus i1
34
37
 
35
38
  double val = 0.;
36
- for (long long d1 = 0; d1 < n_diags1; ++d1)
37
- val += mat(p_out + i1_loc, d1) * x(i1 + d1 - s_in);
39
+ for (long long d1 = 0; d1 < nd1; ++d1)
40
+ val += mat(p_out + i1_loc, d1) * x(i1 + d1 + off1);
38
41
 
39
42
  out(p_out + i1_loc) = val;
40
43
  }
@@ -0,0 +1,38 @@
1
+ """Stencil matrix-vector product ``out = mat @ x`` on the owned rows (1D).
2
+
3
+ The host version of ``stencil_dot_1d``.
4
+
5
+ Moved from ``feectools.linalg.stencil_dot_kernels.matvec_1d_kernel``. The CUDA version in ``stencil_dot_1d_cuda.cu``
6
+ takes the same arguments in the same order.
7
+
8
+ The number of diagonals is read from ``mat``: ``n = mat.shape[1]`` diagonals, the pads of the matrix are
9
+ ``q = (n - 1) // 2`` (at most the pads ``p_in`` of the domain), and diagonal ``d`` of row ``i`` is the column
10
+ ``i - q + d``. Interior rows use all ``n`` diagonals, the last owned row ``n - 1 + add``.
11
+ """
12
+
13
+
14
+ def stencil_dot_1d(mat: 'float[:, :]',
15
+ x: 'float[:]',
16
+ out: 'float[:]',
17
+ s_in: int,
18
+ p_in: int,
19
+ add: int,
20
+ s_out: int,
21
+ e_out: int,
22
+ p_out: int):
23
+
24
+ n_diags1 = mat.shape[1]
25
+ # x index of diagonal 0 minus the global row index: column i1 - q + d1 is at x[i1 - q + d1 - s_in + p_in]
26
+ off1 = p_in - (n_diags1 - 1) // 2 - s_in
27
+
28
+ for i1 in range(s_out, e_out + 1): # global row index
29
+ i1_loc = i1 - s_out # local row index
30
+ nd1 = n_diags1
31
+ if i1 == e_out:
32
+ nd1 = n_diags1 - 1 + add
33
+
34
+ val = 0.
35
+ for d1 in range(nd1):
36
+ val += mat[p_out + i1_loc, d1] * x[i1 + d1 + off1]
37
+
38
+ out[p_out + i1_loc] = val
@@ -7,11 +7,11 @@
7
7
  *
8
8
  * One thread per entry of `out` (n_threads = out.size, see __init__.py), the last axis varying fastest. A
9
9
  * thread outside the owned rows returns without writing, so the padding of `out` is left as it is, as in
10
- * pyccel. Along each direction k, interior rows use 2 * p_in[k] + 1 diagonals and the last owned row
11
- * (i_k == e_out[k]) uses 2 * p_in[k] + add[k]; the pyccel kernel spells out the four combinations, this
12
- * kernel picks its own per thread. The diagonals are summed in the same order (d1 outer, d2 inner).
10
+ * pyccel. Along each direction k the matrix has n_k = mat.shape[2 + k] diagonals; interior rows use all of
11
+ * them, the last owned row (i_k == e_out[k]) uses n_k - 1 + add[k], as in the 1D kernel. The diagonals are
12
+ * summed in the same order as in pyccel (d1 outer, d2 inner).
13
13
  *
14
- * @param mat matrix data, shape (rows of `out`..., 2 * p_in + 1...)
14
+ * @param mat matrix data, shape (rows of `out`..., diagonals...)
15
15
  * @param x data of the domain vector, ghost regions included
16
16
  * @param out data of the codomain vector; the owned rows are written
17
17
  * @param s_in, p_in, add, s_out, e_out, p_out per direction (length 2), as in the 1D kernel
@@ -30,13 +30,16 @@ extern "C" __global__ void stencil_dot_2d(Array4D<double> mat, Array2D<double> x
30
30
  const long long i1 = s_out[0] + i1_loc; // global row indices
31
31
  const long long i2 = s_out[1] + i2_loc;
32
32
 
33
- const long long n_diags1 = (i1 == e_out[0]) ? 2 * p_in[0] + add[0] : 2 * p_in[0] + 1;
34
- const long long n_diags2 = (i2 == e_out[1]) ? 2 * p_in[1] + add[1] : 2 * p_in[1] + 1;
33
+ const long long nd1 = (i1 == e_out[0]) ? mat.shape[2] - 1 + add[0] : mat.shape[2];
34
+ const long long nd2 = (i2 == e_out[1]) ? mat.shape[3] - 1 + add[1] : mat.shape[3];
35
+ // x index of diagonal 0 minus the global row index, per direction
36
+ const long long off1 = p_in[0] - (mat.shape[2] - 1) / 2 - s_in[0];
37
+ const long long off2 = p_in[1] - (mat.shape[3] - 1) / 2 - s_in[1];
35
38
 
36
39
  double val = 0.;
37
- for (long long d1 = 0; d1 < n_diags1; ++d1)
38
- for (long long d2 = 0; d2 < n_diags2; ++d2)
39
- val += mat(p_out[0] + i1_loc, p_out[1] + i2_loc, d1, d2) * x(i1 + d1 - s_in[0], i2 + d2 - s_in[1]);
40
+ for (long long d1 = 0; d1 < nd1; ++d1)
41
+ for (long long d2 = 0; d2 < nd2; ++d2)
42
+ val += mat(p_out[0] + i1_loc, p_out[1] + i2_loc, d1, d2) * x(i1 + d1 + off1, i2 + d2 + off2);
40
43
 
41
44
  out(p_out[0] + i1_loc, p_out[1] + i2_loc) = val;
42
45
  }
@@ -0,0 +1,47 @@
1
+ """Stencil matrix-vector product ``out = mat @ x`` on the owned rows (2D).
2
+
3
+ The host version of ``stencil_dot_2d``.
4
+
5
+ Moved from ``feectools.linalg.stencil_dot_kernels.matvec_2d_kernel``. The CUDA version in ``stencil_dot_2d_cuda.cu``
6
+ takes the same arguments in the same order.
7
+
8
+ The number of diagonals is read from ``mat``, per direction as in ``stencil_dot_1d``: ``n_k = mat.shape[2 + k]``,
9
+ the pads of the matrix are ``q_k = (n_k - 1) // 2`` and diagonal ``d_k`` of row ``i_k`` is the column
10
+ ``i_k - q_k + d_k``. Interior rows use all ``n_k`` diagonals, the last owned row ``n_k - 1 + add[k]``.
11
+ """
12
+
13
+
14
+ def stencil_dot_2d(mat: 'float[:, :, :, :]',
15
+ x: 'float[:, :]',
16
+ out: 'float[:, :]',
17
+ s_in: 'int[:]',
18
+ p_in: 'int[:]',
19
+ add: 'int[:]',
20
+ s_out: 'int[:]',
21
+ e_out: 'int[:]',
22
+ p_out: 'int[:]'):
23
+
24
+ n_diags1 = mat.shape[2]
25
+ n_diags2 = mat.shape[3]
26
+ # x index of diagonal 0 minus the global row index, per direction
27
+ off1 = p_in[0] - (n_diags1 - 1) // 2 - s_in[0]
28
+ off2 = p_in[1] - (n_diags2 - 1) // 2 - s_in[1]
29
+
30
+ for i1 in range(s_out[0], e_out[0] + 1): # global row indices
31
+ i1_loc = i1 - s_out[0] # local row indices
32
+ nd1 = n_diags1
33
+ if i1 == e_out[0]:
34
+ nd1 = n_diags1 - 1 + add[0]
35
+
36
+ for i2 in range(s_out[1], e_out[1] + 1):
37
+ i2_loc = i2 - s_out[1]
38
+ nd2 = n_diags2
39
+ if i2 == e_out[1]:
40
+ nd2 = n_diags2 - 1 + add[1]
41
+
42
+ val = 0.
43
+ for d1 in range(nd1):
44
+ for d2 in range(nd2):
45
+ val += mat[p_out[0] + i1_loc, p_out[1] + i2_loc, d1, d2] * x[i1 + d1 + off1, i2 + d2 + off2]
46
+
47
+ out[p_out[0] + i1_loc, p_out[1] + i2_loc] = val
@@ -0,0 +1,52 @@
1
+ // CUDA version of stencil_dot_3d (stencil_dot_3d_kernels.py), with the same arguments in the same order.
2
+ #include "cunumpy/array_view.cuh"
3
+ #include "cunumpy/index.cuh"
4
+
5
+ /**
6
+ * Stencil matrix-vector product out = mat @ x on the owned rows, as the pyccel kernel stencil_dot_3d.
7
+ *
8
+ * One thread per entry of `out` (n_threads = out.size, see __init__.py), the last axis varying fastest. A
9
+ * thread outside the owned rows returns without writing, so the padding of `out` is left as it is, as in
10
+ * pyccel. Along each direction k the matrix has n_k = mat.shape[3 + k] diagonals; interior rows use all of
11
+ * them, the last owned row (i_k == e_out[k]) uses n_k - 1 + add[k], as in the 1D kernel. The diagonals are
12
+ * summed in the same order as in pyccel (d1, d2, d3 innermost).
13
+ *
14
+ * @param mat matrix data (a 6D view), shape (rows of `out`..., diagonals...)
15
+ * @param x data of the domain vector, ghost regions included
16
+ * @param out data of the codomain vector; the owned rows are written
17
+ * @param s_in, p_in, add, s_out, e_out, p_out per direction (length 3), as in the 1D kernel
18
+ */
19
+ extern "C" __global__ void stencil_dot_3d(Array6D<double> mat, Array3D<double> x, Array3D<double> out,
20
+ const long long* s_in, const long long* p_in, const long long* add,
21
+ const long long* s_out, const long long* e_out,
22
+ const long long* p_out)
23
+ {
24
+ CUNUMPY_THREAD_1D(thread, out.size());
25
+
26
+ const long long i1_loc = thread / (out.shape[1] * out.shape[2]) - p_out[0]; // local row indices
27
+ const long long i2_loc = (thread / out.shape[2]) % out.shape[1] - p_out[1];
28
+ const long long i3_loc = thread % out.shape[2] - p_out[2];
29
+ if (i1_loc < 0 || i1_loc > e_out[0] - s_out[0]) return;
30
+ if (i2_loc < 0 || i2_loc > e_out[1] - s_out[1]) return;
31
+ if (i3_loc < 0 || i3_loc > e_out[2] - s_out[2]) return;
32
+ const long long i1 = s_out[0] + i1_loc; // global row indices
33
+ const long long i2 = s_out[1] + i2_loc;
34
+ const long long i3 = s_out[2] + i3_loc;
35
+
36
+ const long long nd1 = (i1 == e_out[0]) ? mat.shape[3] - 1 + add[0] : mat.shape[3];
37
+ const long long nd2 = (i2 == e_out[1]) ? mat.shape[4] - 1 + add[1] : mat.shape[4];
38
+ const long long nd3 = (i3 == e_out[2]) ? mat.shape[5] - 1 + add[2] : mat.shape[5];
39
+ // x index of diagonal 0 minus the global row index, per direction
40
+ const long long off1 = p_in[0] - (mat.shape[3] - 1) / 2 - s_in[0];
41
+ const long long off2 = p_in[1] - (mat.shape[4] - 1) / 2 - s_in[1];
42
+ const long long off3 = p_in[2] - (mat.shape[5] - 1) / 2 - s_in[2];
43
+
44
+ double val = 0.;
45
+ for (long long d1 = 0; d1 < nd1; ++d1)
46
+ for (long long d2 = 0; d2 < nd2; ++d2)
47
+ for (long long d3 = 0; d3 < nd3; ++d3)
48
+ val += mat(p_out[0] + i1_loc, p_out[1] + i2_loc, p_out[2] + i3_loc, d1, d2, d3) *
49
+ x(i1 + d1 + off1, i2 + d2 + off2, i3 + d3 + off3);
50
+
51
+ out(p_out[0] + i1_loc, p_out[1] + i2_loc, p_out[2] + i3_loc) = val;
52
+ }
@@ -0,0 +1,63 @@
1
+ """Stencil matrix-vector product ``out = mat @ x`` on the owned rows (3D).
2
+
3
+ The host version of ``stencil_dot_3d``.
4
+
5
+ Moved from ``feectools.linalg.stencil_dot_kernels.matvec_3d_kernel``. The CUDA version in ``stencil_dot_3d_cuda.cu``
6
+ takes the same arguments in the same order.
7
+
8
+ The number of diagonals is read from ``mat``, per direction as in ``stencil_dot_1d``: ``n_k = mat.shape[3 + k]``,
9
+ the pads of the matrix are ``q_k = (n_k - 1) // 2`` and diagonal ``d_k`` of row ``i_k`` is the column
10
+ ``i_k - q_k + d_k``. Interior rows use all ``n_k`` diagonals, the last owned row ``n_k - 1 + add[k]``.
11
+ """
12
+
13
+
14
+ def stencil_dot_3d(mat: 'float[:, :, :, :, :, :]',
15
+ x: 'float[:, :, :]',
16
+ out: 'float[:, :, :]',
17
+ s_in: 'int[:]',
18
+ p_in: 'int[:]',
19
+ add: 'int[:]',
20
+ s_out: 'int[:]',
21
+ e_out: 'int[:]',
22
+ p_out: 'int[:]'):
23
+
24
+ n_diags1 = mat.shape[3]
25
+ n_diags2 = mat.shape[4]
26
+ n_diags3 = mat.shape[5]
27
+ # x index of diagonal 0 minus the global row index, per direction
28
+ off1 = p_in[0] - (n_diags1 - 1) // 2 - s_in[0]
29
+ off2 = p_in[1] - (n_diags2 - 1) // 2 - s_in[1]
30
+ off3 = p_in[2] - (n_diags3 - 1) // 2 - s_in[2]
31
+
32
+ for i1 in range(s_out[0], e_out[0] + 1): # global row indices
33
+ i1_loc = i1 - s_out[0] # local row indices
34
+ nd1 = n_diags1
35
+ if i1 == e_out[0]:
36
+ nd1 = n_diags1 - 1 + add[0]
37
+
38
+ for i2 in range(s_out[1], e_out[1] + 1):
39
+ i2_loc = i2 - s_out[1]
40
+ nd2 = n_diags2
41
+ if i2 == e_out[1]:
42
+ nd2 = n_diags2 - 1 + add[1]
43
+
44
+ for i3 in range(s_out[2], e_out[2] + 1):
45
+ i3_loc = i3 - s_out[2]
46
+ nd3 = n_diags3
47
+ if i3 == e_out[2]:
48
+ nd3 = n_diags3 - 1 + add[2]
49
+
50
+ val = 0.
51
+ for d1 in range(nd1):
52
+ for d2 in range(nd2):
53
+ for d3 in range(nd3):
54
+ val += mat[p_out[0] + i1_loc,
55
+ p_out[1] + i2_loc,
56
+ p_out[2] + i3_loc,
57
+ d1, d2, d3] * x[i1 + d1 + off1,
58
+ i2 + d2 + off2,
59
+ i3 + d3 + off3]
60
+
61
+ out[p_out[0] + i1_loc,
62
+ p_out[1] + i2_loc,
63
+ p_out[2] + i3_loc] = val
@@ -6,24 +6,24 @@
6
6
  * Transpose of a stencil matrix, matT = mat.T on the owned rows of matT, as the pyccel kernel
7
7
  * stencil_transpose_1d.
8
8
  *
9
- * One thread per entry (row, diagonal) of `matT` (n_threads = matT.size, see __init__.py). A thread outside
10
- * the owned rows of matT, or at a diagonal d1 the pyccel kernel does not write (d1 >= 2 * p_in + 1, or
11
- * >= 2 * p_in + add in the last owned row), returns without writing. Every other thread copies one entry:
12
- * row i1 of matT, column j1 = i1 - p_in + d1, is row j1 of mat at diagonal p_out + i1 - j1.
9
+ * One thread per entry (row, diagonal) of `matT` (n_threads = matT.size, see __init__.py). matT has
10
+ * n = matT.shape[1] diagonals (pads qT = (n - 1) / 2), mat has the pads q = (mat.shape[1] - 1) / 2. A thread
11
+ * outside the owned rows of matT, or at a diagonal d1 the pyccel kernel does not write (d1 >= n - 1 + add in
12
+ * the last owned row), returns without writing. Every other thread copies one entry: row i1 of matT, column
13
+ * j1 = i1 - qT + d1, is row j1 of mat at diagonal q + i1 - j1.
13
14
  *
14
15
  * @param mat matrix data, shape (rows of the codomain of mat, diagonals)
15
16
  * @param matT data of the transposed matrix; the owned rows are written
16
17
  * @param s_in global start of the rows of mat (= columns of matT) of this process
17
- * @param e_in global end of the rows of mat; not needed in 1D (`mat` is a view), kept for the signature
18
18
  * @param p_in padding of the rows of mat
19
- * @param add 1 if the last row of matT uses all 2 * p_in + 1 diagonals, else 0
19
+ * @param add 1 if the last row of matT uses all diagonals, else 0
20
20
  * @param s_out global start of the rows of matT of this process
21
21
  * @param e_out global end (inclusive) of the rows of matT of this process
22
22
  * @param p_out padding of the rows of matT
23
23
  */
24
24
  extern "C" __global__ void stencil_transpose_1d(Array2D<double> mat, Array2D<double> matT, long long s_in,
25
- long long e_in, long long p_in, long long add,
26
- long long s_out, long long e_out, long long p_out)
25
+ long long p_in, long long add, long long s_out,
26
+ long long e_out, long long p_out)
27
27
  {
28
28
  CUNUMPY_THREAD_1D(thread, matT.size());
29
29
 
@@ -31,10 +31,10 @@ extern "C" __global__ void stencil_transpose_1d(Array2D<double> mat, Array2D<dou
31
31
  const long long i1_loc = thread / matT.shape[1] - p_out; // local row index of matT
32
32
  if (i1_loc < 0 || i1_loc > e_out - s_out) return;
33
33
  const long long i1 = s_out + i1_loc; // global row index of matT = global column index of mat
34
- if (d1 >= ((i1 == e_out) ? 2 * p_in + add : 2 * p_in + 1)) return;
34
+ if (i1 == e_out && d1 >= matT.shape[1] - 1 + add) return;
35
35
 
36
- const long long j1 = i1 - p_in + d1; // global column index of matT
37
- const long long j1_loc = j1 - s_in; // local column index of matT = local row index of mat
36
+ const long long j1 = i1 - (matT.shape[1] - 1) / 2 + d1; // global column index of matT
37
+ const long long j1_loc = j1 - s_in; // local column index of matT = local row index of mat
38
38
 
39
- matT(p_out + i1_loc, d1) = mat(p_in + j1_loc, p_out + i1 - j1);
39
+ matT(p_out + i1_loc, d1) = mat(p_in + j1_loc, (mat.shape[1] - 1) / 2 + i1 - j1);
40
40
  }
@@ -0,0 +1,38 @@
1
+ """Transpose of a stencil matrix, ``matT = mat.T`` on the owned rows of ``matT`` (1D).
2
+
3
+ The host version of ``stencil_transpose_1d``.
4
+
5
+ Moved from ``feectools.linalg.stencil_transpose_kernels.transpose_1d_kernel``. The CUDA version in
6
+ ``stencil_transpose_1d_cuda.cu`` takes the same arguments in the same order.
7
+
8
+ The numbers of diagonals are read from the matrices: ``matT`` has ``n = matT.shape[1]`` diagonals, so its pads are
9
+ ``qT = (n - 1) // 2`` and diagonal ``d`` of row ``i`` is the column ``j = i - qT + d``; that entry is row ``j`` of
10
+ ``mat`` at the diagonal ``q + i - j``, with ``q = (mat.shape[1] - 1) // 2``. Interior rows of ``matT`` get all ``n``
11
+ diagonals, the last owned row ``n - 1 + add``.
12
+ """
13
+
14
+
15
+ def stencil_transpose_1d(mat: 'float[:, :]',
16
+ matT: 'float[:, :]',
17
+ s_in: int, # refers to matT
18
+ p_in: int,
19
+ add: int,
20
+ s_out: int,
21
+ e_out: int,
22
+ p_out: int):
23
+
24
+ n_diags1 = matT.shape[1]
25
+ qT1 = (n_diags1 - 1) // 2 # pads of matT
26
+ q1 = (mat.shape[1] - 1) // 2 # pads of mat
27
+
28
+ for i1 in range(s_out, e_out + 1): # global row index of matT = global column index of mat
29
+ i1_loc = i1 - s_out # local row index of matT
30
+ nd1 = n_diags1
31
+ if i1 == e_out:
32
+ nd1 = n_diags1 - 1 + add
33
+
34
+ for d1 in range(nd1):
35
+ j1 = i1 - qT1 + d1 # global column index of matT
36
+ j1_loc = j1 - s_in # local column index of matT = local row index of mat
37
+
38
+ matT[p_out + i1_loc, d1] = mat[p_in + j1_loc, q1 + i1 - j1]