From 6a5b3fef13890acd1a9413a838c371be344c3e4e Mon Sep 17 00:00:00 2001
From: Stephan Seitz <stephan.seitz@fau.de>
Date: Wed, 26 Feb 2020 13:58:23 +0100
Subject: [PATCH] Try to make module independent from pystencils

---
 src/pyronn_torch/__init__.py |   2 +-
 src/pyronn_torch/codegen.py  | 316 ++++++++++++++++++++---------------
 tests/test_projection.py     |   5 +-
 3 files changed, 189 insertions(+), 134 deletions(-)

diff --git a/src/pyronn_torch/__init__.py b/src/pyronn_torch/__init__.py
index 57356e9..9e1ab5a 100644
--- a/src/pyronn_torch/__init__.py
+++ b/src/pyronn_torch/__init__.py
@@ -24,6 +24,6 @@ except Exception as e:
     import warnings
     warnings.warn(str(e))
     import pyronn_torch.codegen
-    cpp_extension = pyronn_torch.codegen.generate_shared_object()
+    cpp_extension = pyronn_torch.codegen.compile_shared_object()
 
 __all__ = ['ConeBeamProjector', 'cpp_extension']
diff --git a/src/pyronn_torch/codegen.py b/src/pyronn_torch/codegen.py
index 4783c70..b7f11ef 100644
--- a/src/pyronn_torch/codegen.py
+++ b/src/pyronn_torch/codegen.py
@@ -9,142 +9,149 @@
 
 
 import argparse
+import tempfile
 from glob import glob
 from os import makedirs
 from os.path import basename, dirname, join
 from shutil import copyfile, copytree, rmtree
 
