Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
E
elsa
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
34
Issues
34
List
Boards
Labels
Service Desk
Milestones
Iterations
Merge Requests
10
Merge Requests
10
Requirements
Requirements
List
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Code Review
Insights
Issue
Repository
Value Stream
External Wiki
External Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
IP
elsa
Commits
f6e6f3d7
Commit
f6e6f3d7
authored
Jun 23, 2020
by
Nikola Dinev
Committed by
Tobias Lasser
Jun 23, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feature/python bindings
parent
5d96e144
Pipeline
#278200
passed with stages
in 52 minutes and 26 seconds
Changes
21
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1728 additions
and
1 deletion
+1728
-1
.gitmodules
.gitmodules
+3
-0
CMakeLists.txt
CMakeLists.txt
+7
-0
cmake/FindLibClang.cmake
cmake/FindLibClang.cmake
+24
-0
elsa/CMakeLists.txt
elsa/CMakeLists.txt
+41
-0
elsa/core/CMakeLists.txt
elsa/core/CMakeLists.txt
+22
-0
elsa/core/bindings_hints.hpp
elsa/core/bindings_hints.hpp
+213
-0
elsa/functionals/CMakeLists.txt
elsa/functionals/CMakeLists.txt
+6
-0
elsa/generators/CMakeLists.txt
elsa/generators/CMakeLists.txt
+6
-0
elsa/io/CMakeLists.txt
elsa/io/CMakeLists.txt
+11
-0
elsa/io/bindings_hints.hpp
elsa/io/bindings_hints.hpp
+54
-0
elsa/operators/BlockLinearOperator.h
elsa/operators/BlockLinearOperator.h
+2
-0
elsa/operators/CMakeLists.txt
elsa/operators/CMakeLists.txt
+6
-0
elsa/problems/CMakeLists.txt
elsa/problems/CMakeLists.txt
+6
-0
elsa/projectors/CMakeLists.txt
elsa/projectors/CMakeLists.txt
+6
-0
elsa/projectors_cuda/CMakeLists.txt
elsa/projectors_cuda/CMakeLists.txt
+7
-1
elsa/solvers/CMakeLists.txt
elsa/solvers/CMakeLists.txt
+6
-0
thirdparty/pybind11
thirdparty/pybind11
+1
-0
tools/bindings_generation/CMakeLists.txt
tools/bindings_generation/CMakeLists.txt
+14
-0
tools/bindings_generation/Generator.hpp
tools/bindings_generation/Generator.hpp
+384
-0
tools/bindings_generation/Module.h
tools/bindings_generation/Module.h
+103
-0
tools/bindings_generation/Parser.cpp
tools/bindings_generation/Parser.cpp
+806
-0
No files found.
.gitmodules
View file @
f6e6f3d7
...
...
@@ -10,3 +10,6 @@
[submodule "thirdparty/quickvec"]
path = thirdparty/quickvec
url = https://gitlab.lrz.de/IP/quickvec.git
[submodule "thirdparty/pybind11"]
path = thirdparty/pybind11
url = https://github.com/pybind/pybind11.git
CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -26,6 +26,7 @@ option(ELSA_INSTALL "Enable generating the install targets for make install" ${E
option
(
ELSA_BUILD_EXAMPLES
"Enable building of examples"
${
ELSA_MASTER_PROJECT
}
)
option
(
ELSA_BUILD_CUDA_PROJECTORS
"Enable building (or attempting to) the CUDA projectors"
ON
)
option
(
ELSA_BUILD_PYTHON_BINDINGS
"Attempt to build python bindings if enabled (required libclang-dev)"
ON
)
option
(
ELSA_BUILD_WITH_MORE_WARNINGS
"Enable all and extra warnings when building (-Wall -Wextra)"
ON
)
option
(
ELSA_CUDA_VECTOR
"Build elsa with GPU DataContainer support and default"
OFF
)
...
...
@@ -87,6 +88,7 @@ set(INSTALL_CONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/elsa)
# include the InstallElsaModule function
include
(
InstallElsaModule
)
add_subdirectory
(
thirdparty/pybind11
)
# ------------ setup VectorCUDA --------
# ------------
...
...
@@ -171,6 +173,11 @@ endif(ELSA_TESTING OR ELSA_BENCHMARKS)
# ------------ add code/docs -----------
# ------------
# the bindings generator should be added before elsa, as we check whether the target can be built
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
add_subdirectory
(
tools/bindings_generation EXCLUDE_FROM_ALL
)
endif
()
# the elsa library
add_subdirectory
(
elsa
)
...
...
cmake/FindLibClang.cmake
0 → 100644
View file @
f6e6f3d7
find_program
(
LLVM_CONFIG_FOUND
"llvm-config"
)
if
(
LLVM_CONFIG_FOUND
)
execute_process
(
COMMAND llvm-config --cxxflags
OUTPUT_VARIABLE LibClang_Flags
)
string
(
STRIP
${
LibClang_Flags
}
LibClang_Flags
)
separate_arguments
(
LibClang_Flags NATIVE_COMMAND
${
LibClang_Flags
}
)
execute_process
(
COMMAND llvm-config --includedir
OUTPUT_VARIABLE LibClang_INCLUDE_DIR
)
find_library
(
LibClangCpp_LIBRARY NAMES clang-cpp PATH_SUFFIXES llvm-9/lib llvm-10/lib
)
find_library
(
LibLLVM_LIBRARY NAMES LLVM PATH_SUFFIXES llvm-9/lib llvm-10/lib
)
set
(
LibClang_LIBRARIES
${
LibClangCpp_LIBRARY
}
${
LibLLVM_LIBRARY
}
)
set
(
LibClang_INCLUDE_DIRS
${
LibClang_INCLUDE_DIR
}
)
include
(
FindPackageHandleStandardArgs
)
find_package_handle_standard_args
(
LibClang DEFAULT_MSG LibClang_LIBRARIES LibClang_INCLUDE_DIR
)
else
()
message
(
WARNING
"llvm-config couln't be located. Python bindings will not be generated"
)
endif
()
\ No newline at end of file
elsa/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -30,6 +30,47 @@ macro(ELSA_TEST NAME)
catch_discover_tests
(
test_
${
NAME
}
TEST_SPEC
${
ELSA_JUNIT_ARGUMENTS
}
)
endmacro
(
ELSA_TEST
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
find_package
(
PythonLibs
)
if
(
PYTHONLIBS_FOUND
)
add_custom_target
(
pyelsa
)
# macro for generation of the corresponding python module for the elsa target TARGET_NAME
# as a pre-build step the code for the python bindings will be generated and stored in BINDINGS_CODE_PATH
# the sources containing the public interface of TARGET_NAME should be specified as additional arguments
# you can omit a source file from the list to prevent the generation of bindings for that file
macro
(
GENERATE_BINDINGS TARGET_NAME BINDINGS_CODE_PATH HINTS_PATH
)
if
(
TARGET pybind11_generator
)
get_filename_component
(
CMAKE_CURRENT_LIST_DIR_LAST_PART
${
CMAKE_CURRENT_LIST_DIR
}
NAME
)
file
(
APPEND
${
PROJECT_BINARY_DIR
}
/elsa/__init__.py
"from .
${
CMAKE_CURRENT_LIST_DIR_LAST_PART
}
import *
\n
"
)
add_custom_command
(
OUTPUT
${
BINDINGS_CODE_PATH
}
COMMAND
${
PROJECT_BINARY_DIR
}
/tools/bindings_generation/pybind11_generator
${
ARGN
}
--extra-arg=-I
${
PYTHON_INCLUDE_DIRS
}
--extra-arg=-I
${
PYBIND11_INCLUDE_DIR
}
--hints=
${
HINTS_PATH
}
-o=
${
BINDINGS_CODE_PATH
}
--name=
${
TARGET_NAME
}
DEPENDS
${
TARGET_NAME
}
pybind11_generator
${
HINTS_PATH
}
WORKING_DIRECTORY
${
CMAKE_CURRENT_LIST_DIR
}
COMMENT
"Generating bindings code for
${
TARGET_NAME
}
"
VERBATIM
)
pybind11_add_module
(
py
${
TARGET_NAME
}
${
BINDINGS_CODE_PATH
}
)
target_include_directories
(
py
${
TARGET_NAME
}
PUBLIC
${
PROJECT_SOURCE_DIR
}
/elsa/bindings/hints
)
target_link_libraries
(
py
${
TARGET_NAME
}
PUBLIC
${
TARGET_NAME
}
)
target_compile_features
(
py
${
TARGET_NAME
}
PUBLIC cxx_std_17
)
add_dependencies
(
pyelsa py
${
TARGET_NAME
}
)
endif
()
endmacro
()
else
()
message
(
STATUS
"Couldn't find Python.h. Python bindings will not be generated."
)
endif
()
endif
()
# add the elsa modules
add_subdirectory
(
core
)
add_subdirectory
(
logging
)
...
...
elsa/core/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -89,6 +89,28 @@ target_compile_features(${ELSA_MODULE_TARGET_NAME} PUBLIC cxx_std_17)
# set -fPIC
set_target_properties
(
${
ELSA_MODULE_TARGET_NAME
}
PROPERTIES POSITION_INDEPENDENT_CODE ON
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
set
(
BINDINGS_SOURCES
${
MODULE_SOURCES
}
Descriptors/DataDescriptor.h
Descriptors/DescriptorUtils.h
Descriptors/VolumeDescriptor.h
Descriptors/BlockDescriptor.h
Descriptors/IdenticalBlocksDescriptor.h
Descriptors/PartitionDescriptor.h
Descriptors/RandomBlocksDescriptor.h
Geometry.h
)
list
(
FILTER BINDINGS_SOURCES EXCLUDE REGEX DataHandler.*.cpp
)
list
(
FILTER BINDINGS_SOURCES EXCLUDE REGEX .*Descriptor.*.cpp
)
list
(
FILTER BINDINGS_SOURCES EXCLUDE REGEX Geometry.cpp
)
list
(
FILTER BINDINGS_SOURCES EXCLUDE REGEX StrongTypes.cpp
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
bindings_hints.hpp
${
BINDINGS_SOURCES
}
)
endif
()
# build the tests (if enabled)
if
(
ELSA_TESTING
)
...
...
elsa/core/bindings_hints.hpp
0 → 100644
View file @
f6e6f3d7
#include "DataContainer.h"
#include "Descriptors/VolumeDescriptor.h"
#include "LinearOperator.h"
#include "DescriptorUtils.h"
#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <functional>
namespace
elsa
{
template
<
typename
Class
>
struct
ClassHints
{
};
namespace
py
=
pybind11
;
/// wrapper for variadic functions
template
<
typename
T
,
std
::
size_t
N
,
typename
=
std
::
make_index_sequence
<
N
>
>
struct
invokeBestCommonVariadic
;
template
<
typename
T
,
std
::
size_t
N
,
std
::
size_t
...
S
>
struct
invokeBestCommonVariadic
<
T
,
N
,
std
::
index_sequence
<
S
...
>>
{
static
auto
exec
(
const
py
::
args
&
args
)
{
if
(
args
.
size
()
==
N
)
return
elsa
::
bestCommon
(
args
[
S
].
template
cast
<
T
>()...);
if
constexpr
(
N
>
1
)
{
return
invokeBestCommonVariadic
<
T
,
N
-
1
>::
exec
(
args
);
}
else
{
throw
(
std
::
logic_error
(
"Unsupported number of variadic arguments"
));
}
};
};
template
<
typename
data_t
>
LinearOperator
<
data_t
>
adjointHelper
(
const
LinearOperator
<
data_t
>&
op
)
{
return
adjoint
(
op
);
}
template
<
typename
data_t
>
LinearOperator
<
data_t
>
leafHelper
(
const
LinearOperator
<
data_t
>&
op
)
{
return
leaf
(
op
);
}
// define global variables and functions in the module hints
struct
ModuleHints
{
static
void
addCustomFunctions
(
py
::
module
&
m
)
{
m
.
def
(
"adjoint"
,
&
adjointHelper
<
float
>
)
.
def
(
"adjoint"
,
&
adjointHelper
<
double
>
)
.
def
(
"adjoint"
,
&
adjointHelper
<
std
::
complex
<
float
>>
)
.
def
(
"adjoint"
,
&
adjointHelper
<
std
::
complex
<
double
>>
);
m
.
def
(
"leaf"
,
&
leafHelper
<
float
>
)
.
def
(
"leaf"
,
&
leafHelper
<
double
>
)
.
def
(
"leaf"
,
&
leafHelper
<
std
::
complex
<
float
>>
)
.
def
(
"leaf"
,
&
leafHelper
<
std
::
complex
<
double
>>
);
m
.
def
(
"bestCommon"
,
(
std
::
unique_ptr
<
DataDescriptor
>
(
*
)(
const
std
::
vector
<
const
DataDescriptor
*>&
))(
&
bestCommon
))
.
def
(
"bestCommon"
,
&
invokeBestCommonVariadic
<
const
DataDescriptor
&
,
10
>::
exec
);
}
};
template
<
typename
data_t
,
typename
type_
,
typename
...
options
>
void
addOperatorsDc
(
py
::
class_
<
type_
,
options
...
>&
c
)
{
c
.
def
(
"__add__"
,
[](
const
DataContainer
<
data_t
>&
self
,
const
DataContainer
<
data_t
>&
other
)
{
return
DataContainer
<
data_t
>
(
self
+
other
);
},
py
::
return_value_policy
::
move
)
.
def
(
"__mul__"
,
[](
const
DataContainer
<
data_t
>&
self
,
const
DataContainer
<
data_t
>&
other
)
{
return
DataContainer
<
data_t
>
(
self
*
other
);
},
py
::
return_value_policy
::
move
)
.
def
(
"__sub__"
,
[](
const
DataContainer
<
data_t
>&
self
,
const
DataContainer
<
data_t
>&
other
)
{
return
DataContainer
<
data_t
>
(
self
-
other
);
},
py
::
return_value_policy
::
move
)
.
def
(
"__truediv__"
,
[](
const
DataContainer
<
data_t
>&
self
,
const
DataContainer
<
data_t
>&
other
)
{
return
DataContainer
<
data_t
>
(
self
/
other
);
},
py
::
return_value_policy
::
move
)
// TODO: make the generator automatically generate this __setitem__ function
.
def
(
"__setitem__"
,
[](
elsa
::
DataContainer
<
data_t
>&
dc
,
elsa
::
index_t
i
,
data_t
value
)
{
dc
[
i
]
=
value
;
});
}
template
<
typename
data_t
>
struct
DataContainerHints
:
public
ClassHints
<
elsa
::
DataContainer
<
data_t
>>
{
constexpr
static
std
::
tuple
ignoreMethods
=
{
"operator()"
,
"begin"
,
"cbegin"
,
"end"
,
"cend"
,
"rbegin"
,
"crbegin"
,
"rend"
,
"crend"
};
template
<
typename
type_
,
typename
...
options
>
static
void
addCustomMethods
(
py
::
class_
<
type_
,
options
...
>&
c
)
{
addOperatorsDc
<
data_t
>
(
c
);
}
template
<
typename
type_
,
typename
...
options
>
static
void
exposeBufferInfo
(
py
::
class_
<
type_
,
options
...
>&
c
)
{
c
.
def
(
py
::
init
([](
py
::
buffer
b
)
{
py
::
buffer_info
info
=
b
.
request
();
if
(
info
.
format
!=
py
::
format_descriptor
<
data_t
>::
format
())
throw
std
::
invalid_argument
(
"Incompatible scalar types"
);
elsa
::
IndexVector_t
coeffsPerDim
(
info
.
ndim
);
ssize_t
minStride
=
info
.
strides
[
0
];
for
(
std
::
size_t
i
=
0
;
i
<
static_cast
<
std
::
size_t
>
(
info
.
ndim
);
i
++
)
{
if
(
info
.
strides
[
i
]
<
minStride
)
minStride
=
info
.
strides
[
i
];
coeffsPerDim
[
static_cast
<
elsa
::
index_t
>
(
i
)]
=
static_cast
<
elsa
::
index_t
>
(
info
.
shape
[
i
]);
}
if
(
static_cast
<
std
::
size_t
>
(
minStride
)
/
sizeof
(
data_t
)
!=
1
)
throw
std
::
invalid_argument
(
"Cannot convert strided buffer to DataContainer"
);
auto
map
=
Eigen
::
Map
<
Eigen
::
Matrix
<
data_t
,
Eigen
::
Dynamic
,
1
>>
(
static_cast
<
data_t
*>
(
info
.
ptr
),
coeffsPerDim
.
prod
());
elsa
::
VolumeDescriptor
dd
{
coeffsPerDim
};
return
std
::
make_unique
<
elsa
::
DataContainer
<
data_t
>>
(
dd
,
map
);
})).
def_buffer
([](
elsa
::
DataContainer
<
data_t
>&
m
)
{
std
::
vector
<
ssize_t
>
dims
,
strides
;
auto
coeffsPerDim
=
m
.
getDataDescriptor
().
getNumberOfCoefficientsPerDimension
();
ssize_t
combined
=
1
;
for
(
int
i
=
0
;
i
<
coeffsPerDim
.
size
();
i
++
)
{
dims
.
push_back
(
coeffsPerDim
[
i
]);
strides
.
push_back
(
combined
*
static_cast
<
ssize_t
>
(
sizeof
(
data_t
)));
combined
*=
coeffsPerDim
[
i
];
}
return
py
::
buffer_info
(
&
m
[
0
],
sizeof
(
data_t
),
py
::
format_descriptor
<
data_t
>::
format
(),
m
.
getDataDescriptor
().
getNumberOfDimensions
(),
coeffsPerDim
,
strides
);
});
}
};
template
<
typename
data_t
>
struct
LinearOperatorHints
:
public
ClassHints
<
elsa
::
LinearOperator
<
data_t
>>
{
template
<
typename
type_
,
typename
...
options
>
static
void
addCustomMethods
(
py
::
class_
<
type_
,
options
...
>&
c
)
{
c
.
def
(
py
::
self
+
py
::
self
).
def
(
py
::
self
*
py
::
self
);
}
};
template
<
typename
data_t
>
struct
DataContainerComplexHints
:
public
ClassHints
<
elsa
::
DataContainer
<
data_t
>>
{
constexpr
static
std
::
tuple
ignoreMethods
=
{
"operator()"
,
"begin"
,
"cbegin"
,
"end"
,
"cend"
,
"rbegin"
,
"crbegin"
,
"rend"
,
"crend"
};
template
<
typename
type_
,
typename
...
options
>
static
void
addCustomMethods
(
py
::
class_
<
type_
,
options
...
>&
c
)
{
addOperatorsDc
<
data_t
>
(
c
);
}
// TODO: pybind11 does not provide a default format_descriptor for complex types -> make a
// custom one for this to work
// CustomMethod<true, py::buffer_info, elsa::DataContainer<data_t>&> buffer =
// [](elsa::DataContainer<data_t>& m) {
// std::vector<ssize_t> dims, strides;
// auto coeffsPerDim = m.getDataDescriptor().getNumberOfCoefficientsPerDimension();
// ssize_t combined = 1;
// for (int i = 0; i < coeffsPerDim.size(); i++) {
// dims.push_back(coeffsPerDim[i]);
// strides.push_back(combined * static_cast<ssize_t>(sizeof(data_t)));
// combined *= coeffsPerDim[i];
// }
// return py::buffer_info(
// &m[0], /* Pointer to buffer */
// sizeof(data_t), /* Size of one scalar */
// py::format_descriptor<data_t>::format(),
// /* Python struct-style format descriptor
// */
// m.getDataDescriptor().getNumberOfDimensions(), /* Number of dimensions */
// coeffsPerDim, /* Buffer dimensions */
// strides);
// };
};
template
struct
DataContainerHints
<
float
>;
template
struct
DataContainerComplexHints
<
std
::
complex
<
float
>
>
;
template
struct
DataContainerHints
<
double
>;
template
struct
DataContainerComplexHints
<
std
::
complex
<
double
>
>
;
template
struct
DataContainerHints
<
index_t
>;
template
struct
LinearOperatorHints
<
float
>;
template
struct
LinearOperatorHints
<
double
>;
template
struct
LinearOperatorHints
<
std
::
complex
<
float
>
>
;
template
struct
LinearOperatorHints
<
std
::
complex
<
double
>
>
;
}
// namespace elsa
\ No newline at end of file
elsa/functionals/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -61,6 +61,12 @@ if(ELSA_TESTING)
add_subdirectory
(
tests
)
endif
(
ELSA_TESTING
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
""
${
MODULE_SOURCES
}
)
endif
()
# register the module
registerComponent
(
${
ELSA_MODULE_NAME
}
)
...
...
elsa/generators/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -43,6 +43,12 @@ if(ELSA_TESTING)
add_subdirectory
(
tests
)
endif
(
ELSA_TESTING
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
""
${
MODULE_SOURCES
}
CircleTrajectoryGenerator.h
)
endif
()
# register the module
registerComponent
(
${
ELSA_MODULE_NAME
}
)
...
...
elsa/io/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -37,6 +37,17 @@ target_compile_features(${ELSA_MODULE_TARGET_NAME} PUBLIC cxx_std_17)
# set -fPIC
set_target_properties
(
${
ELSA_MODULE_TARGET_NAME
}
PROPERTIES POSITION_INDEPENDENT_CODE ON
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
set
(
BINDINGS_SOURCES
${
MODULE_SOURCES
}
)
list
(
FILTER BINDINGS_SOURCES EXCLUDE REGEX ioUtils.cpp
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
bindings_hints.hpp
${
BINDINGS_SOURCES
}
)
endif
()
# build the tests (if enabled)
if
(
ELSA_TESTING
)
...
...
elsa/io/bindings_hints.hpp
0 → 100644
View file @
f6e6f3d7
#include "EDFHandler.h"
#include "MHDHandler.h"
#include <pybind11/pybind11.h>
#include <functional>
namespace
elsa
{
template
<
typename
Class
>
struct
ClassHints
{
};
template
<
class
Return
,
class
...
Args
>
using
CustomMethod
=
std
::
pair
<
bool
,
std
::
function
<
Return
(
Args
...)
>>
;
namespace
py
=
pybind11
;
struct
EDFHints
:
public
ClassHints
<
EDF
>
{
constexpr
static
std
::
tuple
ignoreMethods
=
{
"read"
};
template
<
typename
type_
,
typename
...
options
>
static
void
addCustomMethods
(
py
::
class_
<
type_
,
options
...
>&
c
)
{
c
.
def_static
(
"readf"
,
[](
std
::
string
filename
)
{
return
EDF
::
read
<
float
>
(
filename
);
},
py
::
return_value_policy
::
move
)
.
def_static
(
"readd"
,
[](
std
::
string
filename
)
{
return
EDF
::
read
<
double
>
(
filename
);
},
py
::
return_value_policy
::
move
)
.
def_static
(
"readl"
,
[](
std
::
string
filename
)
{
return
EDF
::
read
<
index_t
>
(
filename
);
},
py
::
return_value_policy
::
move
);
}
};
struct
MHDHints
:
public
ClassHints
<
MHD
>
{
constexpr
static
std
::
tuple
ignoreMethods
=
{
"read"
};
template
<
typename
type_
,
typename
...
options
>
static
void
addCustomMethods
(
py
::
class_
<
type_
,
options
...
>&
c
)
{
c
.
def_static
(
"readf"
,
[](
std
::
string
filename
)
{
return
MHD
::
read
<
float
>
(
filename
);
},
py
::
return_value_policy
::
move
)
.
def_static
(
"readd"
,
[](
std
::
string
filename
)
{
return
MHD
::
read
<
double
>
(
filename
);
},
py
::
return_value_policy
::
move
)
.
def_static
(
"readl"
,
[](
std
::
string
filename
)
{
return
MHD
::
read
<
index_t
>
(
filename
);
},
py
::
return_value_policy
::
move
);
}
};
}
// namespace elsa
\ No newline at end of file
elsa/operators/BlockLinearOperator.h
View file @
f6e6f3d7
...
...
@@ -75,6 +75,8 @@ namespace elsa
/// default destructor
~
BlockLinearOperator
()
override
=
default
;
BlockLinearOperator
&
operator
=
(
BlockLinearOperator
&
)
=
delete
;
/// return the operator corresponding to the i-th block of the matrix
const
LinearOperator
<
data_t
>&
getIthOperator
(
index_t
i
)
const
;
...
...
elsa/operators/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -52,6 +52,12 @@ if(ELSA_TESTING)
add_subdirectory
(
tests
)
endif
(
ELSA_TESTING
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
""
${
MODULE_SOURCES
}
)
endif
()
# register the module
registerComponent
(
${
ELSA_MODULE_NAME
}
)
...
...
elsa/problems/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -47,6 +47,12 @@ if(ELSA_TESTING)
add_subdirectory
(
tests
)
endif
(
ELSA_TESTING
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
""
${
MODULE_SOURCES
}
)
endif
()
# register the module
registerComponent
(
${
ELSA_MODULE_NAME
}
)
...
...
elsa/projectors/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -58,6 +58,12 @@ if(ELSA_TESTING)
add_subdirectory
(
tests
)
endif
(
ELSA_TESTING
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
""
${
MODULE_SOURCES
}
)
endif
()
# register the module
registerComponent
(
${
ELSA_MODULE_NAME
}
)
...
...
elsa/projectors_cuda/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -55,7 +55,13 @@ if (CMAKE_CUDA_COMPILER)
add_subdirectory
(
tests
)
endif
(
ELSA_TESTING
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
""
${
MODULE_SOURCES
}
)
endif
()
# register the module
registerComponent
(
${
ELSA_MODULE_NAME
}
)
...
...
elsa/solvers/CMakeLists.txt
View file @
f6e6f3d7
...
...
@@ -43,6 +43,12 @@ if(ELSA_TESTING)
add_subdirectory
(
tests
)
endif
(
ELSA_TESTING
)
if
(
ELSA_BUILD_PYTHON_BINDINGS
)
GENERATE_BINDINGS
(
${
ELSA_MODULE_TARGET_NAME
}
${
CMAKE_CURRENT_BINARY_DIR
}
/bind_
${
ELSA_MODULE_NAME
}
.cpp
""
${
MODULE_SOURCES
}
)
endif
()
# register the module
registerComponent
(
${
ELSA_MODULE_NAME
}
)
...
...
pybind11
@
d96c3451
Subproject commit d96c34516d276f4c33d25df23d5934af1c5b2406
tools/bindings_generation/CMakeLists.txt
0 → 100644
View file @
f6e6f3d7
cmake_minimum_required
(
VERSION 3.10
)
find_package
(
LibClang
)
if
(
LIBCLANG_FOUND
)
add_executable
(
pybind11_generator Parser.cpp
)
target_compile_options
(
pybind11_generator PUBLIC
${
LibClang_Flags
}
)
target_compile_features
(
pybind11_generator PUBLIC cxx_std_17
)
target_link_libraries
(
pybind11_generator LLVM clang-cpp stdc++fs
)
file
(
WRITE
${
PROJECT_BINARY_DIR
}
/elsa/__init__.py
""
)
else
()
message
(
WARNING
"libclang-dev not found. Python bindings will not be generated."
)
endif
()
tools/bindings_generation/Generator.hpp
0 → 100644
View file @
f6e6f3d7
#pragma once
#include "Module.h"
#include <iostream>
#include <fstream>
#include <map>
#include <algorithm>
#include <assert.h>
// Determine wether to use <filesystem> or <experimental/filesystem>. Adapted from
// https://stackoverflow.com/questions/53365538/how-to-determine-whether-to-use-filesystem-or-experimental-filesystem,
// simplified by removing MSVC specific code, as this is expected to be run with clang
// We haven't checked which filesystem to include yet
#ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
// Check for feature test macro for <filesystem>
#if defined(__cpp_lib_filesystem)
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
// Check for feature test macro for <experimental/filesystem>
#elif defined(__cpp_lib_experimental_filesystem)
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
// We can't check if headers exist...
// Let's assume experimental to be safe
#elif !defined(__has_include)
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
// Check if the header "<filesystem>" exists
#elif __has_include(<filesystem>)
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
// Check if the header "<filesystem>" exists
#elif __has_include(<experimental/filesystem>)
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
// Fail if neither header is available with a nice error message
#else
#error Could not find system header "<filesystem>" or "<experimental/filesystem>"
#endif
// We priously determined that we need the exprimental version
#if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
// Include it
#include <experimental/filesystem>
// We need the alias from std::experimental::filesystem to std::filesystem
namespace
std
{
namespace
filesystem
=
experimental
::
filesystem
;
}
// We have a decent compiler and can use the normal version
#else
// Include it
#include <filesystem>
#endif
#endif // #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
class
Generator
{
public:
static
void
generateBindingsForModule
(
const
elsa
::
Module
&
m
,
std
::
string
outputPath
)
{
if
(
outputPath
.
empty
())
outputPath
=
"bind_"
+
m
.
name
+
".cpp"
;
std
::
filesystem
::
path
p
(
outputPath
);
p
=
p
.
parent_path
();
p
.
append
(
"__init__.py"
);
std
::
ofstream
outputFile
(
outputPath
);
std
::
ofstream
initFile
(
p
.
c_str
());
initFile
<<
"from ."
<<
m
.
pythonName
<<
" import *
\n
"
;