This code implements a ready-to-use bi-directional interface to Python. As motivated by Theresa Swift, Python opens many doors for accessing resources such as graphics, machine learning and many more.
The API defined in this interface has been established as a PIP,
Prolog Improvement Proposal. When the PIP is finished and published
we will properly reference it. The main predicates and Python
functions of this interface are compatible with the XSB Python package
janus_xsb
. Both janus_swi
and janus_xsb
implement extensions
upon the agreed interface. For example, janus_swi
supports
SWI-Prolog dicts and defines thread synchronization between Prolog and
Python.
This GIT repository is a GIT submodule of the SWI-Prolog source
repository. As part of the SWI-Prolog source distribution it is used
to build library(janus)
, a Prolog library that embeds Python. This
same module can be used stand-alone to build the Python package
janus_swi
that embeds Prolog into Python. Loaded either way, Janus
is the same and allows for mutually recursive calls between Prolog and
Python.
If this repository is used to build the Python pip package
janus_swi
, we can load SWI-Prolog into Python and call predicates.
For example:
python
>>> import janus_swi as janus
>>> janus.query_once("writeln('Hello world!')")
Hello world!
{'truth': True}
>>>
The Python package is available from PyPi as
janus-swi. We currently
provide a few wheels for Windows. The binaries in the Windows
wheel probably supports all Python and Prolog versions that are also
supported by the source. The package can be installed using pip
from source on any system with CPython 3.6 or later, SWI-Prolog 9.1.12
or later and a C compiler. For compiling the C code, GCC, Clang and
VS2022 have been tested. Thus, normally the package can be installed
using
pip install janus-swi
SWI-Prolog is selected by finding swipl
on the executable search
path. If swipl.exe
is not in %PATH%
on Windows the Windows
registry is examined to find SWI-Prolog.
If you installed SWI-Prolog from source, it is advised to install
Janus from the packages/swipy
directory in the Prolog source. The
package can be installed from within this directory using
pip install .
Configuration and installation of library(janus)
which embeds Python
into Prolog is handled by the normal Prolog configuration. Building
the interface requires the libraries and C headers for Python
embedding to be installed. Below are the commands for installing the
embedded Python engine for Ubuntu and Fedora Linux.
apt install python3 libpython3-dev # Ubuntu
dnf install python3-devel # Fedora
On MacOS, these files are included in the Homebrew and Macports versions of Python
On Windows, these files are included in the default installer.
Configuration requires Python to appear in %PATH%
.
After successful installation, running py_version/0
should result in
printing relevant information on the embedded Python system.
?- py_version.
% Janus embeds Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0
Ongoing work to get SWI-Prolog working under Conda can be found at https://github.com/SWI-Prolog/swi-prolog-feedstock. Eventually, this work shall be merged with https://anaconda.org/conda-forge/swi-prolog
As is, https://github.com/SWI-Prolog/swi-prolog-feedstock has been used to build the full SWI-Prolog system with Janus interface on Linux, MacOS and Windows.
SWI-Prolog comes bundled with MQI. MQI is initiated from Python and starts SWI-Prolog as a server. It allows calling Prolog from Python. Separated using networking, this approach is easy to install and runs with any Python version. It does not allow calling Python from Prolog, the primary reason for the existence of this package. Using networking, the latency is relatively high.
The pyswip interface uses the Python ctypes to embed Prolog into Python. Only relying on ctypes, the package is a fully portable Python package that supports a wide range of Python and Prolog versions.
Unlike this package, embedding Python into Prolog is not possible. pyswip calls Prolog, similarly than janus, using a string. However, where janus allows passing input to the goal as a dict that is transferred using the C API rather than strings, pyswip also passes the input as a string. This is slower, sensitive to injection attacks and complicated because the user is responsible for generating valid Prolog syntax. Calls from Prolog to Python are possible by defining a Prolog predicate from Python. This only seems to support deterministic predicates and it cannot pass data back to Prolog. Janus supports calling Python functions and methods directly and supports enumerating Python iterators and generators as non-deterministic goals using py_iter/2.
The overhead of Janus is roughly 5 times less than pyswip. As pyswip still sustains over 100K calls per second this is irrelevant to many applications.