GSL Python 化教程

GSL Python 化教程#

本教程介绍了 pythonizations 及其如何解决底层问题的使用方式。

设定:想象您想使用 numpy,但被提供了一个基于 GNU 科学库(GNU Scientific Library,简称 GSL)的 C 或 C++ 库。如何让它们友好地协同工作?

GSL 是用 C 编写的,但结构非常严谨:它有一致的命名约定,为其主要结构提供分配器/解除分配器,并且具有明确的所有权规则。由于这种结构,可以根据反射信息编写 pythonizations,最终变得非常简单,因此易于维护。

import cppyy
/media/pc/data/lxw/envs/anaconda3x/envs/xxx/bin/python3.12: not an ELF file.

为了简化,我们将使用 gsl_blas_dgemm 作为“基于GSL的C/C++库”的替代。为了让我们的生活更轻松,我们将把绑定到 GSL 和 GSLBLAS 的内容包装成单个反射字典。对于简单项目来说,这有些过头了,但如果我们想访问所有的 GSL(而不是单独的 GSL 和 GSLBLAS),这种方法是可行的。

# first, pull in all headers from the GSL installation directory (/usr/include on my system).
import glob, os
GSL_HOME = '/usr/include'
gsl_headers = [os.path.relpath(x, GSL_HOME) for x in glob.glob(GSL_HOME+'/gsl/*.h')]

接下来,我们编写一个选择文件,从 GSL 中提取所有有趣的部分。由于适当的命名约定,这个文件可以保持简单(如果 GSL 是一个 C++ 库,并且位于一个命名空间中,它可能会更简单)。然后我们运行 genreflex 命令生成字典文件并编译它,链接到 GSL 和 GSLBLAS。最后,我们准备将字典反射文件加载到 cppyy 中。

!mkdir -p .temp
%%file .temp/gsl_selection.xml
<lcgdict>
   <struct pattern="gsl_*" />
   <function pattern="gsl_*" />
   <enum pattern="GSL*" />
   <enum pattern="CBLAS*" />
</lcgdict>
Overwriting .temp/gsl_selection.xml
# conventional name for generated output
rfldct = 'GSLDict'

if not os.path.exists(f'{rfldct}_rflx.cpp'):
    import subprocess, sys

    # generate the reflection dictionary
    try:
        subprocess.check_output(
            ['genreflex',                  # utility installed by pip when installing cppyy
             '-s', '.temp/gsl_selection.xml',    # selection file (see above)
             '-o', f'.temp/{rfldct}_rflx.cpp',   # intermediate output file
             '-I'+GSL_HOME]+               # include search path for GSL headers
             gsl_headers)                  # headers themselves
    except subprocess.CalledProcessError as e:
        print(f"genreflex failed ({e.returncode:d}): {e.output}")
    else:
        print("genreflex done")
genreflex failed (1): b''
Error: No valid header was provided!
if not os.path.exists('%s.so' % rfldct):
    # get command line arguments for compiler from cling
    try:
        clingflags = subprocess.check_output(
            ['cling-config',               # utility installed by pip when installing cppyy
             '--cppflags'])
    except subprocess.CalledProcessError as e:
        print('cling-config failed (%d):' % e.returncode, e.output)
        raise
    else:
        print('cling-config done')

    # compile generated file
    try:
        subprocess.check_output(
            ['g++',                        # C++ compiler
             '-fPIC',                      # require position independent code
             '-shared',                    # generate shared library
             '-o', '%s.so'%rfldct,         # output file
             '-I'+GSL_HOME,                # include search path for GSL headers
             '%s_rflx.cpp'%rfldct]+        # intermediate file to compile
             clingflags.split()+           # extra flags provided by cling
             ['-lgsl', '-lgslcblas'])      # link in GSL and GSLBLAS
    except subprocess.CalledProcessError as e:
        print('compilation failed (%d):' % e.returncode, e.output)
    else:
        print('compilation done')
cling-config done
compilation failed (1): b''
cc1plus: fatal error: GSLDict_rflx.cpp: No such file or directory
compilation terminated.
# load the generated dictionary
cppyy.load_reflection_info(rfldct)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[7], line 2
      1 # load the generated dictionary
----> 2 cppyy.load_reflection_info(rfldct)

File /media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/cppyy/_cpython_cppyy.py:164, in load_reflection_info(name)
    162 sc = gbl.gSystem.Load(name)
    163 if sc == -1:
--> 164     raise RuntimeError("Unable to load reflection library "+name)

RuntimeError: Unable to load reflection library GSLDict