Skip to content

Commit

Permalink
Port app4triqs to clair and c2py
Browse files Browse the repository at this point in the history
  • Loading branch information
parcollet authored and Wentzell committed Sep 29, 2023
1 parent ab04e46 commit 3891b0d
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 26 deletions.
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ project(app4triqs VERSION 3.2.0 LANGUAGES C CXX)
get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)

# ############
# Load TRIQS and CPP2PY
# Load TRIQS
find_package(TRIQS 3.2 REQUIRED)

# Get the git hash & print status
Expand Down Expand Up @@ -72,6 +72,13 @@ if(PythonSupport AND NOT TRIQS_WITH_PYTHON_SUPPORT)
message(FATAL_ERROR "TRIQS was installed without Python support. Cannot build the Python Interface. Disable the build with -DPythonSupport=OFF")
endif()

# Clair
option(Clair "Use Clair python bindings generators" OFF)
if(Clair)
find_package(Clair REQUIRED)
MESSAGE(STATUS "Found Clair plugin. Will use it to compile.")
endif()

# Documentation
option(Build_Documentation "Build documentation" OFF)
if(NOT IS_SUBPROJECT AND (Build_Documentation AND NOT PythonSupport))
Expand Down
19 changes: 19 additions & 0 deletions c++/app4triqs/app4triqs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <triqs/mesh.hpp>
#include <h5/h5.hpp>

using namespace triqs::gfs;

namespace app4triqs {

/**
Expand Down Expand Up @@ -76,4 +78,21 @@ namespace app4triqs {
*/
int chain(int i, int j);


inline double sum_array(nda::array<double,2> const & a) {
return sum(a);
}

inline nda::array<double,2> make_array(double i) {
return {{1,i},{2,3}};
}

inline auto make_gf() {
return gf<imfreq>{{10.0, Fermion}, {2, 2}};
}

inline void print_gf(int i, gf<imfreq> const & g) {
std::cout << g << std::endl;
}

} // namespace app4triqs
10 changes: 5 additions & 5 deletions deps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ else()
endif()
endif()

# -- Cpp2Py --
# -- c2py --
if(PythonSupport OR (NOT IS_SUBPROJECT AND Build_Documentation))
external_dependency(Cpp2Py
GIT_REPO https://github.com/TRIQS/cpp2py
VERSION 2.0
GIT_TAG master
external_dependency(c2py
GIT_REPO https://github.com/TRIQS/c2py
#VERSION 2.0
GIT_TAG unstable
BUILD_ALWAYS
EXCLUDE_FROM_ALL
)
Expand Down
57 changes: 41 additions & 16 deletions python/app4triqs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
# Configure the version
configure_file(version.py.in version.py)

# All Python files. Copy them in the build dir to have a complete package for the tests.
# ------- Python files ----------

# Copy them in the build dir to have a complete package for the tests.
file(GLOB_RECURSE python_sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.py)
file(GLOB_RECURSE wrap_generators RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *_desc.py)
list(REMOVE_ITEM python_sources "${wrap_generators}")
foreach(file ${python_sources})
configure_file(${file} ${file} COPYONLY)
configure_file(${file} ${file} COPYONLY)
endforeach()

# Install python files to proper location
set(PYTHON_LIB_DEST ${TRIQS_PYTHON_LIB_DEST_ROOT}/${PROJECT_NAME})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/version.py DESTINATION ${PYTHON_LIB_DEST})
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${TRIQS_PYTHON_LIB_DEST_ROOT} FILES_MATCHING PATTERN "*.py" PATTERN "*_desc.py" EXCLUDE)

# Build and install any python modules
foreach(gen ${wrap_generators})
string(REPLACE "_desc.py" "" gen ${gen})
get_filename_component(module_name ${gen} NAME_WE)
get_filename_component(module_dir ${gen} DIRECTORY)
add_cpp2py_module(NAME ${module_name} DIRECTORY ${module_dir})
add_library(${PROJECT_NAME}::${module_name} ALIAS ${module_name})
target_link_libraries(${module_name} ${PROJECT_NAME}_c triqs_py)
install(TARGETS ${module_name} DESTINATION ${PYTHON_LIB_DEST}/${module_dir})
endforeach()
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${TRIQS_PYTHON_LIB_DEST_ROOT} FILES_MATCHING PATTERN "*.py")

# ------- Python Extension ----------

find_package(Python COMPONENTS Interpreter Development NumPy)

set(module_name app4triqs_module)

Python_add_library( ${module_name} MODULE ${module_name}.$<IF:$<TARGET_EXISTS:clair::c2py_plugin>,cpp,wrap.cxx>)

target_include_directories(${module_name} SYSTEM PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${module_name} PRIVATE ${PROJECT_NAME}_c $<TARGET_NAME_IF_EXISTS:clair::c2py_plugin> c2py::c2py Python::NumPy triqs_py)

# What is the use of this ?
add_library(${PROJECT_NAME}::${module_name} ALIAS ${module_name})

