feectools 0.5.0__tar.gz → 0.7.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 (162) hide show
  1. {feectools-0.5.0/feectools.egg-info → feectools-0.7.0}/PKG-INFO +1 -1
  2. {feectools-0.5.0 → feectools-0.7.0}/feectools/accelerate/compile_psydac.mk +1 -1
  3. {feectools-0.5.0 → feectools-0.7.0}/feectools/api/fem_bilinear_form.py +2 -2
  4. {feectools-0.5.0 → feectools-0.7.0}/feectools/api/fem_common.py +1 -1
  5. {feectools-0.5.0 → feectools-0.7.0}/feectools/feec/derivatives.py +15 -0
  6. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/basic.py +29 -19
  7. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/block.py +5 -2
  8. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/direct_solvers.py +110 -22
  9. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/fft.py +3 -0
  10. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_dot_1d/stencil_dot_1d_cuda.cu +10 -7
  11. feectools-0.7.0/feectools/linalg/kernels/stencil_dot_1d/stencil_dot_1d_kernels.py +38 -0
  12. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_dot_2d/stencil_dot_2d_cuda.cu +12 -9
  13. feectools-0.7.0/feectools/linalg/kernels/stencil_dot_2d/stencil_dot_2d_kernels.py +47 -0
  14. feectools-0.7.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_cuda.cu +52 -0
  15. feectools-0.7.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_kernels.py +63 -0
  16. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_transpose_1d/stencil_transpose_1d_cuda.cu +12 -12
  17. feectools-0.7.0/feectools/linalg/kernels/stencil_transpose_1d/stencil_transpose_1d_kernels.py +38 -0
  18. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_transpose_2d/stencil_transpose_2d_cuda.cu +11 -12
  19. feectools-0.7.0/feectools/linalg/kernels/stencil_transpose_2d/stencil_transpose_2d_kernels.py +52 -0
  20. feectools-0.7.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_cuda.cu +57 -0
  21. feectools-0.7.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_kernels.py +69 -0
  22. feectools-0.7.0/feectools/linalg/kron.py +1578 -0
  23. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/solvers.py +62 -32
  24. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/stencil.py +48 -27
  25. feectools-0.7.0/feectools/linalg/tests/inner_on_device_child.py +149 -0
  26. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/kernel_test_args.py +34 -17
  27. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_cuda_parity.py +1 -1
  28. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_device_matvec.py +63 -20
  29. feectools-0.7.0/feectools/linalg/tests/test_inner_on_device.py +160 -0
  30. feectools-0.7.0/feectools/linalg/tests/test_kron_device_solve.py +284 -0
  31. feectools-0.7.0/feectools/linalg/tests/test_kron_stencil_matrix.py +526 -0
  32. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_mpi_device.py +62 -0
  33. {feectools-0.5.0 → feectools-0.7.0/feectools.egg-info}/PKG-INFO +1 -1
  34. {feectools-0.5.0 → feectools-0.7.0}/feectools.egg-info/SOURCES.txt +3 -0
  35. {feectools-0.5.0 → feectools-0.7.0}/pyproject.toml +1 -1
  36. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_1d/stencil_dot_1d_kernels.py +0 -35
  37. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_2d/stencil_dot_2d_kernels.py +0 -99
  38. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_cuda.cu +0 -64
  39. feectools-0.5.0/feectools/linalg/kernels/stencil_dot_3d/stencil_dot_3d_kernels.py +0 -206
  40. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_1d/stencil_transpose_1d_kernels.py +0 -37
  41. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_2d/stencil_transpose_2d_kernels.py +0 -116
  42. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_cuda.cu +0 -76
  43. feectools-0.5.0/feectools/linalg/kernels/stencil_transpose_3d/stencil_transpose_3d_kernels.py +0 -252
  44. feectools-0.5.0/feectools/linalg/kron.py +0 -933
  45. feectools-0.5.0/feectools/linalg/tests/test_kron_stencil_matrix.py +0 -115
  46. {feectools-0.5.0 → feectools-0.7.0}/AUTHORS +0 -0
  47. {feectools-0.5.0 → feectools-0.7.0}/LICENSE +0 -0
  48. {feectools-0.5.0 → feectools-0.7.0}/README.md +0 -0
  49. {feectools-0.5.0 → feectools-0.7.0}/feectools/__init__.py +0 -0
  50. {feectools-0.5.0 → feectools-0.7.0}/feectools/accelerate/__init__.py +0 -0
  51. {feectools-0.5.0 → feectools-0.7.0}/feectools/accelerate/accelerate.py +0 -0
  52. {feectools-0.5.0 → feectools-0.7.0}/feectools/api/__init__.py +0 -0
  53. {feectools-0.5.0 → feectools-0.7.0}/feectools/api/essential_bc.py +0 -0
  54. {feectools-0.5.0 → feectools-0.7.0}/feectools/api/fem_sum_form.py +0 -0
  55. {feectools-0.5.0 → feectools-0.7.0}/feectools/api/settings.py +0 -0
  56. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/__init__.py +0 -0
  57. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/bsplines.py +0 -0
  58. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/bsplines_kernels.py +0 -0
  59. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/field_evaluation_kernels.py +0 -0
  60. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/tests/__init__.py +0 -0
  61. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/tests/test_bsplines.py +0 -0
  62. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/tests/test_bsplines_kernel.py +0 -0
  63. {feectools-0.5.0 → feectools-0.7.0}/feectools/core/tests/test_bsplines_pyccel.py +0 -0
  64. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/__init__.py +0 -0
  65. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/basic.py +0 -0
  66. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/blocking_data_exchanger.py +0 -0
  67. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/cart.py +0 -0
  68. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/interface_data_exchanger.py +0 -0
  69. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/nonblocking_data_exchanger.py +0 -0
  70. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/partition.py +0 -0
  71. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/petsc.py +0 -0
  72. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/__init__.py +0 -0
  73. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/test_cart_1d.py +0 -0
  74. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/test_cart_2d.py +0 -0
  75. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/test_cart_3d.py +0 -0
  76. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/test_coarsen.py +0 -0
  77. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/test_device_binding.py +0 -0
  78. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/test_multicart_2d.py +0 -0
  79. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/tests/test_partition.py +0 -0
  80. {feectools-0.5.0 → feectools-0.7.0}/feectools/ddm/utilities.py +0 -0
  81. {feectools-0.5.0 → feectools-0.7.0}/feectools/feec/__init__.py +0 -0
  82. {feectools-0.5.0 → feectools-0.7.0}/feectools/feec/dof_kernels.py +0 -0
  83. {feectools-0.5.0 → feectools-0.7.0}/feectools/feec/global_geometric_projectors.py +0 -0
  84. {feectools-0.5.0 → feectools-0.7.0}/feectools/feec/hodge.py +0 -0
  85. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/__init__.py +0 -0
  86. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/basic.py +0 -0
  87. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/grid.py +0 -0
  88. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/lst_preconditioner.py +0 -0
  89. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/partitioning.py +0 -0
  90. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/projectors.py +0 -0
  91. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/splines.py +0 -0
  92. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tensor.py +0 -0
  93. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/__init__.py +0 -0
  94. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/analytical_profiles_1d.py +0 -0
  95. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/analytical_profiles_base.py +0 -0
  96. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/splines_error_bounds.py +0 -0
  97. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/test_dirichlet_projectors.py +0 -0
  98. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/test_spline_histopolation.py +0 -0
  99. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/test_spline_interpolation.py +0 -0
  100. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/test_splines.py +0 -0
  101. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/test_splines_par.py +0 -0
  102. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/test_tensor.py +0 -0
  103. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/test_vector_spaces.py +0 -0
  104. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/tests/utilities.py +0 -0
  105. {feectools-0.5.0 → feectools-0.7.0}/feectools/fem/vector.py +0 -0
  106. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/__init__.py +0 -0
  107. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/__init__.py +0 -0
  108. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/matvec_kernels.py +0 -0
  109. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil2IJV_kernels.py +0 -0
  110. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil2coo_kernels.py +0 -0
  111. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_1d/__init__.py +0 -0
  112. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_1d/stencil_axpy_1d_cuda.cu +0 -0
  113. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_1d/stencil_axpy_1d_kernels.py +0 -0
  114. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_2d/__init__.py +0 -0
  115. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_2d/stencil_axpy_2d_cuda.cu +0 -0
  116. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_2d/stencil_axpy_2d_kernels.py +0 -0
  117. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_3d/__init__.py +0 -0
  118. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_3d/stencil_axpy_3d_cuda.cu +0 -0
  119. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_axpy_3d/stencil_axpy_3d_kernels.py +0 -0
  120. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_dot_1d/__init__.py +0 -0
  121. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_dot_2d/__init__.py +0 -0
  122. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_dot_3d/__init__.py +0 -0
  123. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_1d/__init__.py +0 -0
  124. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_1d/stencil_inner_1d_cuda.cu +0 -0
  125. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_1d/stencil_inner_1d_kernels.py +0 -0
  126. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_2d/__init__.py +0 -0
  127. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_2d/stencil_inner_2d_cuda.cu +0 -0
  128. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_2d/stencil_inner_2d_kernels.py +0 -0
  129. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_3d/__init__.py +0 -0
  130. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_3d/stencil_inner_3d_cuda.cu +0 -0
  131. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_inner_3d/stencil_inner_3d_kernels.py +0 -0
  132. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_transpose_1d/__init__.py +0 -0
  133. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_transpose_2d/__init__.py +0 -0
  134. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/stencil_transpose_3d/__init__.py +0 -0
  135. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/kernels/transpose_kernels.py +0 -0
  136. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/memory.py +0 -0
  137. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/sparse.py +0 -0
  138. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/__init__.py +0 -0
  139. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/cuda_parity_cases.py +0 -0
  140. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_axpy_ghost_sync.py +0 -0
  141. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_block.py +0 -0
  142. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_cuda_emulation.py +0 -0
  143. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_fft.py +0 -0
  144. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_linalg.py +0 -0
  145. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_matrix_free.py +0 -0
  146. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_solvers.py +0 -0
  147. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_stencil_interface_matrix.py +0 -0
  148. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_stencil_vector.py +0 -0
  149. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_stencil_vector_space.py +0 -0
  150. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/test_toarray.py +0 -0
  151. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/tests/utilities.py +0 -0
  152. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/topetsc.py +0 -0
  153. {feectools-0.5.0 → feectools-0.7.0}/feectools/linalg/utilities.py +0 -0
  154. {feectools-0.5.0 → feectools-0.7.0}/feectools/utilities/__init__.py +0 -0
  155. {feectools-0.5.0 → feectools-0.7.0}/feectools/utilities/quadratures.py +0 -0
  156. {feectools-0.5.0 → feectools-0.7.0}/feectools/utilities/utils.py +0 -0
  157. {feectools-0.5.0 → feectools-0.7.0}/feectools/version.py +0 -0
  158. {feectools-0.5.0 → feectools-0.7.0}/feectools.egg-info/dependency_links.txt +0 -0
  159. {feectools-0.5.0 → feectools-0.7.0}/feectools.egg-info/entry_points.txt +0 -0
  160. {feectools-0.5.0 → feectools-0.7.0}/feectools.egg-info/requires.txt +0 -0
  161. {feectools-0.5.0 → feectools-0.7.0}/feectools.egg-info/top_level.txt +0 -0
  162. {feectools-0.5.0 → feectools-0.7.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.7.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>
@@ -37,7 +37,7 @@ all: $(OUTPUTS)
37
37
  @for dep in $^ ; do \
38
38
  echo $$dep ; \
39
39
  done
40
- pyccel compile -v $(FLAGS)$(FLAGS_openmp) $<
40
+ pyccel compile $(FLAGS)$(FLAGS_openmp) $<
41
41
  @echo ""
42
42
 
43
43
  #--------------------------------------
@@ -594,9 +594,9 @@ class DiscreteBilinearForm:
594
594
  if is_conformal:
595
595
  matrix[k1, k2] = global_mats[k1, k2]
596
596
  elif use_restriction:
597
- matrix.multiplicants[-1][k1, k2] = global_mats[k1, k2]
597
+ matrix.multiplicands[-1][k1, k2] = global_mats[k1, k2]
598
598
  elif use_prolongation:
599
- matrix.multiplicants[0][k1, k2] = global_mats[k1, k2]
599
+ matrix.multiplicands[0][k1, k2] = global_mats[k1, k2]
600
600
 
601
601
  else: # case of scalar equation
602
602
  if is_broken: # multi-patch
@@ -277,7 +277,7 @@ def extract_stencil_mats(mats):
277
277
  if isinstance(M, (StencilInterfaceMatrix, StencilMatrix)):
278
278
  new_mats.append(M)
279
279
  elif isinstance(M, ComposedLinearOperator):
280
- new_mats += [i for i in M.multiplicants if isinstance(i, (StencilInterfaceMatrix, StencilMatrix))]
280
+ new_mats += [i for i in M.multiplicands if isinstance(i, (StencilInterfaceMatrix, StencilMatrix))]
281
281
  return new_mats
282
282
 
283
283
  #==============================================================================
@@ -136,6 +136,21 @@ class DirectionalDerivativeOperator(LinearOperator):
136
136
  def dtype( self ):
137
137
  return self.domain.dtype
138
138
 
139
+ @property
140
+ def diffdir(self) -> int:
141
+ """Direction (axis) of the derivative."""
142
+ return self._diffdir
143
+
144
+ @property
145
+ def negative(self) -> bool:
146
+ """Whether the operator is the negative derivative."""
147
+ return self._negative
148
+
149
+ @property
150
+ def transposed(self) -> bool:
151
+ """Whether the operator is the transposed derivative."""
152
+ return self._transposed
153
+
139
154
  def __truediv__(self, a):
140
155
  """ Divide by scalar. """
141
156
  return self * (1.0 / a)
@@ -8,6 +8,7 @@ provides the fundamental classes for linear algebra operations.
8
8
  """
9
9
 
10
10
  import itertools
11
+ import warnings
11
12
  from abc import ABC, abstractmethod
12
13
  from types import LambdaType
13
14
  from inspect import signature
@@ -93,9 +94,11 @@ class VectorSpace(ABC):
93
94
 
94
95
  Returns
95
96
  -------
96
- float | complex
97
+ float | complex | cupy.ndarray
97
98
  The scalar product of the two vectors. Note that inner(x, x) is
98
99
  a non-negative real number which is zero if and only if x = 0.
100
+ For vectors with device (CuPy) data, a 0-d device array: the
101
+ result stays on the device.
99
102
 
100
103
  """
101
104
 
@@ -1055,27 +1058,27 @@ class ComposedLinearOperator(LinearOperator):
1055
1058
  for i in range(len(args)-1):
1056
1059
  assert args[i].domain == args[i+1].codomain
1057
1060
 
1058
- multiplicants = ()
1061
+ multiplicands = ()
1059
1062
  tmp_vectors = []
1060
1063
  for a in args[:-1]:
1061
1064
  if isinstance(a, ComposedLinearOperator):
1062
- multiplicants = (*multiplicants, *a.multiplicants)
1065
+ multiplicands = (*multiplicands, *a.multiplicands)
1063
1066
  tmp_vectors.extend(a.tmp_vectors)
1064
1067
  tmp_vectors.append(a.domain.zeros())
1065
1068
  else:
1066
- multiplicants = (*multiplicants, a)
1069
+ multiplicands = (*multiplicands, a)
1067
1070
  tmp_vectors.append(a.domain.zeros())
1068
1071
 
1069
1072
  last = args[-1]
1070
1073
  if isinstance(last, ComposedLinearOperator):
1071
- multiplicants = (*multiplicants, *last.multiplicants)
1074
+ multiplicands = (*multiplicands, *last.multiplicands)
1072
1075
  tmp_vectors.extend(last.tmp_vectors)
1073
1076
  else:
1074
- multiplicants = (*multiplicants, last)
1077
+ multiplicands = (*multiplicands, last)
1075
1078
 
1076
1079
  self._domain = domain
1077
1080
  self._codomain = codomain
1078
- self._multiplicants = multiplicants
1081
+ self._multiplicands = multiplicands
1079
1082
  self._tmp_vectors = tuple(tmp_vectors)
1080
1083
 
1081
1084
  @property
@@ -1096,34 +1099,41 @@ class ComposedLinearOperator(LinearOperator):
1096
1099
  return self._codomain
1097
1100
 
1098
1101
  @property
1099
- def multiplicants(self):
1102
+ def multiplicands(self):
1100
1103
  r"""
1101
- A tuple $(A_1,\dots,A_n)$ containing the multiplicants of the linear operator
1104
+ A tuple $(A_1,\dots,A_n)$ containing the multiplicands of the linear operator
1102
1105
  $self = A_n\circ\dots\circ A_1$.
1103
1106
 
1104
1107
  """
1105
- return self._multiplicants
1108
+ return self._multiplicands
1109
+
1110
+ @property
1111
+ def multiplicants(self):
1112
+ """Deprecated alias of ``multiplicands``."""
1113
+ warnings.warn("ComposedLinearOperator.multiplicants is deprecated, use multiplicands instead.",
1114
+ DeprecationWarning, stacklevel=2)
1115
+ return self._multiplicands
1106
1116
 
1107
1117
  @property
1108
1118
  def dtype(self):
1109
1119
  return None
1110
1120
 
1111
1121
  def tosparse(self):
1112
- mats = [M.tosparse() for M in self._multiplicants]
1122
+ mats = [M.tosparse() for M in self._multiplicands]
1113
1123
  M = mats[0]
1114
1124
  for Mi in mats[1:]:
1115
1125
  M = M @ Mi
1116
1126
  return coo_matrix(M)
1117
1127
 
1118
1128
  def transpose(self, conjugate=False):
1119
- t_multiplicants = ()
1120
- for a in self._multiplicants:
1121
- t_multiplicants = (a.transpose(conjugate=conjugate), *t_multiplicants)
1129
+ t_multiplicands = ()
1130
+ for a in self._multiplicands:
1131
+ t_multiplicands = (a.transpose(conjugate=conjugate), *t_multiplicands)
1122
1132
  new_dom = self.codomain
1123
1133
  new_cod = self.domain
1124
1134
  assert isinstance(new_dom, VectorSpace)
1125
1135
  assert isinstance(new_cod, VectorSpace)
1126
- return ComposedLinearOperator(self.codomain, self.domain, *t_multiplicants)
1136
+ return ComposedLinearOperator(self.codomain, self.domain, *t_multiplicands)
1127
1137
 
1128
1138
  def dot(self, v, out=None):
1129
1139
  assert isinstance(v, Vector)
@@ -1135,11 +1145,11 @@ class ComposedLinearOperator(LinearOperator):
1135
1145
  x = v
1136
1146
  for i in range(len(self._tmp_vectors)):
1137
1147
  y = self._tmp_vectors[-1-i]
1138
- A = self._multiplicants[-1-i]
1148
+ A = self._multiplicands[-1-i]
1139
1149
  A.dot(x, out=y)
1140
1150
  x = y
1141
1151
 
1142
- A = self._multiplicants[0]
1152
+ A = self._multiplicands[0]
1143
1153
  if out is not None:
1144
1154
 
1145
1155
  A.dot(x, out=out)
@@ -1148,11 +1158,11 @@ class ComposedLinearOperator(LinearOperator):
1148
1158
  return out
1149
1159
 
1150
1160
  def exchange_assembly_data(self):
1151
- for op in self._multiplicants:
1161
+ for op in self._multiplicands:
1152
1162
  op.exchange_assembly_data()
1153
1163
 
1154
1164
  def set_backend(self, backend, precompiled=False):
1155
- for op in self._multiplicants:
1165
+ for op in self._multiplicands:
1156
1166
  op.set_backend(backend)
1157
1167
 
1158
1168
  #===============================================================================
@@ -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
+ }