Variables with Janus

I’m using janus to call Prolog from Python code. I don’t understand why, if I write

janus.consult("rules", """
answer1 = janus.query_once("rule([1,1,0,0])")

output: {‘truth’: True}

which is correct, but if I use lists/tuple or numpy arrays as

import numpy as np

line = np.array([1,1,0,0])

janus.consult("rules", """

d = np.array([1,1,0,0])
answer1 = janus.query_once("rule(d)")

output: {‘truth’: False}
which is wrong.

How can I use variables in janus? What am I doing wrong?

Thank you for four time

Use "rule(D)". Prolog says that identifiers that start with a capital are variables. Now the result is a dict that holds the key D with the Python value that results from translating the Prolog result to Python. So, you must make sure Prolog binds the variable to something that can be converted to Python. If you do not care about the value of the binding, use _D, i.e., start the variable with an underscore.

Oh yes, thank you very much!
So is there the possibility to use string variables inside janus.consult? Because I have tried with:

r1 = "rule(b7,b1,-b5)"
r2 = "rule(b7,b1,-b5)"
r3 = "rule(b1,0,b3)"

janus.consult("rules", """

answer1 = janus.query_once("rule(X,0,b3)")

but in the line answer1 it gives me the error:" PrologError: ‘$rule’/3: Type error: db_reference' expected, found b3’ (an atom)"

This has little to do with Janus. You can use Python string interpolation, e.g.,

  janus.consult("rules", f"{r1}. {r2}.")

The error comes because rule/3 is a built-in that you are calling with invalid arguments.

But, this is not how you are supposed to use Janus. If you have Python data that you want in the Prolog database call assertz/1 through the API. In such cases I’d define a Prolog predicate that does the proper assert from simply arguments, e.g.

 assert_data(A,B,C) :- assert(data(A,B,C)).

Now you can call this, where a, b and c are (simple) Python data items.

                  {"A": a, "B":b, "C":c)})

The janus.consult() using a string is mainly there to allow you defining Prolog predicates that you need in the interface directly from Python.

ok, it is clear, thanks again

Sorry, I have just found this problem: if I write

import numpy as np

line = np.array([1,1,__,_]) # I put the underscore because there can be any value here
janus.consult(“rules”, “”"

D = np.array([1,1,0,0])
answer1 = janus.query_once(“r(_D)”)
E = np.array([0,1,0,0])
answer2 = janus.query_once(“r(_E)”)

I have the output:
{‘truth’: True}
{‘truth’: True}

but the second one should be False, because it’s not equal to line. What am I doing wrong?

This simply adds the Prolog fact r(line). The argument to consult is just a Python string. If you want anything expanded there you need Python string interpolation. I know about f"....{expression}...", but there is probably more to say about that. I am not a Python expert.

This D does not relate to anything.

This binds _E to line, but as you do not ask for the binding if the variable starts with an underscore, the answer is simply true.

Looks like you have some serious misconceptions about both Python and Prolog. I suggest reading some basics on Prolog such that you understand facts, rules and queries. Next, understand what you want to do in Prolog without Python and then get Python into the picture.

I’m sorry, I obviously didn’t explain myself well. I write here another version of the code, it returns the same (I tried):

import numpy as np

line = np.array([1,1,_,_])  
janus.consult("rules", """

D = np.array([1,1,0,0])
answer1 = janus.query_once("rule(D)")
E = np.array([0,1,0,0])
answer2 = janus.query_once("rule(E)")


{'truth': True, 'D': 'line'}
{'truth': True, 'E': 'line'}

I only would like to work with the variable line as a rule. If the variables E or D are equal to line it would return True, otherwise False. The problem is that it always returns True and I don’t understant why. I modified the code as you explained me some hours ago: non d letter, but D, otherwise it always returns False, because d is not a variable since it’s not in capital letter (as you have written above). The problem is that now it always returns True, but I don’t understand why

I’m sorry, I can only repeat my previous answer. Maybe someone else understands what you want? Surely the behaviour you show is what this code is supposed to do. And surely, this doesn’t make much sense …

The variables in Python and the variables in Prolog are completely different – you can’t expect rule(line) to reference your Python variable line … in fact, in Prolog, line is an atom, not a variable.

There’s an additional issue here: you’re using Numpy, and AFAIK, the Python-Prolog interface doesn’t know how to handle Numpy arrays.

Anyway, ignoring Numpy, if you want to pass the value of a Python variable to Python, you need to convert it to something the Prolog understands. So, if you have a Python variable s, you can pass its value to Prolog like this:

s = 'foo'
janus.consult("rules", f"rule('{s}').")

which, through Python interpolation, is the same as:

janus.consult("rules", f"rule('foo').")

(I put the single quotes in the call to janus.consule() as a simple way of allowing s to, for example, have a blank in it … however, this doesn’t handle situations such as having a single quote inside the value of s).

1 Like

Thank you,
I have read the documentation about the data conversion and, if I understood correctly, it’s best not to try to use lists, arrays, etc. directly, but only strings. Is it correct?

Let’s see what happens … janus.consult() takes a string, so:

>>> s = [1,'b']
>>> f"rule('{s}')."
"rule('[1, 'b']')."

That’ll give you a syntax error in Prolog and even if it didn’t (e.g., with [1,2,3]), isn’t what you want.

1 Like

Perfectly clear, thanks for all your help

The arrays are not the main issue (I think). The problem is that numpy does not use native Python numbers. So, we get

?- py_call(numpy:array([1,2,3]), R).
R = [<py_int64>(0x7ff1fc3292f0), <py_int64>(0x7ff1fc2e8230), <py_int64>(0x7ff1fd23be30)].

Here, <py_int64> says this is an instance of [numpy].int64, which is not a subclass of Python int. The Prolog interface has no way to know it would be better to turn this into an integer. We can handle them by using the item() method on the numpy data:

?- py_call(numpy:array([1,2,3]), _R), member(I, _R), py_call(I:item(), Value).
I = <py_int64>(0x7ff0f75fea70),
Value = 1 ;
I = <py_int64>(0x7ff0f75fe9f0),
Value = 2 ;
I = <py_int64>(0x7ff0f75feb50),
Value = 3.

I hadn’t thought about the item method, I’ll try, thank you!