install(TARGETS ${module_name} DESTINATION ${PYTHON_LIB_DEST}/${module_dir})

if(ASAN)
target_link_libraries(${module_name} PRIVATE $<BUILD_INTERFACE:asan>)
endif()

if(UBSAN)
target_link_libraries(${module_name} PRIVATE $<BUILD_INTERFACE:ubsan>)
endif()

#--------------------------------------------------------
# A target to copy the wrap.cxx files back to the src
#--------------------------------------------------------

add_custom_target(copy_wrap_cxx_to_src)
add_custom_command(
TARGET copy_wrap_cxx_to_src
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/*.wrap.cxx ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND echo "** Copying all wrap.cxx to src"
)

4 changes: 2 additions & 2 deletions python/app4triqs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
DOC
"""
from .app4triqs_module import Toto, chain
from .app4triqs_module import Toto, chain, sum_array, make_array, make_gf, print_gf

__all__ = ['Toto', 'chain']
__all__ = [] #'Toto', 'chain']
22 changes: 22 additions & 0 deletions python/app4triqs/app4triqs_module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <c2py/c2py.hpp>
#include "app4triqs/app4triqs.hpp"

using namespace std::string_literals;
using app4triqs::toto;
template <> struct c2py::arithmetic<toto, c2py::OpName::Add> : std::tuple<triplet<toto,toto,toto>> {};


/*template<> inline const std::string c2py::cpp_name<triqs::gfs::imfreq> = "imfreq"s;*/
/*template<> inline const std::string c2py::cpp_name<triqs::gfs::matrix_valued> = "matrix_valued"s;*/

/*template <typename M, typename T> */
/*static inline const std::string c2py::cpp_name<triqs::gfs::gf<M,T>> = "gf<"s + c2py::cpp_name<M> + ", " + c2py::cpp_name<T> + ">";*/


namespace c2py_module {

// auto documentation = "Module documentation";
auto ns = "app4triqs";
auto package_name = "app4triqs";

} // namespace c2py_module
144 changes: 144 additions & 0 deletions python/app4triqs/app4triqs_module.wrap.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@

// C.f. https://numpy.org/doc/1.21/reference/c-api/array.html#importing-the-api
#define PY_ARRAY_UNIQUE_SYMBOL _cpp2py_ARRAY_API
#pragma clang diagnostic ignored "-W#warnings"
#include "app4triqs_module.cpp"
#include "c2py/c2py.hpp"

using c2py::operator"" _a;

// ==================== Wrapped classes =====================

template <> inline constexpr bool c2py::is_wrapped<app4triqs::toto> = true;

// ==================== enums =====================

// ==================== module classes =====================

template <> inline const std::string c2py::cpp_name<app4triqs::toto> = "app4triqs::toto";
template <> inline constexpr auto c2py::tp_name<app4triqs::toto> = "app4triqs.app4triqs_module.Toto";
template <>
inline constexpr const char *c2py::tp_doc<app4triqs::toto> = R"DOC( A very useful and important class)DOC";

static auto init_0 =
c2py::dispatcher_c_kw_t{c2py::c_constructor<app4triqs::toto>(), c2py::c_constructor<app4triqs::toto, int>("i_")};
template <> constexpr initproc c2py::tp_init<app4triqs::toto> = c2py::pyfkw_constructor<init_0>;
// f
static auto const fun_0 = c2py::dispatcher_f_kw_t{c2py::cfun(c2py::castm<int>(&app4triqs::toto::f), "u")};

// get_i
static auto const fun_1 = c2py::dispatcher_f_kw_t{c2py::cfun(c2py::castmc<>(&app4triqs::toto::get_i))};
static const auto doc_d_0 = fun_0.doc({R"DOC( A simple function with :math:`G(
Parameters
----------
u: Nothing useful)DOC"});
static const auto doc_d_1 = fun_1.doc({R"DOC( Simple accessor)DOC"});

// ----- Method table ----
template <>
PyMethodDef c2py::tp_methods<app4triqs::toto>[] = {
{"f", (PyCFunction)c2py::pyfkw<fun_0>, METH_VARARGS | METH_KEYWORDS, doc_d_0.c_str()},
{"get_i", (PyCFunction)c2py::pyfkw<fun_1>, METH_VARARGS | METH_KEYWORDS, doc_d_1.c_str()},
{"__write_hdf5__", c2py::tpxx_write_h5<app4triqs::toto>, METH_VARARGS, " "},
{"__getstate__", c2py::getstate_tuple<app4triqs::toto>, METH_NOARGS, ""},
{"__setstate__", c2py::setstate_tuple<app4triqs::toto>, METH_O, ""},
{nullptr, nullptr, 0, nullptr} // Sentinel
};

// ----- Method table ----

template <>
constinit PyGetSetDef c2py::tp_getset<app4triqs::toto>[] = {

{nullptr, nullptr, nullptr, nullptr, nullptr}};

// ==================== module functions ====================

// chain
static auto const fun_2 = c2py::dispatcher_f_kw_t{c2py::cfun(c2py::cast<int, int>(&app4triqs::chain), "i", "j")};

// make_array
static auto const fun_3 = c2py::dispatcher_f_kw_t{c2py::cfun(c2py::cast<double>(&app4triqs::make_array), "i")};

// make_gf
static auto const fun_4 = c2py::dispatcher_f_kw_t{c2py::cfun(c2py::cast<>(&app4triqs::make_gf))};

// sum_array
static auto const fun_5 =
c2py::dispatcher_f_kw_t{c2py::cfun(c2py::cast<const nda::array<double, 2> &>(&app4triqs::sum_array), "a")};

static const auto doc_d_2 = fun_2.doc({R"DOC( Chain digits of two integers
A set of functions that implement chaining
Do I really need to explain more ?
Parameters
----------
j: The second integer
i: The first integer)DOC"});
static const auto doc_d_3 = fun_3.doc({R"DOC( )DOC"});
static const auto doc_d_4 = fun_4.doc({R"DOC( )DOC"});
static const auto doc_d_5 = fun_5.doc({R"DOC( )DOC"});
//--------------------- module function table -----------------------------

static PyMethodDef module_methods[] = {
{"chain", (PyCFunction)c2py::pyfkw<fun_2>, METH_VARARGS | METH_KEYWORDS, doc_d_2.c_str()},
{"make_array", (PyCFunction)c2py::pyfkw<fun_3>, METH_VARARGS | METH_KEYWORDS, doc_d_3.c_str()},
{"make_gf", (PyCFunction)c2py::pyfkw<fun_4>, METH_VARARGS | METH_KEYWORDS, doc_d_4.c_str()},
{"sum_array", (PyCFunction)c2py::pyfkw<fun_5>, METH_VARARGS | METH_KEYWORDS, doc_d_5.c_str()},
{nullptr, nullptr, 0, nullptr} // Sentinel
};

//--------------------- module struct & init error definition ------------

//// module doc directly in the code or "" if not present...
/// Or mandatory ?
static struct PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
"app4triqs_module", /* name of module */
"DOC MODULE", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
module_methods,
NULL,
NULL,
NULL,
NULL};

//--------------------- module init function -----------------------------

extern "C" __attribute__((visibility("default"))) PyObject *PyInit_app4triqs_module() {

// import numpy iff 'numpy/arrayobject.h' included
#ifdef Py_ARRAYOBJECT_H
import_array();
#endif

PyObject *m;

if (PyType_Ready(&c2py::py_type_object<c2py::py_range>) < 0)
return NULL;
if (PyType_Ready(&c2py::py_type_object<app4triqs::toto>) < 0)
return NULL;

m = PyModule_Create(&module_def);
if (m == NULL)
return NULL;

auto &conv_table = *c2py::conv_table_sptr.get();

conv_table[std::type_index(typeid(c2py::py_range)).name()] = &c2py::py_type_object<c2py::py_range>;
CLAIR_C2PY_ADD_TYPE_OBJECT(app4triqs::toto, "Toto");

c2py::pyref module = c2py::pyref::module("h5.formats");
if (not module)
return nullptr;
c2py::pyref register_class = module.attr("register_class");

register_h5_type<app4triqs::toto>(register_class);

return m;
}
19 changes: 17 additions & 2 deletions test/python/Basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import unittest

from app4triqs import Toto, chain
from app4triqs import Toto, chain, sum_array, make_array, make_gf, print_gf
from h5 import *
from triqs.utility import mpi
import numpy as np

class test_toto(unittest.TestCase):

Expand All @@ -18,7 +19,6 @@ def test_add(self):


def test_h5(self):

a=Toto(0)
with HDFArchive("f.h5",'w') as A:
A["a"] = a
Expand All @@ -37,6 +37,21 @@ def test_mpi(self):

self.assertEqual(a, Toto(1))

def test_nda(self):

a=np.array([(1,2),(2,3)])
self.assertEqual(sum_array(a), 8)
np.testing.assert_array_equal(make_array(2),a)

def test_gf(self):

g = make_gf()
print(g.mesh)
print_gf(2, g)
# Error to test error message
# print_gf(8,2)


class test_chain(unittest.TestCase):

def test_chain(self):
Expand Down
5 changes: 5 additions & 0 deletions test/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ foreach(file ${all_h5_ref_files})
configure_file(${file} ${file} COPYONLY)
endforeach()

#find_package(sanitizer REQUIRED "asan")
MESSAGE("SANITIZER_RT_PRELOAD = ${SANITIZER_RT_PRELOAD}")

# List of all tests
set(all_tests Basic)

Expand All @@ -13,3 +16,5 @@ foreach(test ${all_tests})
add_test(NAME Py_${test_name} COMMAND ${TRIQS_PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${test_dir}/${test_name}.py WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir})
set_property(TEST Py_${test_name} APPEND PROPERTY ENVIRONMENT PYTHONPATH=${PROJECT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${SANITIZER_RT_PRELOAD})
endforeach()


0 comments on commit 3891b0d

Please sign in to comment.