-import pystencils
-from pystencils.astnodes import Block
-from pystencils.cpu.cpujit import get_cache_config
-from pystencils.data_types import TypedSymbol, create_type
-from pystencils.kernelparameters import FieldPointerSymbol, FieldShapeSymbol
-from pystencils_autodiff.backends.astnodes import TorchModule
-from pystencils_autodiff.framework_integration.astnodes import (
-    CustomFunctionCall, WrapperFunction)
-from pystencils_autodiff.framework_integration.printer import \
-    FrameworkIntegrationPrinter
-
-volume = pystencils.fields('volume: float32[3d]')
-projection = pystencils.fields('projection: float32[3d]')
-projection_matrices = pystencils.fields('matrices: float32[3d]')
-inv_matrices = pystencils.fields('inv_matrices: float32[3d]')
-source_points = pystencils.fields('source_points: float32[1d]')
-volume_slice = pystencils.fields('volume_slice: float32[2d]')
-projections_1d = pystencils.fields('projections_1d: float32[2d]')
-ray_vectors = pystencils.fields('ray_vectors: float32[2d]')
-
-FUNCTIONS = {
-        'Cone_Backprojection3D_Kernel_Launcher': CustomFunctionCall('Cone_Backprojection3D_Kernel_Launcher',
-                                             FieldPointerSymbol(projection.name, projection.dtype, const=True),
-                                             FieldPointerSymbol(volume.name, volume.dtype, const=False),
-                                             FieldPointerSymbol(projection_matrices.name,
-                                                                projection_matrices.dtype, const=True),
-                                             FieldShapeSymbol(['matrices'], 0),
-                                             *[FieldShapeSymbol(['volume'], i) for i in range(2, -1, -1)],
-                                             TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_z', create_type('float32'), const=True),
-                                             TypedSymbol('volume_origin_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_origin_y', create_type('float32'), const=True),
-                                             TypedSymbol('volume_origin_z', create_type('float32'), const=True),
-                                             *[FieldShapeSymbol(['projection'], i) for i in range(2, 0, -1)],
-                                             TypedSymbol('projection_multiplier', create_type('float32'), const=True),
-                                             fields_accessed=[volume, projection, projection_matrices], custom_signature="""
-void Cone_Backprojection3D_Kernel_Launcher(const float *sinogram_ptr, float *out, const float *projection_matrices, const int number_of_projections,
-                                          const int volume_width, const int volume_height, const int volume_depth,
-                                          const float volume_spacing_x, const float volume_spacing_y, const float volume_spacing_z,
-                                          const float volume_origin_x, const float volume_origin_y, const float volume_origin_z,
-                                          const int detector_width, const int detector_height, const float projection_multiplier);
-"""),  # noqa
-'Cone_Projection_Kernel_Launcher': CustomFunctionCall('Cone_Projection_Kernel_Launcher',
-                                             FieldPointerSymbol(volume.name, volume.dtype, const=True),
-                                             FieldPointerSymbol(projection.name, projection.dtype, const=False),
-                                             FieldPointerSymbol(inv_matrices.name,
-                                                                inv_matrices.dtype, const=True),
-                                             FieldPointerSymbol(source_points.name,
-                                                                source_points.dtype, const=True),
-                                             FieldShapeSymbol([source_points.name], 0),
-                                             *[FieldShapeSymbol(['volume'], i) for i in range(2, -1, -1)],
-                                             TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_z', create_type('float32'), const=True),
-                                             *[FieldShapeSymbol(['projection'], i) for i in range(2, 0, -1)],
-                                             TypedSymbol('step_size', create_type('float32'), const=True),
-                                             fields_accessed=[volume, projection, inv_matrices, source_points], custom_signature="""
-void Cone_Projection_Kernel_Launcher(const float* volume_ptr, float *out, const float *inv_AR_matrix, const float *src_points, 
-                                    const int number_of_projections, const int volume_width, const int volume_height, const int volume_depth, 
-                                    const float volume_spacing_x, const float volume_spacing_y, const float volume_spacing_z,
-                                    const int detector_width, const int detector_height, const float step_size);
-"""),  # noqa
-'Cone_Projection_Kernel_Tex_Interp_Launcher': CustomFunctionCall('Cone_Projection_Kernel_Tex_Interp_Launcher',
-                                             FieldPointerSymbol(volume.name, volume.dtype, const=True),
-                                             FieldPointerSymbol(projection.name, projection.dtype, const=False),
-                                             FieldPointerSymbol(inv_matrices.name,
-                                                                inv_matrices.dtype, const=True),
-                                             FieldPointerSymbol(source_points.name,
-                                                                source_points.dtype, const=True),
-                                             FieldShapeSymbol([source_points.name], 0),
-                                             *[FieldShapeSymbol(['volume'], i) for i in range(2, -1, -1)],
-                                             TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_z', create_type('float32'), const=True),
-                                             *[FieldShapeSymbol(['projection'], i) for i in range(2, 0, -1)],
-                                             TypedSymbol('step_size', create_type('float32'), const=True),
-                                             fields_accessed=[volume, projection, inv_matrices, source_points], custom_signature="""
-void Cone_Projection_Kernel_Tex_Interp_Launcher(
-    const float *__restrict__ volume_ptr, float *out,
-    const float *inv_AR_matrix, const float *src_points,
-    const int number_of_projections, const int volume_width,
-    const int volume_height, const int volume_depth,
-    const float volume_spacing_x, const float volume_spacing_y,
-    const float volume_spacing_z, const int detector_width,
-    const int detector_height, const float step_size);
-"""),  # noqa
-'Parallel_Projection2D_Kernel_Launcher': CustomFunctionCall('Parallel_Projection2D_Kernel_Launcher',
-                                             FieldPointerSymbol(volume_slice.name, volume_slice.dtype, const=True),
-                                             FieldPointerSymbol(projections_1d.name, projections_1d.dtype, const=False),
-                                             FieldPointerSymbol(ray_vectors.name,
-                                                                ray_vectors.dtype, const=True),
-                                             FieldShapeSymbol([ray_vectors.name], 0),
-                                             *[FieldShapeSymbol(['volume_slice'], i) for i in range(1, -1, -1)],
-                                             TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
-                                             TypedSymbol('volume_origin_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_origin_y', create_type('float32'), const=True),
-                                             FieldShapeSymbol([projections_1d.name], 1),
-                                             TypedSymbol('detector_spacing', create_type('float32'), const=True),
-                                             TypedSymbol('detector_origin', create_type('float32'), const=True),
-                                             fields_accessed=[volume_slice, projections_1d, ray_vectors], custom_signature="""
-void Parallel_Projection2D_Kernel_Launcher(
-    const float *volume_ptr, float *out, const float *ray_vectors,
-    const int number_of_projections, const int volume_width,
-    const int volume_height, const float volume_spacing_x,
-    const float volume_spacing_y, const float volume_origin_x,
-    const float volume_origin_y, const int detector_size,
-    const float detector_spacing, const float detector_origin);
-"""),  # noqa
-'Parallel_Backprojection2D_Kernel_Launcher': CustomFunctionCall('Parallel_Backprojection2D_Kernel_Launcher',
-                                             FieldPointerSymbol(projections_1d.name, projections_1d.dtype, const=True),
-                                             FieldPointerSymbol(volume_slice.name, volume_slice.dtype, const=False),
-                                             FieldPointerSymbol(ray_vectors.name,
-                                                                ray_vectors.dtype, const=True),
-                                             FieldShapeSymbol([ray_vectors.name], 0),
-                                             *[FieldShapeSymbol(['volume_slice'], i) for i in range(1, -1, -1)],
-                                             TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
-                                             TypedSymbol('volume_origin_x', create_type('float32'), const=True),
-                                             TypedSymbol('volume_origin_y', create_type('float32'), const=True),
-                                             FieldShapeSymbol([projections_1d.name], 1),
-                                             TypedSymbol('detector_spacing', create_type('float32'), const=True),
-                                             TypedSymbol('detector_origin', create_type('float32'), const=True),
-                                             fields_accessed=[volume_slice, projections_1d, ray_vectors], custom_signature="""
-void Parallel_Backprojection2D_Kernel_Launcher(const float *sinogram_ptr, float *out, const float *ray_vectors, const int number_of_projections,
-                                               const int volume_width, const int volume_height, const float volume_spacing_x, const float volume_spacing_y,
-                                               const float volume_origin_x, const float volume_origin_y,
-                                               const int detector_size, const float detector_spacing, const float detector_origin);
-"""),  # noqa
-        }
+try:
+    import pystencils
+    from pystencils.cpu.cpujit import get_cache_config
+    from pystencils.data_types import TypedSymbol, create_type
+    from pystencils.kernelparameters import FieldPointerSymbol, FieldShapeSymbol
+    from pystencils_autodiff.backends.astnodes import TorchModule
+    from pystencils_autodiff.framework_integration.astnodes import \
+        CustomFunctionCall
+    from pystencils_autodiff.framework_integration.printer import \
+        FrameworkIntegrationPrinter
+
+    volume = pystencils.fields('volume: float32[3d]')
+    projection = pystencils.fields('projection: float32[3d]')
+    projection_matrices = pystencils.fields('matrices: float32[3d]')
+    inv_matrices = pystencils.fields('inv_matrices: float32[3d]')
+    source_points = pystencils.fields('source_points: float32[1d]')
+    volume_slice = pystencils.fields('volume_slice: float32[2d]')
+    projections_1d = pystencils.fields('projections_1d: float32[2d]')
+    ray_vectors = pystencils.fields('ray_vectors: float32[2d]')
+
+    FUNCTIONS = {
+            'Cone_Backprojection3D_Kernel_Launcher': CustomFunctionCall('Cone_Backprojection3D_Kernel_Launcher',
+                                                 FieldPointerSymbol(projection.name, projection.dtype, const=True),
+                                                 FieldPointerSymbol(volume.name, volume.dtype, const=False),
+                                                 FieldPointerSymbol(projection_matrices.name,
+                                                                    projection_matrices.dtype, const=True),
+                                                 FieldShapeSymbol(['matrices'], 0),
+                                                 *[FieldShapeSymbol(['volume'], i) for i in range(2, -1, -1)],
+                                                 TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_z', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_origin_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_origin_y', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_origin_z', create_type('float32'), const=True),
+                                                 *[FieldShapeSymbol(['projection'], i) for i in range(2, 0, -1)],
+                                                 TypedSymbol('projection_multiplier',
+                                                             create_type('float32'), const=True),
+                                                 fields_accessed=[volume, projection, projection_matrices], custom_signature="""
+    void Cone_Backprojection3D_Kernel_Launcher(const float *sinogram_ptr, float *out, const float *projection_matrices, const int number_of_projections,
+                                              const int volume_width, const int volume_height, const int volume_depth,
+                                              const float volume_spacing_x, const float volume_spacing_y, const float volume_spacing_z,
+                                              const float volume_origin_x, const float volume_origin_y, const float volume_origin_z,
+                                              const int detector_width, const int detector_height, const float projection_multiplier);
+    """),  # noqa
+    'Cone_Projection_Kernel_Launcher': CustomFunctionCall('Cone_Projection_Kernel_Launcher',
+                                                 FieldPointerSymbol(volume.name, volume.dtype, const=True),
+                                                 FieldPointerSymbol(projection.name, projection.dtype, const=False),
+                                                 FieldPointerSymbol(inv_matrices.name,
+                                                                    inv_matrices.dtype, const=True),
+                                                 FieldPointerSymbol(source_points.name,
+                                                                    source_points.dtype, const=True),
+                                                 FieldShapeSymbol([source_points.name], 0),
+                                                 *[FieldShapeSymbol(['volume'], i) for i in range(2, -1, -1)],
+                                                 TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_z', create_type('float32'), const=True),
+                                                 *[FieldShapeSymbol(['projection'], i) for i in range(2, 0, -1)],
+                                                 TypedSymbol('step_size', create_type('float32'), const=True),
+                                                 fields_accessed=[volume, projection, inv_matrices, source_points], custom_signature="""
+    void Cone_Projection_Kernel_Launcher(const float* volume_ptr, float *out, const float *inv_AR_matrix, const float *src_points, 
+                                        const int number_of_projections, const int volume_width, const int volume_height, const int volume_depth, 
+                                        const float volume_spacing_x, const float volume_spacing_y, const float volume_spacing_z,
+                                        const int detector_width, const int detector_height, const float step_size);
+    """),  # noqa
+    'Cone_Projection_Kernel_Tex_Interp_Launcher': CustomFunctionCall('Cone_Projection_Kernel_Tex_Interp_Launcher',
+                                                 FieldPointerSymbol(volume.name, volume.dtype, const=True),
+                                                 FieldPointerSymbol(projection.name, projection.dtype, const=False),
+                                                 FieldPointerSymbol(inv_matrices.name,
+                                                                    inv_matrices.dtype, const=True),
+                                                 FieldPointerSymbol(source_points.name,
+                                                                    source_points.dtype, const=True),
+                                                 FieldShapeSymbol([source_points.name], 0),
+                                                 *[FieldShapeSymbol(['volume'], i) for i in range(2, -1, -1)],
+                                                 TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_z', create_type('float32'), const=True),
+                                                 *[FieldShapeSymbol(['projection'], i) for i in range(2, 0, -1)],
+                                                 TypedSymbol('step_size', create_type('float32'), const=True),
+                                                 fields_accessed=[volume, projection, inv_matrices, source_points], custom_signature="""
+    void Cone_Projection_Kernel_Tex_Interp_Launcher(
+        const float *__restrict__ volume_ptr, float *out,
+        const float *inv_AR_matrix, const float *src_points,
+        const int number_of_projections, const int volume_width,
+        const int volume_height, const int volume_depth,
+        const float volume_spacing_x, const float volume_spacing_y,
+        const float volume_spacing_z, const int detector_width,
+        const int detector_height, const float step_size);
+    """),  # noqa
+    'Parallel_Projection2D_Kernel_Launcher': CustomFunctionCall('Parallel_Projection2D_Kernel_Launcher',
+                                                 FieldPointerSymbol(volume_slice.name, volume_slice.dtype, const=True),
+                                                 FieldPointerSymbol(projections_1d.name,
+                                                                    projections_1d.dtype, const=False),
+                                                 FieldPointerSymbol(ray_vectors.name,
+                                                                    ray_vectors.dtype, const=True),
+                                                 FieldShapeSymbol([ray_vectors.name], 0),
+                                                 *[FieldShapeSymbol(['volume_slice'], i) for i in range(1, -1, -1)],
+                                                 TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_origin_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_origin_y', create_type('float32'), const=True),
+                                                 FieldShapeSymbol([projections_1d.name], 1),
+                                                 TypedSymbol('detector_spacing', create_type('float32'), const=True),
+                                                 TypedSymbol('detector_origin', create_type('float32'), const=True),
+                                                 fields_accessed=[volume_slice, projections_1d, ray_vectors], custom_signature="""
+    void Parallel_Projection2D_Kernel_Launcher(
+        const float *volume_ptr, float *out, const float *ray_vectors,
+        const int number_of_projections, const int volume_width,
+        const int volume_height, const float volume_spacing_x,
+        const float volume_spacing_y, const float volume_origin_x,
+        const float volume_origin_y, const int detector_size,
+        const float detector_spacing, const float detector_origin);
+    """),  # noqa
+    'Parallel_Backprojection2D_Kernel_Launcher': CustomFunctionCall('Parallel_Backprojection2D_Kernel_Launcher',
+                                                 FieldPointerSymbol(projections_1d.name,
+                                                                    projections_1d.dtype, const=True),
+                                                 FieldPointerSymbol(volume_slice.name, volume_slice.dtype, const=False),
+                                                 FieldPointerSymbol(ray_vectors.name,
+                                                                    ray_vectors.dtype, const=True),
+                                                 FieldShapeSymbol([ray_vectors.name], 0),
+                                                 *[FieldShapeSymbol(['volume_slice'], i) for i in range(1, -1, -1)],
+                                                 TypedSymbol('volume_spacing_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_spacing_y', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_origin_x', create_type('float32'), const=True),
+                                                 TypedSymbol('volume_origin_y', create_type('float32'), const=True),
+                                                 FieldShapeSymbol([projections_1d.name], 1),
+                                                 TypedSymbol('detector_spacing', create_type('float32'), const=True),
+                                                 TypedSymbol('detector_origin', create_type('float32'), const=True),
+                                                 fields_accessed=[volume_slice, projections_1d, ray_vectors], custom_signature="""
+    void Parallel_Backprojection2D_Kernel_Launcher(const float *sinogram_ptr, float *out, const float *ray_vectors, const int number_of_projections,
+                                                   const int volume_width, const int volume_height, const float volume_spacing_x, const float volume_spacing_y,
+                                                   const float volume_origin_x, const float volume_origin_y,
+                                                   const int detector_size, const float detector_spacing, const float detector_origin);
+    """),  # noqa
+            }
+except Exception as e:
+    import warnings
+    warnings.warn(e)
 
 
 def generate_shared_object(output_folder=None, source_files=None, show_code=False):
@@ -189,6 +196,50 @@ def generate_shared_object(output_folder=None, source_files=None, show_code=Fals
     return extension
 
 
+def compile_shared_object(output_folder=None, source_files=None):
+    """
+    Same as generate_shared_object without pystencils dependency
+    """
+    object_cache = tempfile.gettempdir()
+    module_name = 'pyronn_torch_cpp'
+
+    if not output_folder:
+        output_folder = dirname(__file__)
+
+    if not source_files:
+        source_files = glob(join(dirname(__file__), 'PYRO-NN-Layers', '*.cu.cc'))
+
+    generated_file = join(dirname(__file__), 'pyronn_torch.cpp')
+
+    cuda_sources = []
+    makedirs(join(object_cache, module_name), exist_ok=True)
+    rmtree(join(object_cache, module_name, 'helper_headers'), ignore_errors=True)
+    copytree(join(dirname(__file__), 'PYRO-NN-Layers', 'helper_headers'),
+             join(object_cache, module_name, 'helper_headers'))
+
+    for s in source_files:
+        dst = join(object_cache, module_name, basename(s).replace('.cu.cc', '.cu'))
+        copyfile(s, dst)  # Torch only accepts *.cu as CUDA
+        cuda_sources.append(dst)
+
+    # extension = module.compile(extra_source_files=cuda_sources,
+        # extra_cuda_flags=['-arch=sm_35'],
+        # with_cuda=True,
+        # compile_module_name=module_name)
+
+    from torch.utils.cpp_extension import load
+    torch_extension = load(module_name,
+                           [generated_file] + cuda_sources,
+                           with_cuda=True,
+                           extra_cflags=['--std=c++14'],
+                           extra_cuda_cflags=['-std=c++14', '-arch=sm_35'],
+                           build_directory=join(object_cache, module_name),
+                           extra_include_paths=[])
+    shared_object_file = join(object_cache, module_name, module_name + '.so')
+    copyfile(shared_object_file, join(output_folder, module_name + '.so'))
+    return torch_extension
+
+
 def main():
 
     parser = argparse.ArgumentParser()
@@ -196,7 +247,8 @@ def main():
     parser.add_argument('--source-files',  default=None)
     args = parser.parse_args()
 
-    generate_shared_object(args.output_folder, args.source_files, show_code=True)
+    # generate_shared_object(args.output_folder, args.source_files, show_code=True)
+    compile_shared_object(args.output_folder, args.source_files)
 
 
 if __name__ == '__main__':
diff --git a/tests/test_projection.py b/tests/test_projection.py
index 6fac28f..fad1a3a 100644
--- a/tests/test_projection.py
+++ b/tests/test_projection.py
@@ -6,6 +6,8 @@
 """
 
 """
+import pytest
+
 import pyronn_torch
 
 
@@ -13,7 +15,8 @@ def test_init():
     assert pyronn_torch.cpp_extension
 
 
-def test_projection():
+@pytest.mark.parametrize('with_texture', ('with_texture', False))
+def test_projection(with_texture):
     projector = pyronn_torch.ConeBeamProjector.from_conrad_config()
 
     volume = projector.new_volume_tensor()
-- 
GitLab