Janus: accessing elements of python tuples and arrays

I’ve unpacked some values from a Python tuple like this:

14 ?- py_call($Ss:next_step(),_Obs,[py_object]), _Obs =.. [-,T,Pos,Occ,Cov,Cts].
T = 789.0,
Pos = <py_ndarray>(0000018ff1d0f9f0),
Occ = <py_ndarray>(0000018ff1c86910),
Cov = <py_list>(0000018ff2e062c0),
Cts = <py_ndarray>(0000018ff3c47090),
Ss = <py_SurveySimulationGrid>(0000018fc8f17d60).

Pos is a 2d array of zeroes with a single 1 and I want to get the coordinates of the 1. I’m trying to do this with numpy where() but:

17 ?- py_call($Ss:next_step(),_Obs,[py_object]), _Obs =.. [-,T,Pos,Occ,Cov,Cts], py_call(numpy:where(Pos == 1),XY).
ERROR: Domain error: `py_term' expected, found `<py_ndarray>(0000018ff2dfcb10)==1'
ERROR: In:
ERROR:   [13] janus:py_call(numpy:where(...),_5007164)
ERROR:   [11] toplevel_call(user:user: ...) at c:/program files/swipl/boot/toplevel.pl:1318
ERROR:
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.
^  Exception: (4) setup_call_cleanup('$toplevel':notrace(call_repl_loop_hook(begin, 0)), '$toplevel':'$query_loop'(0), '$toplevel':notrace(call_repl_loop_hook(end, 0))) ? abort
% Execution Aborted

More generally I have no idea how to access the elements of tuples and arrays positionally, in Janus. For example that’s why I’m unpacking _Obs above with univ (also because it has ‘-’ as a functor and I want to get to the tuple values).

Suppose I try to do it like this, as I would normally in Python:

17 ?- py_call($Ss:next_step(),_Obs,[py_object]), _Obs =.. [-,T,Pos,Occ,Cov,Cts], py_call(Pos[1][1],El)
.
ERROR: Syntax error: Operator expected
ERROR: py_call($Ss:next_step(),_Obs,[py_object]), _Obs =.. [-,T,Pos,Occ,Cov,Cts], py_call(Po
ERROR: ** here **
ERROR: s[1][1],El) .

That’s not allowed. But then how do I access a tuple or array elements by index?

In both cases, the root of the problem is that we can only evaluate functions (or methods, but these are basically the same). I’m not sure about the nympy:where() case. The Pos == 1 should be a condition, but I do not know what that is in Python/numpy speak.

Getting an array element is written in Python as a[i], but that is just syntactic sugar for a.__getitem__(i) and we can call that from Prolog.

In general, I think you should write a Python function to get the data into a shape that you want from Prolog. That also holds the other way around.

Possibly we should consider a better interface over numpy. For example:

?- py_call(numpy:array([1,2,3]), X, [py_object]).
X = <py_ndarray>(0xfffec0192610).

?- py_call($X:'__getitem__'(2), V).
V = <py_int64>(0xfffec0161410),
X = <py_ndarray>(0xfffec0192610).

Now I still need some method to get to the actual integer. I was thinking about adding a method, e.g., __prolog__() to a Python object that would do the conversion to a meaningful Prolog object. But, py_int64 is a class to which we cannot add methods (forgot the Python name for that) and there is no point in subclassing as numpy simply creates instances of py_int64.

The alternative is possibly some Prolog definition that tells the system how to represent these values?

1 Like

Ah, thanks. I was looking all over the place for this information but I couldn’t find it.

That’s how I’m doing it for now, but that means I have to maintain a separate Python file to go along with my Prolog file and it’s not the end of the world but I was hoping to avoid that. On the other hand, writing Python in Python is more natural than trying to fit everything in py_call() and similar so that’s OK.

Possibly. I wonder if it’s just a matter of waiting for good practices to arise in the community. For the time being if I search online for some hints on how to use Janus I only find a few comments on this server (a couple by myself) and the official documentation. I’m hoping that eventually more user generated content will start to appear from folks who know both Python and Prolog well. Then I can stop bothering the server with silly questions :slight_smile:

There is py_module/2 for that purpose. That keeps the code nicely together.

Janus is still quite new. It takes time to get mature and problems like these are the way to get there. For quite some Python libraries it all works nicely. For others it looks clumsy. numpy is one of these :frowning: One way out is of course to write a Prolog friendly layer on top. If numpy is the real target we should probably consider a native binding to numpy as that most likely a lot faster. For the record, I wrote such a thing for ROS2 (the robotics protocol). The Prolog interface is a lot faster than the Python binding and thus even more so then Prolog → Python → ROS. It is more work though :frowning:

1 Like

Thanks! I need to explore the Janus interface better.

I think as long as it is possible to call a Python file from Prolog there is no reason to complain that Janus doesn’t have specific syntax for all Python libraries. After all what will you do, anticipate every future library? I think Janus is already extremely useful the way it is now. Chatter from the ILP community tells me that’s not just me who thinks so!

The ROS2 interface is really interesting to me because I work with some roboticists and they use it extensively, as expected. I can tell that it is a lot of work though so if my PIs are in a hurry for me to use it I can always tell them to spare some of our funding to sponsor the project :slight_smile:

1 Like

It is a bit sleeping project as the funding stopped, but I’m happy to revive it if there is new funding.

1 Like

I’m not promising anything but if we end up using it I’ll certainly make a point of suggesting it.

Thanks. I didn’t know Python had arrays as data types. But I also read they are not built-in (like lists, dictionaries, tuples, …): one has to import them from “specialized” modules (like numpy) before working with them