0

I have a small program that runs in python. Basically, it has a pretty simple loop: Query a Prolog program, do something with the result, change some state back in Prolog.

I have written a logical way (no mutations) to change a state in Prolog. The queries use a state as their argument, and result in a new state. The question is, where do I store the new state?

I can pass it back and forth from Prolog to Python, but as the state grows large it might become problematic. I can also store a global variable, but that seems like a bad solution.

This is the basic flow of my program:

some_query(OldState, NewState) :- 
    % do some stuff here

python code:
state = '[]'
while Not_Exit:
    query = MyLogic(state) # state is not actually used here (python-wise), just to pass it to prolog
    result = prologBridge.Query(query)
    state = result["NewState"] # how do I change that in Prolog without passing the state back and forth?

EDIT: The state contains a list of terms, and state changes revolve around adding and removing terms. For example. S1 = [T1, T2, T3] changes into S2 = [T2, T3, T4].

  • Can you be more specific about state. Will it have to survive exiting Prolog? (it needs to be saved to a file). Will it have to available to all goals and not just the goal it was created? Will it hold lots of data, thousands of items? Is the structure of the data simple, e.g. scalar, or complex like a record with many properties and possibly nested? I am asking because there are many was to answer this but I don't want to write a few chapters on it in a StackOverflow answer. This i s not the place for such informative answers. – Guy Coder Oct 22 '20 at 18:31
  • **currently** it is just a list of simple terms representing some simulated environment, such as connected(node1, node2), expanded(node1), agentAt(agent1, node1), etc. It does not contain that many terms yet, but you can see how it might grow. saving is a non-issue because worst-case I can implement it in Python and assign it once when the program starts. The problem is that the state is mutable by it's nature – Null Terminator Oct 23 '20 at 08:19
  • Based on the info in the comment I would use [library(persistence)](https://www.swi-prolog.org/pldoc/man?section=persistency) and [EDCGs](https://www.swi-prolog.org/pack/list?p=edcg). If you have more questions about this, then please ask at the [SWI-Prolog forum](https://swi-prolog.discourse.group/) – Guy Coder Oct 23 '20 at 09:16

2 Answers2

0

Possible solutions:

  1. Store state or other facts in a file that you reconsult in Prolog.
  2. Use data bases to persist state
  3. Keep Prolog running as a separate process in the background, then current state should be available.

What is your choice ? what about functional programming ?

peter.cyc
  • 1,763
  • 1
  • 12
  • 19
  • Keeping the state is possible by passing it back in forth as in the example code (the query depends on the state), I was wondering if there is a more Prolog way of doing things (without passing it back and forth) – Null Terminator Oct 22 '20 at 18:17
  • @NullTerminator If the Prolog process is kept running, you can just "assert new facts" in the Prolog database using [`assertz/1`](https://eu.swi-prolog.org/pldoc/doc_for?object=assertz/1) and friends. In effect, this "extends the logic program" with more facts and rules (i.e. the program self-modifies). The next query will consider these updates. – David Tonhofer Oct 22 '20 at 19:21
0

As long as each query runs in the same Prolog session (i.e., you don't start a new Prolog each time), you can use Prolog's built-in database. This is a store of "dynamic" Prolog clauses that can be modified at runtime.

You declare a dynamic predicate using a :- dynamic directive. You remove old facts using rectract or retractall and assert new ones using asserta or assertz. Querying a dynamic predicate is done by calling it as usual.

For example, you can declare your state like this:

:- dynamic mystate/1.

A predicate querying the state, computing the next one, and updating the database, looks like this:

query_and_update_state :-
    mystate(OldState),
    some_query(OldState, NewState),
    write('new state: '),
    write(NewState),
    nl,
    retractall(mystate(_)),
    asserta(mystate(NewState)).

This assumes the existence of your some_query predicate. Some initial state must be set at the beginning as well. For example, if the state is just a counter:

init_state :-
    retractall(mystate(_)),
    asserta(mystate(0)).

some_query(Old, New) :-
    succ(Old, New).

Running this interactively:

?- init_state.
true.

?- query_and_update_state.
new state: 1
true.

?- query_and_update_state.
new state: 2
true.

?- query_and_update_state.
new state: 3
true.

?- query_and_update_state.
new state: 4
true.

Your Python code would call init_state once and then call query_and_update_state repeatedly, presumably for some side effect. (It's not clear to me from your question.)

Caveat: If you're worried about the costs of copying the state, you might not be happy about this either. Asserting a state and querying it both involve making a complete deep copy of the data structure. Such a Prolog-Prolog copy might indeed be cheaper than a Prolog-Python copy. But depending on your Python-Prolog bridge, maybe it doesn't make a deep copy and could be cheaper? I don't know.

Isabelle Newbie
  • 9,258
  • 1
  • 20
  • 32
  • I'm not worried about deep copying since the state is made of simple facts, but I suspect that shallow-copying a state consisting hundreds (if not thousand) facts in a loop is not optimal – Null Terminator Oct 23 '20 at 12:04