Term_variable/2 behavior from Python via swiplserver

What I get is self explanatory:

>>> pl.query("term_variables(q1(X, Y), P)")
[{'X': 'A', 'Y': 'B', 'P': ['A', 'B']}]

I would have expected [{'P': ['X', 'Y']}] and that is what I get from SWI itself:

?- term_variables(q1(X, Y), P).
P = [X, Y].

Any clue why it is creating new fresh variables A and B?

Thanks in advance.

The letters A, B, X, Y are arbitrary. The only thing you can get from them is that if they’re different uninstantiated variables, there’ll be different letters (that is

A=B, A==B.
A\==B, A=B % Because A=B is after the \== test. 

So, there’s no reason for the variables from the query to be the same “name” as what is passed in to the query - the result could just as easily have been:

>>> pl.query("term_variables(q1(X, Y), P)")
[{'X': '_123', 'Y': '_456', 'P': ['_123', '_456']}]

Which interface are you using? When I tried your example I got:

>>> import janus_swi as janus
>>> list(janus.query("term_variables(f(x,y), P)"))
[{'truth': True, 'P': []}]
>>> list(janus.query("term_variables(f(x,Y), P)"))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/peter/src/swipl-devel/packages/swipy/venv/lib/python3.11/site-packages/janus_swi/janus.py", line 205, in __next__
    rc = _swipl.next_solution(self.state)
janus.PrologError: Arguments are not sufficiently instantiated

(I might be wrong; Jan can correct me if I am)

Thanks for your answer. Yes I agree that the answer is “equivalent”, but it makes life harder as I need to recover the original name in Python.

Sorry I should have been more complete in my code. I am using swiplserver

$ python
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from swiplserver import PrologMQI, PrologThread
>>> pl = PrologMQI().create_thread()
>>> pl.query("term_variables(q1(X, Y), P)")
[{'X': 'A', 'Y': 'B', 'P': ['A', 'B']}]

I was not sure what is the difference between swiplserver and Janus but I found a good explanation from Jan here. Seems swiplserver is more powerful/flexible, but with a bit more overhead.

Janus has become a lot more mature and stable. I think it is now the superior way to combine Prolog and Python in most scenarios. The main drawback is that, like the Java interface JPL and the R interface as the real package, linking the two systems is complicated on some platforms. Python makes this worse than the other two mentioned because the C API is not stable and each new Python release comes with a new shared object/DLL version.

I think that if the linking problem is manageable in the scenario you anticipate, Janus is the way to go. If not, you may try to help improve on this issue. Notably we should eventually have good PyPi binaries. As is, there is only the source (which needs to be updated).

1 Like

From this example, it appears that swiplserver can return unbound values but Janus can’t. Is there a way around this?
(BTW, the documentation has a bindings= keyword for janus.query() but that seems to have changed to inputs=)

The Janus interface only maps a selected number of Prolog terms to Python data. That was a deliberate choice as it allows us to wrap Prolog data in compound terms (that otherwise also results in a domain error) to indicate the translation we want. For example, py_set(List) creates a Python set object rather than a list.

That makes Janus less suitable as a “Prolog terminal in Python”, but more suitable for getting the data you want from Prolog in the format you want in Python. Logical variables are an example of things that map poorly to Python.

You can of course create a Prolog wrapper predicate that maps any Prolog term into a term that is acceptable by Python. You can also wrap the Prolog argument into prolog(Term), which passes a handle to (a copy of) the Prolog term as an instance of janus.Term(). As yet, janus.Term() is more of a placeholder that allow you to get arbitrary terms from Prolog and pass them (a copy) back to Prolog. The only other thing you can do is get a string from janus.Term().

I don’t know whether this is the best choice. I came out of the discussions with the XSB and Ciao teams. I am inclined to think this is the best choice. It is a different choice than the WASM version, but the same choice was long ago made for SWI-Prolog’s xpce graphics subsystem.

Thanks. Fixed (the docs) in the git repo.

1 Like

Thanks both!

I would have to look into Janus more, wasn’t aware and I thought the swiplserver was the way to Python; it has been working very well. I am using it on a generic automarker for Prolog projects, works really well. Within that application, one could argue I am using swiplserer as a (powerful) “Prolog terminal in Python”; I do need mapping of Prolog variables into Python for example.

Will need to look closer and see if I can get the equivalent using Janus…

What is better depends. swiplserver has the advantage that you have no linking problems connecting the two and far more relaxed version dependencies. The disadvantages are a high overhead and limited interaction. Janus is harder to connect, depending on the platform. Once connected though, the interface is fast, deals with mutual (recursive) calls and allows for smooth interaction between Python and Prolog threads. Python calling Prolog releases the Python GIL and thus multiple Python thread can call Prolog and get multiple cores active. The interface allows both for embedding Python code in Prolog sources as Prolog source in Python in addition to loading from files.

I have been working with direct embedding in the past. This was typically disappointing because the interfaces were not good enough and the systems too unstable while debugging a crash in such a hybrid environment can be tedious. E.g, if memory gets corrupted, who did it? All this has improved a lot though. The systems are more robust, the interfaces are richer and the debugging tools have improved a lot.

When Theresa Swift (from XSB) came with the idea I was skeptical at first, but gradually became convinced.