diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c49077bb27a2af158624e7222d999973487f0951..449e5e2227c63121d1aeb6c824423c4fbb4e54aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -65,16 +65,21 @@ minimal-windows: - python -c "import numpy" - python setup.py quicktest -minimal-ubuntu: +ubuntu: stage: test except: variables: - $ENABLE_NIGHTLY_BUILDS - image: i10git.cs.fau.de:5005/pycodegen/pycodegen/minimal_ubuntu + image: i10git.cs.fau.de:5005/pycodegen/pycodegen/ubuntu script: - - python3 setup.py quicktest + - mkdir -p ~/.config/matplotlib + - echo "backend:template" > ~/.config/matplotlib/matplotlibrc + - sed -i 's/--doctest-modules //g' pytest.ini + - pytest-3 -v -m "not longrun" tags: - docker + - cuda + - AVX minimal-conda: stage: test diff --git a/conftest.py b/conftest.py index 3bbdefe7c1f3e03ad87d07449d7d7214e18bfeba..272f42303e620cd13fc7f26648bc03d437b1fda6 100644 --- a/conftest.py +++ b/conftest.py @@ -44,7 +44,7 @@ collect_ignore += [os.path.join(SCRIPT_FOLDER, "pystencils/autodiff.py")] try: import pycuda except ImportError: - collect_ignore += [os.path.join(SCRIPT_FOLDER, "pystencils/pystencils_tests/test_cudagpu.py")] + collect_ignore += [os.path.join(SCRIPT_FOLDER, "pystencils_tests/test_cudagpu.py")] add_path_to_ignore('pystencils/gpucuda') try: @@ -73,7 +73,22 @@ try: import blitzdb except ImportError: add_path_to_ignore('pystencils/runhelper') + collect_ignore += [os.path.join(SCRIPT_FOLDER, "pystencils_tests/test_parameterstudy.py")] +try: + import islpy +except ImportError: + collect_ignore += [os.path.join(SCRIPT_FOLDER, "pystencils/integer_set_analysis.py")] + +try: + import graphviz +except ImportError: + collect_ignore += [os.path.join(SCRIPT_FOLDER, "pystencils/backends/dot.py")] + +try: + import pyevtk +except ImportError: + collect_ignore += [os.path.join(SCRIPT_FOLDER, "pystencils/datahandling/vtk.py")] collect_ignore += [os.path.join(SCRIPT_FOLDER, 'setup.py')] @@ -129,7 +144,7 @@ class IPyNbFile(pytest.File): exporter.exclude_markdown = True exporter.exclude_input_prompt = True - notebook_contents = self.fspath.open() + notebook_contents = self.fspath.open(encoding='utf-8') with warnings.catch_warnings(): warnings.filterwarnings("ignore", "IPython.core.inputsplitter is deprecated") diff --git a/doc/notebooks/01_tutorial_getting_started.ipynb b/doc/notebooks/01_tutorial_getting_started.ipynb index 97ff73da1cf3bb63e19fa6fac122499120d71cee..70b3c2a8a3e86d81fb8972ad50ffbdd0ad4bcb89 100644 --- a/doc/notebooks/01_tutorial_getting_started.ipynb +++ b/doc/notebooks/01_tutorial_getting_started.ipynb @@ -932,14 +932,18 @@ } ], "source": [ - "import requests\n", - "import imageio\n", - "from io import BytesIO\n", + "try:\n", + " import requests\n", + " import imageio\n", + " from io import BytesIO\n", "\n", - "response = requests.get(\"https://www.python.org/static/img/python-logo.png\")\n", - "img = imageio.imread(BytesIO(response.content)).astype(np.double)\n", - "img /= img.max()\n", - "plt.imshow(img);" + " response = requests.get(\"https://www.python.org/static/img/python-logo.png\")\n", + " img = imageio.imread(BytesIO(response.content)).astype(np.double)\n", + " img /= img.max()\n", + " plt.imshow(img);\n", + "except ImportError:\n", + " print(\"No requests installed\")\n", + " img = np.random.random((82, 290, 4))" ] }, { diff --git a/doc/notebooks/demo_plotting_and_animation.ipynb b/doc/notebooks/demo_plotting_and_animation.ipynb index 07c980788f7617b1bfb8bfad8a13f2f2d7b5c33d..13d658a9cb4980a78cc984caeeb78e220b543553 100644 --- a/doc/notebooks/demo_plotting_and_animation.ipynb +++ b/doc/notebooks/demo_plotting_and_animation.ipynb @@ -6,7 +6,9 @@ "metadata": {}, "outputs": [], "source": [ - "from pystencils.session import *" + "from pystencils.session import *\n", + "\n", + "import shutil" ] }, { @@ -408,9 +410,12 @@ } ], "source": [ - "plt.figure()\n", - "animation = plt.scalar_field_animation(run_func, frames=60)\n", - "ps.jupyter.display_as_html_video(animation)" + "if shutil.which(\"ffmpeg\") is not None:\n", + " plt.figure()\n", + " animation = plt.scalar_field_animation(run_func, frames=60)\n", + " ps.jupyter.display_as_html_video(animation)\n", + "else:\n", + " print(\"No ffmpeg installed\")" ] }, { @@ -474,8 +479,11 @@ } ], "source": [ - "animation = plt.surface_plot_animation(run_func, frames=60)\n", - "ps.jupyter.display_as_html_video(animation)" + "if shutil.which(\"ffmpeg\") is not None:\n", + " animation = plt.surface_plot_animation(run_func, frames=60)\n", + " ps.jupyter.display_as_html_video(animation)\n", + "else:\n", + " print(\"No ffmpeg installed\")" ] }, { @@ -636,9 +644,12 @@ } ], "source": [ - "plt.figure()\n", - "animation = plt.vector_field_animation(run_func, frames=60)\n", - "ps.jupyter.display_as_html_video(animation)" + "if shutil.which(\"ffmpeg\") is not None:\n", + " plt.figure()\n", + " animation = plt.vector_field_animation(run_func, frames=60)\n", + " ps.jupyter.display_as_html_video(animation)\n", + "else:\n", + " print(\"No ffmpeg installed\")" ] }, { @@ -671,8 +682,11 @@ } ], "source": [ - "animation = plt.vector_field_magnitude_animation(run_func, frames=60)\n", - "ps.jupyter.display_as_html_video(animation)" + "if shutil.which(\"ffmpeg\") is not None:\n", + " animation = plt.vector_field_magnitude_animation(run_func, frames=60)\n", + " ps.jupyter.display_as_html_video(animation)\n", + "else:\n", + " print(\"No ffmpeg installed\")" ] }, { diff --git a/doc/notebooks/demo_wave_equation.ipynb b/doc/notebooks/demo_wave_equation.ipynb index a4d5a9ff7c1cf5df33b83d3d48c3272f214a4d39..2aa083d96b33e6b2c69bee0783e1c55b4ab616fd 100644 --- a/doc/notebooks/demo_wave_equation.ipynb +++ b/doc/notebooks/demo_wave_equation.ipynb @@ -8,7 +8,9 @@ }, "outputs": [], "source": [ - "from pystencils.session import *" + "from pystencils.session import *\n", + "\n", + "import shutil" ] }, { @@ -397,8 +399,11 @@ } ], "source": [ - "ani = plt.surface_plot_animation(run, zlim=(-1, 1))\n", - "ps.jupyter.display_as_html_video(ani)" + "if shutil.which(\"ffmpeg\") is not None:\n", + " ani = plt.surface_plot_animation(run, zlim=(-1, 1))\n", + " ps.jupyter.display_as_html_video(ani)\n", + "else:\n", + " print(\"No ffmpeg installed\")" ] }, { @@ -466,9 +471,12 @@ " u_arrays[0], u_arrays[1], u_arrays[2] = u_arrays[1], u_arrays[2], u_arrays[0]\n", " return u_arrays[2]\n", " \n", - " ani = plt.surface_plot_animation(run_LLVM, zlim=(-1, 1))\n", " assert np.isfinite(np.max(u_arrays[2]))\n", - "ps.jupyter.display_as_html_video(ani)" + " if shutil.which(\"ffmpeg\") is not None:\n", + " ani = plt.surface_plot_animation(run_LLVM, zlim=(-1, 1))\n", + " ps.jupyter.display_as_html_video(ani)\n", + " else:\n", + " print(\"No ffmpeg installed\")" ] }, { diff --git a/pystencils/astnodes.py b/pystencils/astnodes.py index 062d3687f582423bd38b8a1c2e9dae3ff95a6bfb..8ec80917af20aefc5883c68ac51f360005b4350b 100644 --- a/pystencils/astnodes.py +++ b/pystencils/astnodes.py @@ -291,7 +291,10 @@ class Block(Node): self._nodes = nodes self.parent = None for n in self._nodes: - n.parent = self + try: + n.parent = self + except AttributeError: + pass @property def args(self): diff --git a/pystencils/backends/cbackend.py b/pystencils/backends/cbackend.py index 3f016810a9a673035adf1130039bc95e5d6ab70d..ad339a4bdc2a2a5d696c964d602fbc6d01fcfaa9 100644 --- a/pystencils/backends/cbackend.py +++ b/pystencils/backends/cbackend.py @@ -387,6 +387,13 @@ class CustomSympyPrinter(CCodePrinter): return self._print(expr.args[0]) elif isinstance(expr, fast_inv_sqrt): return "({})".format(self._print(1 / sp.sqrt(expr.args[0]))) + elif isinstance(expr, sp.Abs): + return "abs({})".format(self._print(expr.args[0])) + elif isinstance(expr, sp.Mod): + if expr.args[0].is_integer and expr.args[1].is_integer: + return "({} % {})".format(self._print(expr.args[0]), self._print(expr.args[1])) + else: + return "fmod({}, {})".format(self._print(expr.args[0]), self._print(expr.args[1])) elif expr.func in infix_functions: return "(%s %s %s)" % (self._print(expr.args[0]), infix_functions[expr.func], self._print(expr.args[1])) elif expr.func == int_power_of_2: diff --git a/pystencils/cpu/cpujit.py b/pystencils/cpu/cpujit.py index 6376ffb85bff4dca347e0dde7044a1d92ad7bab7..07a8a84d9c6d10ed8580a7ef83d6720b522cfd98 100644 --- a/pystencils/cpu/cpujit.py +++ b/pystencils/cpu/cpujit.py @@ -270,7 +270,8 @@ if( PyErr_Occurred() ) {{ return NULL; }} template_extract_complex = """ PyObject * obj_{name} = PyDict_GetItemString(kwargs, "{name}"); if( obj_{name} == NULL) {{ PyErr_SetString(PyExc_TypeError, "Keyword argument '{name}' missing"); return NULL; }}; -{target_type} {name}{{ {extract_function_real}( obj_{name} ), {extract_function_imag}( obj_{name} ) }}; +{target_type} {name}{{ ({real_type}) {extract_function_real}( obj_{name} ), + ({real_type}) {extract_function_imag}( obj_{name} ) }}; if( PyErr_Occurred() ) {{ return NULL; }} """ @@ -409,6 +410,8 @@ def create_function_boilerplate_code(parameter_info, name, insert_checks=True): pre_call_code += template_extract_complex.format(extract_function_real=extract_function[0], extract_function_imag=extract_function[1], target_type=target_type, + real_type="float" if target_type == "ComplexFloat" + else "double", name=param.symbol.name) else: pre_call_code += template_extract_scalar.format(extract_function=extract_function, diff --git a/pystencils/fd/derivative.py b/pystencils/fd/derivative.py index 02d8f74ee912ecbc59b80db0e757e27a26d4686e..31daf4294f4ffa70c766632c4126dd7403690251 100644 --- a/pystencils/fd/derivative.py +++ b/pystencils/fd/derivative.py @@ -316,7 +316,8 @@ def expand_diff_full(expr, functions=None, constants=None): functions.difference_update(constants) def visit(e): - e = e.expand() + if not isinstance(e, sp.Tuple): + e = e.expand() if e.func == Diff: result = 0 @@ -341,6 +342,9 @@ def expand_diff_full(expr, functions=None, constants=None): return result elif isinstance(e, sp.Piecewise): return sp.Piecewise(*((expand_diff_full(a, functions, constants), b) for a, b in e.args)) + elif isinstance(expr, sp.Tuple): + new_args = [visit(arg) for arg in e.args] + return sp.Tuple(*new_args) else: new_args = [visit(arg) for arg in e.args] return e.func(*new_args) if new_args else e @@ -380,6 +384,9 @@ def expand_diff_linear(expr, functions=None, constants=None): return diff.split_linear(functions) elif isinstance(expr, sp.Piecewise): return sp.Piecewise(*((expand_diff_linear(a, functions, constants), b) for a, b in expr.args)) + elif isinstance(expr, sp.Tuple): + new_args = [expand_diff_linear(e, functions) for e in expr.args] + return sp.Tuple(*new_args) else: new_args = [expand_diff_linear(e, functions) for e in expr.args] result = sp.expand(expr.func(*new_args) if new_args else expr) diff --git a/pystencils/include/cuda_complex.hpp b/pystencils/include/cuda_complex.hpp index ad555264a87881d8eaee6b2476c482039d606f71..535aa52e30781a03e170298e8e5ebbed872585dc 100644 --- a/pystencils/include/cuda_complex.hpp +++ b/pystencils/include/cuda_complex.hpp @@ -1173,53 +1173,53 @@ operator<<(std::basic_ostream<_CharT, _Traits> &__os, const complex<_Tp> &__x) { template <class U, class V> CUDA_CALLABLE_MEMBER auto operator*(const complex<U> &complexNumber, const V &scalar) -> complex<U> { - return complex<U>{real(complexNumber) * scalar, imag(complexNumber) * scalar}; + return complex<U>(real(complexNumber) * scalar, imag(complexNumber) * scalar); } template <class U, class V> CUDA_CALLABLE_MEMBER auto operator*(const V &scalar, const complex<U> &complexNumber) -> complex<U> { - return complex<U>{real(complexNumber) * scalar, imag(complexNumber) * scalar}; + return complex<U>(real(complexNumber) * scalar, imag(complexNumber) * scalar); } template <class U, class V> CUDA_CALLABLE_MEMBER auto operator+(const complex<U> &complexNumber, const V &scalar) -> complex<U> { - return complex<U>{real(complexNumber) + scalar, imag(complexNumber)}; + return complex<U>(real(complexNumber) + scalar, imag(complexNumber)); } template <class U, class V> CUDA_CALLABLE_MEMBER auto operator+(const V &scalar, const complex<U> &complexNumber) -> complex<U> { - return complex<U>{real(complexNumber) + scalar, imag(complexNumber)}; + return complex<U>(real(complexNumber) + scalar, imag(complexNumber)); } template <class U, class V> CUDA_CALLABLE_MEMBER auto operator-(const complex<U> &complexNumber, const V &scalar) -> complex<U> { - return complex<U>{real(complexNumber) - scalar, imag(complexNumber)}; + return complex<U>(real(complexNumber) - scalar, imag(complexNumber)); } template <class U, class V> CUDA_CALLABLE_MEMBER auto operator-(const V &scalar, const complex<U> &complexNumber) -> complex<U> { - return complex<U>{scalar - real(complexNumber), imag(complexNumber)}; + return complex<U>(scalar - real(complexNumber), imag(complexNumber)); } template <class U, class V> CUDA_CALLABLE_MEMBER auto operator/(const complex<U> &complexNumber, const V scalar) -> complex<U> { - return complex<U>{real(complexNumber) / scalar, imag(complexNumber) / scalar}; + return complex<U>(real(complexNumber) / scalar, imag(complexNumber) / scalar); } template <class U, class V> CUDA_CALLABLE_MEMBER auto operator/(const V scalar, const complex<U> &complexNumber) -> complex<U> { - return complex<U>{scalar, 0} / complexNumber; + return complex<U>(scalar, 0) / complexNumber; } using ComplexDouble = complex<double>; diff --git a/pystencils/kernelcreation.py b/pystencils/kernelcreation.py index aaca6d3124c4303ae289e831fdb7b7680369c408..2c8aabaa530f985f7b645a41a29a9537a655fa07 100644 --- a/pystencils/kernelcreation.py +++ b/pystencils/kernelcreation.py @@ -79,14 +79,14 @@ def create_kernel(assignments, [0., 0., 0., 0., 0.]]) """ # ---- Normalizing parameters + if isinstance(assignments, Assignment): + assignments = [assignments] assert assignments, "Assignments must not be empty!" split_groups = () if isinstance(assignments, AssignmentCollection): if 'split_groups' in assignments.simplification_hints: split_groups = assignments.simplification_hints['split_groups'] assignments = assignments.all_assignments - if isinstance(assignments, Assignment): - assignments = [assignments] # ---- Creating ast if target == 'cpu': diff --git a/pystencils_tests/test_boundary.py b/pystencils_tests/test_boundary.py index 096b1348fc59181e14eb15042d8db0098e71c521..23770c8ef6d61c15110f94875b0000c3e7d11fac 100644 --- a/pystencils_tests/test_boundary.py +++ b/pystencils_tests/test_boundary.py @@ -3,6 +3,8 @@ from tempfile import TemporaryDirectory import numpy as np +import pytest + from pystencils import Assignment, create_kernel from pystencils.boundaries import BoundaryHandling, Neumann, add_neumann_boundary from pystencils.datahandling import SerialDataHandling @@ -83,5 +85,6 @@ def test_kernel_vs_copy_boundary(): np.testing.assert_almost_equal(python_copy_result, handling_result) with TemporaryDirectory() as tmp_dir: + pytest.importorskip('pyevtk') boundary_handling.geometry_to_vtk(file_name=os.path.join(tmp_dir, 'test_output1'), ghost_layers=False) boundary_handling.geometry_to_vtk(file_name=os.path.join(tmp_dir, 'test_output2'), ghost_layers=True) diff --git a/pystencils_tests/test_buffer_gpu.py b/pystencils_tests/test_buffer_gpu.py index fc27a2331be1690b8bb1d0da043b47b3ba6fcfc9..f9ee96e9b19c147d95f006de4221980900375cb1 100644 --- a/pystencils_tests/test_buffer_gpu.py +++ b/pystencils_tests/test_buffer_gpu.py @@ -22,6 +22,7 @@ FIELD_SIZES = [(4, 3), (9, 3, 7)] def _generate_fields(dt=np.uint8, stencil_directions=1, layout='numpy'): + pytest.importorskip('pycuda') field_sizes = FIELD_SIZES if stencil_directions > 1: field_sizes = [s + (stencil_directions,) for s in field_sizes] @@ -44,7 +45,6 @@ def _generate_fields(dt=np.uint8, stencil_directions=1, layout='numpy'): return fields -@pytest.mark.gpu def test_full_scalar_field(): """Tests fully (un)packing a scalar field (from)to a GPU buffer.""" fields = _generate_fields() @@ -73,7 +73,6 @@ def test_full_scalar_field(): np.testing.assert_equal(src_arr, dst_arr) -@pytest.mark.gpu def test_field_slice(): """Tests (un)packing slices of a scalar field (from)to a buffer.""" fields = _generate_fields() @@ -109,7 +108,6 @@ def test_field_slice(): np.testing.assert_equal(src_arr[pack_slice], dst_arr[unpack_slice]) -@pytest.mark.gpu def test_all_cell_values(): """Tests (un)packing all cell values of the a field (from)to a buffer.""" num_cell_values = 7 @@ -148,7 +146,6 @@ def test_all_cell_values(): np.testing.assert_equal(src_arr, dst_arr) -@pytest.mark.gpu def test_subset_cell_values(): """Tests (un)packing a subset of cell values of the a field (from)to a buffer.""" num_cell_values = 7 @@ -190,7 +187,6 @@ def test_subset_cell_values(): np.testing.assert_equal(dst_arr, mask_arr.filled(int(0))) -@pytest.mark.gpu def test_field_layouts(): num_cell_values = 7 for layout_str in ['numpy', 'fzyx', 'zyxf', 'reverse_numpy']: diff --git a/pystencils_tests/test_complex_numbers.py b/pystencils_tests/test_complex_numbers.py index 2fb558cbc883ca89f81667ea3d03ffd46eafc3bf..41dc76a40411129c4ee68f0ee42028b57875d71e 100644 --- a/pystencils_tests/test_complex_numbers.py +++ b/pystencils_tests/test_complex_numbers.py @@ -57,6 +57,9 @@ def test_complex_numbers(assignment, scalar_dtypes, target): print(code) assert "Not supported" not in code + if target == 'gpu': + pytest.importorskip('pycuda') + kernel = ast.compile() assert kernel is not None @@ -100,6 +103,9 @@ def test_complex_numbers_64(assignment, target): print(code) assert "Not supported" not in code + if target == 'gpu': + pytest.importorskip('pycuda') + kernel = ast.compile() assert kernel is not None @@ -125,6 +131,7 @@ def test_complex_execution(dtype, target, with_complex_argument): }) if target == 'gpu': + pytest.importorskip('pycuda') from pycuda.gpuarray import zeros x_arr = zeros((20, 30), complex_dtype) y_arr = zeros((20, 30), complex_dtype) diff --git a/pystencils_tests/test_cuda_known_functions.py b/pystencils_tests/test_cuda_known_functions.py index c610d9e8517f25352a1853448f35889da16f1c23..dcdabeaef6934e06b289351127b168d77d803be9 100644 --- a/pystencils_tests/test_cuda_known_functions.py +++ b/pystencils_tests/test_cuda_known_functions.py @@ -1,5 +1,7 @@ import sympy +import pytest + import pystencils from pystencils.astnodes import get_dummy_symbol from pystencils.backends.cuda_backend import CudaSympyPrinter @@ -18,6 +20,7 @@ def test_cuda_known_functions(): }) ast = pystencils.create_kernel(assignments, 'gpu') + pytest.importorskip('pycuda') pystencils.show_code(ast) kernel = ast.compile() assert(kernel is not None) diff --git a/pystencils_tests/test_custom_backends.py b/pystencils_tests/test_custom_backends.py index 6b0fc6fd1307426bd2b2a88549570f8e2fca2a07..6500076ce278bff4fc2a81f65c55553bd9523d78 100644 --- a/pystencils_tests/test_custom_backends.py +++ b/pystencils_tests/test_custom_backends.py @@ -25,7 +25,7 @@ class ScreamingGpuBackend(CudaBackend): return normal_code.upper() -def test_custom_backends(): +def test_custom_backends_cpu(): z, x, y = pystencils.fields("z, y, x: [2d]") normal_assignments = pystencils.AssignmentCollection([pystencils.Assignment( @@ -36,6 +36,16 @@ def test_custom_backends(): with pytest.raises(CalledProcessError): pystencils.cpu.cpujit.make_python_function(ast, custom_backend=ScreamingBackend()) + +def test_custom_backends_gpu(): + pytest.importorskip('pycuda') + import pycuda.driver + + z, x, y = pystencils.fields("z, y, x: [2d]") + + normal_assignments = pystencils.AssignmentCollection([pystencils.Assignment( + z[0, 0], x[0, 0] * sympy.log(x[0, 0] * y[0, 0]))], []) + ast = pystencils.create_kernel(normal_assignments, target='gpu') pystencils.show_code(ast, ScreamingGpuBackend()) with pytest.raises(pycuda.driver.CompileError): diff --git a/pystencils_tests/test_datahandling.py b/pystencils_tests/test_datahandling.py index 1b79fdbec5aa8bbbfb90450d2014d058065e998b..7f95fe1f04d1bbf72770d2837bf6723681c1f50d 100644 --- a/pystencils_tests/test_datahandling.py +++ b/pystencils_tests/test_datahandling.py @@ -128,6 +128,7 @@ def kernel_execution_jacobi(dh, target): def vtk_output(dh): + pytest.importorskip('pyevtk') dh.add_array('scalar_field') dh.add_array('vector_field', values_per_cell=dh.dim) dh.add_array('multiple_scalar_field', values_per_cell=9) @@ -223,6 +224,7 @@ def test_kernel_param(target): def test_vtk_output(): + pytest.importorskip('pyevtk') for domain_shape in [(4, 5), (3, 4, 5)]: dh = create_data_handling(domain_size=domain_shape, periodicity=True) vtk_output(dh) diff --git a/pystencils_tests/test_field.py b/pystencils_tests/test_field.py index a2813e34f9c72c8bbcd848d21993c181cf31fc43..253ca9f26547d78e6b23b2c6dbc582196b49960d 100644 --- a/pystencils_tests/test_field.py +++ b/pystencils_tests/test_field.py @@ -44,7 +44,7 @@ def test_error_handling(): Field.create_generic('f', spatial_dimensions=2, index_dimensions=1, dtype=struct_dtype) assert 'index dimension' in str(e.value) - arr = np.array([[1, 2.0, 3], [1, 2.0, 3]], dtype=struct_dtype) + arr = np.array([[[(1,)*3, (2,)*3, (3,)*3]]*2], dtype=struct_dtype) Field.create_from_numpy_array('f', arr, index_dimensions=0) with pytest.raises(ValueError) as e: Field.create_from_numpy_array('f', arr, index_dimensions=1) diff --git a/pystencils_tests/test_interpolation.py b/pystencils_tests/test_interpolation.py index b35427f51619ecad198a7a512c2744204c3c43dd..477765bb31289dbfdc48927e3cc55f10d49f16a0 100644 --- a/pystencils_tests/test_interpolation.py +++ b/pystencils_tests/test_interpolation.py @@ -14,8 +14,6 @@ import numpy as np import pytest import sympy -import pycuda.autoinit # NOQA -import pycuda.gpuarray as gpuarray import pystencils from pystencils.interpolation_astnodes import LinearInterpolator from pystencils.spatial_coordinates import x_, y_ @@ -51,7 +49,7 @@ def test_interpolation(): print(assignments) ast = pystencils.create_kernel(assignments) print(ast) - pystencils.show_code(ast) + print(pystencils.show_code(ast)) kernel = ast.compile() pyconrad.imshow(lenna) @@ -71,7 +69,7 @@ def test_scale_interpolation(): print(assignments) ast = pystencils.create_kernel(assignments) print(ast) - pystencils.show_code(ast) + print(pystencils.show_code(ast)) kernel = ast.compile() out = np.zeros_like(lenna) @@ -83,9 +81,9 @@ def test_scale_interpolation(): ['border', 'clamp', pytest.param('warp', marks=pytest.mark.xfail( - reason="Fails on newer SymPy version due to complex conjugate()")), + reason="requires interpolation-refactoring branch")), pytest.param('mirror', marks=pytest.mark.xfail( - reason="Fails on newer SymPy version due to complex conjugate()")), + reason="requires interpolation-refactoring branch")), ]) def test_rotate_interpolation(address_mode): """ @@ -102,7 +100,7 @@ def test_rotate_interpolation(address_mode): print(assignments) ast = pystencils.create_kernel(assignments) print(ast) - pystencils.show_code(ast) + print(pystencils.show_code(ast)) kernel = ast.compile() out = np.zeros_like(lenna) @@ -114,7 +112,10 @@ def test_rotate_interpolation(address_mode): @pytest.mark.parametrize('address_mode', ('border', 'wrap', 'clamp', 'mirror')) @pytest.mark.parametrize('use_textures', ('use_textures', False)) def test_rotate_interpolation_gpu(dtype, address_mode, use_textures): + pytest.importorskip('pycuda') + import pycuda.gpuarray as gpuarray + import pycuda.autoinit # noqa rotation_angle = sympy.pi / 5 scale = 1 @@ -141,49 +142,56 @@ def test_rotate_interpolation_gpu(dtype, address_mode, use_textures): kernel(x=lenna_gpu, y=out) pyconrad.imshow(out, f"out {address_mode} texture:{use_textures} {type_map[dtype]}") - skimage.io.imsave(f"/tmp/out {address_mode} texture:{use_textures} {type_map[dtype]}.tif", - np.ascontiguousarray(out.get(), np.float32)) -@pytest.mark.parametrize('address_mode', ['border', 'wrap', 'clamp', 'mirror']) +@pytest.mark.parametrize('address_mode', ['border', 'wrap', + pytest.param('warp', marks=pytest.mark.xfail( + reason="% printed as fmod on old sympy")), + pytest.param('mirror', marks=pytest.mark.xfail( + reason="% printed as fmod on old sympy")), + ]) @pytest.mark.parametrize('dtype', [np.float64, np.float32, np.int32]) @pytest.mark.parametrize('use_textures', ('use_textures', False,)) def test_shift_interpolation_gpu(address_mode, dtype, use_textures): + sver = sympy.__version__.split(".") + if (int(sver[0]) == 1 and int(sver[1]) < 2) and address_mode in ['mirror', 'warp']: + pytest.skip() + pytest.importorskip('pycuda') + + import pycuda.gpuarray as gpuarray + import pycuda.autoinit # noqa rotation_angle = 0 # sympy.pi / 5 scale = 1 # shift = - sympy.Matrix([1.5, 1.5]) shift = sympy.Matrix((0.0, 0.0)) - for dtype in [np.float64, np.float32, np.int32]: - if dtype == np.int32: - lenna_gpu = gpuarray.to_gpu( - np.ascontiguousarray(lenna * 255, dtype)) - else: - lenna_gpu = gpuarray.to_gpu( - np.ascontiguousarray(lenna, dtype)) - for use_textures in [True, False]: - x_f, y_f = pystencils.fields('x,y: %s [2d]' % type_map[dtype], ghost_layers=0) - - if use_textures: - transformed = scale * sympy.rot_axis3(rotation_angle)[:2, :2] * sympy.Matrix((x_, y_)) + shift - else: - transformed = scale * sympy.rot_axis3(rotation_angle)[:2, :2] * sympy.Matrix((x_, y_)) + shift - assignments = pystencils.AssignmentCollection({ - y_f.center(): LinearInterpolator(x_f, address_mode=address_mode).at(transformed) - }) - # print(assignments) - ast = pystencils.create_kernel(assignments, target='gpu', use_textures_for_interpolation=use_textures) - # print(ast) - pystencils.show_code(ast) - kernel = ast.compile() - - out = gpuarray.zeros_like(lenna_gpu) - kernel(x=lenna_gpu, y=out) - pyconrad.imshow(out, - f"out {address_mode} texture:{use_textures} {type_map[dtype]}") - skimage.io.imsave(f"/tmp/out {address_mode} texture:{use_textures} {type_map[dtype]}.tif", - np.ascontiguousarray(out.get(), np.float32)) + if dtype == np.int32: + lenna_gpu = gpuarray.to_gpu( + np.ascontiguousarray(lenna * 255, dtype)) + else: + lenna_gpu = gpuarray.to_gpu( + np.ascontiguousarray(lenna, dtype)) + + x_f, y_f = pystencils.fields('x,y: %s [2d]' % type_map[dtype], ghost_layers=0) + + if use_textures: + transformed = scale * sympy.rot_axis3(rotation_angle)[:2, :2] * sympy.Matrix((x_, y_)) + shift + else: + transformed = scale * sympy.rot_axis3(rotation_angle)[:2, :2] * sympy.Matrix((x_, y_)) + shift + assignments = pystencils.AssignmentCollection({ + y_f.center(): LinearInterpolator(x_f, address_mode=address_mode).at(transformed) + }) + # print(assignments) + ast = pystencils.create_kernel(assignments, target='gpu', use_textures_for_interpolation=use_textures) + # print(ast) + print(pystencils.show_code(ast)) + kernel = ast.compile() + + out = gpuarray.zeros_like(lenna_gpu) + kernel(x=lenna_gpu, y=out) + pyconrad.imshow(out, + f"out {address_mode} texture:{use_textures} {type_map[dtype]}") @pytest.mark.parametrize('address_mode', ['border', 'clamp']) @@ -202,7 +210,7 @@ def test_rotate_interpolation_size_change(address_mode): print(assignments) ast = pystencils.create_kernel(assignments) print(ast) - pystencils.show_code(ast) + print(pystencils.show_code(ast)) kernel = ast.compile() out = np.zeros((100, 150), np.float64) @@ -221,7 +229,7 @@ def test_field_interpolated(address_mode, target): print(assignments) ast = pystencils.create_kernel(assignments, target=target) print(ast) - pystencils.show_code(ast) + print(pystencils.show_code(ast)) kernel = ast.compile() out = np.zeros_like(lenna) diff --git a/pystencils_tests/test_loop_cutting.py b/pystencils_tests/test_loop_cutting.py index cd89f37f6f365b4223e1463db68874f50e81c46d..0291074b87c051bcf381883e474078c663274ac1 100644 --- a/pystencils_tests/test_loop_cutting.py +++ b/pystencils_tests/test_loop_cutting.py @@ -1,6 +1,8 @@ import numpy as np import sympy as sp +import pytest + import pystencils as ps import pystencils.astnodes as ast from pystencils.field import Field, FieldType @@ -57,6 +59,7 @@ def test_staggered_iteration(): sum(f[o] for o in offsets_in_plane(d, -1, dim))) assignments = [ps.Assignment(s.staggered_access(d), expressions[i]) for i, d in enumerate(s.staggered_stencil)] func_optimized = create_staggered_kernel(assignments).compile() + pytest.importorskip('islpy') assert not func_optimized.ast.atoms(Conditional), "Loop cutting optimization did not work" func(f=f_arr, s=s_arr_ref) @@ -99,6 +102,7 @@ def test_staggered_iteration_manual(): move_constants_before_loop(kernel_ast.body) cleanup_blocks(kernel_ast.body) + pytest.importorskip('islpy') assert not kernel_ast.atoms(Conditional), "Loop cutting optimization did not work" func_optimized = make_python_function(kernel_ast) diff --git a/pystencils_tests/test_opencl.py b/pystencils_tests/test_opencl.py index ac9aaad9f8584fead6fa4ea198cd6c7419226139..6c013cefed7387827888385feeb11352b96cc8e6 100644 --- a/pystencils_tests/test_opencl.py +++ b/pystencils_tests/test_opencl.py @@ -246,11 +246,12 @@ def test_kernel_creation(): print(assignments) + import pystencils.opencl.autoinit ast = pystencils.create_kernel(assignments, target='opencl') print(ast.backend) - code = str(pystencils.show_code(ast)) + code = pystencils.get_code_str(ast) print(code) assert 'get_local_size' in code diff --git a/pystencils_tests/test_phasefield_dentritic_3D.ipynb b/pystencils_tests/test_phasefield_dentritic_3D.ipynb index aebdeefdddf53e9736feb7d8673c09bc5d8fa6d0..1a01f103891fb229385d8192f263fb53dd837a78 100644 --- a/pystencils_tests/test_phasefield_dentritic_3D.ipynb +++ b/pystencils_tests/test_phasefield_dentritic_3D.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pytest\n", + "pytest.importorskip('pycuda')" + ] + }, { "cell_type": "code", "execution_count": 1, diff --git a/pystencils_tests/test_plot.py b/pystencils_tests/test_plot.py index 6234334bb91352c80c643c474c76c15b968ce1c7..4c9207216266e48d4f2b0b14db1a5e160732c5cb 100644 --- a/pystencils_tests/test_plot.py +++ b/pystencils_tests/test_plot.py @@ -1,5 +1,8 @@ import os from tempfile import TemporaryDirectory +import shutil + +import pytest import numpy as np @@ -20,6 +23,7 @@ def example_vector_field(t=0, shape=(40, 40)): return result +@pytest.mark.skipif(shutil.which('ffmpeg') is None, reason="ffmpeg not available") def test_animation(): t = 0 diff --git a/pystencils_tests/test_print_infinity.py b/pystencils_tests/test_print_infinity.py index 4b1e488822163e684dcad4009fca1b72c7d87d2c..9e2dbd29b18389265b206513ec6c4625e0722a10 100644 --- a/pystencils_tests/test_print_infinity.py +++ b/pystencils_tests/test_print_infinity.py @@ -17,6 +17,9 @@ def test_print_infinity(type, negative, target): assignment = pystencils.Assignment(x.center, oo) ast = pystencils.create_kernel(assignment, data_type=type, target=target) + if target == 'gpu': + pytest.importorskip('pycuda') + ast.compile() print(ast.compile().code) diff --git a/pystencils_tests/test_random.py b/pystencils_tests/test_random.py index 473aa3d1215449f51e70aa3b09f2c605ed13c313..1b3f89f2f81aa6e079f7bc030f27da74e778dd13 100644 --- a/pystencils_tests/test_random.py +++ b/pystencils_tests/test_random.py @@ -1,5 +1,7 @@ import numpy as np +import pytest + import pystencils as ps from pystencils.rng import PhiloxFourFloats, PhiloxTwoDoubles, AESNIFourFloats, AESNITwoDoubles @@ -12,6 +14,9 @@ philox_reference = np.array([[[3576608082, 1252663339, 1987745383, 348040302], def test_philox_double(): for target in ('cpu', 'gpu'): + if target == 'gpu': + pytest.importorskip('pycuda') + dh = ps.create_data_handling((2, 2), default_ghost_layers=0, default_target=target) f = dh.add_array("f", values_per_cell=2) @@ -39,6 +44,9 @@ def test_philox_double(): def test_philox_float(): for target in ('cpu', 'gpu'): + if target == 'gpu': + pytest.importorskip('pycuda') + dh = ps.create_data_handling((2, 2), default_ghost_layers=0, default_target=target) f = dh.add_array("f", values_per_cell=4) diff --git a/pystencils_tests/test_staggered_kernel.py b/pystencils_tests/test_staggered_kernel.py index 310220d21442dc1d1a24b0e74e50f3e068374b67..c4f491393d2b4d08fbb9b4c99356abdc7e7199b6 100644 --- a/pystencils_tests/test_staggered_kernel.py +++ b/pystencils_tests/test_staggered_kernel.py @@ -1,6 +1,8 @@ import numpy as np import sympy as sp +import pytest + import pystencils as ps @@ -88,6 +90,7 @@ def test_staggered_subexpressions(): def test_staggered_loop_cutting(): + pytest.importorskip('islpy') dh = ps.create_data_handling((4, 4), periodicity=True, default_target='cpu') j = dh.add_array('j', values_per_cell=4, field_type=ps.FieldType.STAGGERED) assignments = [ps.Assignment(j.staggered_access("SW"), 1)] diff --git a/pytest.ini b/pytest.ini index 4e9f5caddfef20639da974ad9327e53fa8b6ced4..0070425966626449378fcd1ff9eabe468256a112 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,7 +3,6 @@ python_files = test_*.py *_test.py scenario_*.py norecursedirs = *.egg-info .git .cache .ipynb_checkpoints htmlcov addopts = --doctest-modules --durations=20 --cov-config pytest.ini markers = - gpu: test that require a gpu kerncraft: tests depending on kerncraft [run]