The pyten_module directory contains .cpp
files which, when compiled and linked into a shared library, make up the Python3 bindings for SyTen. These bindings allow calling SyTen from within Python at negligible speed differences. In a certain sense, the bindings are like another interface to the library next to the binary executables in bin/
and lat/
. Most but not all functions are already available from Python. If a specific function you need is missing, it is generally straightforward to expose it to Python (and just not done yet due to lazyness/no need/etc.).
For obvious reasons, the Python3 module of SyTen is called pyten
.
Compile and install with make copy
, make sure that your make.inc
file defines a variable EXT_PYPDIR
denoting a writable folder. Also make sure that this folder is contained in your PYTHONPATH
environment variable.
Then, you can either use
or
to use the library from Python.
To call a C++ function from Python, Python needs to learn about this function. The shared library cpp_pyten.so
defined in cpp_pyten.cpp
hence calls various init_*()
functions which make C++ functions available to Python one-by-one. These init_*()
functions are distributed over various .cpp
files in the subdirectories here, corresponding loosely to the subdirectories of the main toolkit (with inc/
flattened away). Classes and enums are also made known to Python in a very similar fashion, numerous examples already exist.
However, it is also possible to extend the pyten
module/package with native Python code. To this end, a package pyten
is constructed as a directory tree (python_module/pyten
). This directory tree firstly contains an __init__.py
file. This file is executed/parsed when the pyten
module is imported. In particular, it exposes the C++
functions defined in the cpp_pyten.so
shared library directly below the pyten
namespace and, in addition, also reads them all in and adds them to the special __all__
variable. This way, if you say
e.g. the class TruncationType
is available in the ptn
namespace. Similarly, with
the class TruncationType
is injected in the global (current) namespace. On installation, the cpp_pyten.so
library is copied into this directory tree and the whole thing is copied into the PYMOD_DIR
directory (which is supposed to be contained in the PYTHONPATH
environment variable).
STL containers corresponding to built-in Python types are copied at the Python/C++ interface. This incurs a performance penalty and other unwanted things. In most cases, its still fine (e.g. vectors of MPOs transported this way into addLog()
) but sometimes this overhead may be very large. A solution is outlined in the pybind11 docs.
Unfortunately, Python doesn’t know templates. Worse, it has no generic programming facility available to translate C++ templates. Hence, the needed templates have to be instantiated one-by-one if they are to be available on the Python side. This is not a problem for MPS etc. which are only internally-templated, but is seriously annoying for generic tensors. At the moment, python_module/dense/dense.cpp
declares dense tensors of rank up to 8 with double
and std::complex<double>
scalars. It also declares all tensor product functions currently used in the toolkit library. Functions not declared there are not available to Python, e.g. it is not possible to declare a rank-13 dense tensor or a dense product of a rank-8 and rank-2 tensor (unless added there!). Unfortunately, simply declaring all possible products of tensors below e.g. rank-10 was not feasible due to memory and compile time constraints.
In a similar vein to the above, it is not possible to declare function templates in Python. Since prodD<>()
takes the number of summed-over legs as a template argument, it is necessary to transport this number as a type from C++ to Python to allow for correct overloading. For this reason, prodD()
on the Python side takes an additional first argument, an array of the same size as the summed over dimensions. That is
@code{.cpp} prodD<2>(a, b, {1, 2, -3, -5}, {-1, 1, -2, 2, -4}) @endcode
becomes
@code{.py} prodD([1,2], a, b, [1, 2, -3, -5], [-1, 1, -2, 2, -4]) @endcode
An alternative solution would be a Python-side wrapper which could insert this argument based on the contents of the c_a
and c_b
arrays.
Examples should be placed in python_module/examples
, furthermore, at least one lattice in lat/
already exists using the Pyten interface. Over time, new binaries in bin/
or lat/
may be implemented in Python, if this is suitable.
There are some small Python-only functions supplied in python_module/pyten
. Doxygen is not very good at extracting the docstrings from them. To improve this formatting, consider installing doxypy
(e.g. in Ubuntu's package repository) and then add a line
FILTER_PATTERNS = *.py=doxypy
to your build/doxygen.conf.inc
.