# Save and Load Knowledgebase

**URL:** <https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755>\
**Category:** Help!\
**Created:** [March 22, 2021, 6:31am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755 "2021-03-22T06:31:50Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![aminemarref](https://avatars.discourse-cdn.com/v4/letter/a/7feea3/32.png) [@aminemarref](https://swi-prolog.discourse.group/u/aminemarref)\
**Post date:** [March 22, 2021, 6:31am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/1 "2021-03-22T06:31:50Z")

</div>

Hello,

I would like to add a “save knowledgebase” and “load knowledgebase” functionality to my program.

I would like to be able to save the knowledgebase of my program (contains both dynamic and thread\_local predicates) to a list e.g.  
`knowledgebase_snapshot(I, L)`  
where `L` is a list of all predicates in the knowledgebase when `knowledgebase_snapshot/2` is asserted, and `I` being an ID.

I then would like to be able to load a knowledgebase which overwrites the current knowledgebase using the already-saved `knowledgebase_snapshot(I, L)`.

My program’s execution is roughly the following:

- Load the graph-based problem to solve, and perform some work resulting in asserting new predicates in the knowledgebase.
- “Save the knowledgebase”.
- Make some “smart” manipulations of the graph, discover useful facts, and assert them in the knowledgebase.
- Attempt solving the problem: if problem is solved or a timeout is reached, we are done; else “Load the knowledgebase” and go back to the previous step.

It is quite late for me to change my program logic and avoid using assertions, and I am not even sure if I can do that with my Prolog skills.

So what is available in SWI’s toolbox to enable me to do the save and load functionality?

I thought about exporting the result of [listing/0](https://www.swi-prolog.org/pldoc/man?predicate=listing/0) to a file (save) and consulting it (load), but I cannot afford a lot of file IO wasted time since the number of my “smart” manipulations requiring load and save is not exactly small.

I also thought about creating lists that store the arguments of my dynamic predicates (the save functionality); and when I want to load the knowledgebase, I retract all my dynamic predicates and re-assert them by looking up the saved lists (the load functionality).

Any insights will be appreciated.

Many thanks.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 7:26am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/2 "2021-03-22T07:26:18Z")

</div>

> [@aminemarref](#):
>
> Attempt solving the problem: if problem is solved or a timeout is reached, we are done; else “Load the knowledgebase” and go back to the previous step.

You can do that using transaction/1 🙂 Does require the development versions.

> [@aminemarref](#):
>
> I thought about exporting the result of [listing/0](https://www.swi-prolog.org/pldoc/man?predicate=listing/0) to a file (save) and consulting it (load)

That works, but better simply use write\_canonical/1 to dump the facts and just read\_term/2 with assertz/1 to restore the data. That is a lot faster. In theory we could provide access to the primitives that write .qlf files to dump the clauses as VM code and load it. The save is about as expensive (rough guess), but the loading is about 20 times faster.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [March 22, 2021, 9:02am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/3 "2021-03-22T09:02:13Z")

</div>

I would have expected [library(persistency)](https://www.swi-prolog.org/pldoc/man?section=persistency) to be part of the answer.

Are there reasons you are not recommending [library(persistency)](https://www.swi-prolog.org/pldoc/man?section=persistency) ?

---

<div class="post-metadata">

**Author:** ![aminemarref](https://avatars.discourse-cdn.com/v4/letter/a/7feea3/32.png) [@aminemarref](https://swi-prolog.discourse.group/u/aminemarref)\
**Post date:** [March 22, 2021, 12:07pm UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/4 "2021-03-22T12:07:09Z")

</div>

Thank you Jan.

I am trying [transaction/1](https://www.swi-prolog.org/pldoc/doc_for?object=transaction/1) with the following program.

```
:- dynamic p1/1.
:- dynamic p2/1.

program:-
	assert(p1(a)),	
	assert(p2(b)),	
	transaction(goal).	
	
goal:-
	assert(p1(c)),
	p2(z).
	
goal:-
	assert(p1(d)),
	p2(b).		

```

The program `program` basically does stuff until the point `goal`. Then it makes a single assert, after which it continues execution (calls `p2/1`).

What I need is that if the first choice of `goal/0` fails, the second choice starts with only `p1(a)` and `p2(b)` asserted. However, in this case, `p1(c)` is also asserted as shown in the the following.

```
?- program.
true.

?- listing(p1/1).
:- dynamic p1/1.

p1(a).
p1(c).
p1(d).

true.

```

As can be seen, the knowledgebase – when executing the second choice of `goal/0` – already contains `p1(c)`, but I want it to discard all asserts made in the first choice of `goal/0`.

Am I using [transaction/1](https://www.swi-prolog.org/pldoc/doc_for?object=transaction/1) correctly?

Cheers.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 1:24pm UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/5 "2021-03-22T13:24:00Z")

</div>

I was more thinking about the code below. Note that the first clause commits as you cannot decide to accept a transaction and later roll it back anyway.

```prolog
program:-
	assert(p1(a)),	
	assert(p2(b)),	
	goal.	
	
goal:-
	transaction((assert(p1(c)),
	             p2(z))),
    !.
	
goal:-
	assert(p1(d)),
	p2(b).	

```

Note that this may also help:

```prolog
assert_backtrackable(Term) :-
    ( assertz(Term, Ref)
    ; erase(Ref),
         fail
    ).

```

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 7:28pm UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/6 "2021-03-22T19:28:34Z")

</div>

> [@EricGT](#):
>
> Are there reasons you are not recommending [library(persistency)](https://www.swi-prolog.org/pldoc/man?section=persistency) ?

It doesn’t deal with saving a snapshot and returning to this. It targets at tracking the changes to a dynamic predicate and maintain a file that allows for restoring the final state.

P.s. It should probably also be extended to use the assert/retract hooks to simply track normal assert/retract instead of defining its own modification operations. That would simplify adding/removing persistency.

---

<div class="post-metadata">

**Author:** ![aminemarref](https://avatars.discourse-cdn.com/v4/letter/a/7feea3/32.png) [@aminemarref](https://swi-prolog.discourse.group/u/aminemarref)\
**Post date:** [March 23, 2021, 5:30am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/8 "2021-03-23T05:30:10Z")

</div>

Wow, awesome, you guys thought about implementing everything 🙂 Transaction is working for my purposes!

I have a few of questions:

- Does transaction work with both dynamic and thread\_local predicates?
- Can it co-exist with tabling? Maybe the answer here is obvious, but because of my lack of a solid background on tabling and transactions, I am imagining them as two beasts put next to each other and I wonder if they can work together.
- Does _“Run Goal as [once/1](https://www.swi-prolog.org/pldoc/man?predicate=once/1) in a transaction”_ in the help page on [transaction/1](https://www.swi-prolog.org/pldoc/doc_for?object=transaction/1) mean that I can only get one solution for the goal inside the transaction? Why is that?

I am also keen on the `assert_backtrackable/1` you kindly suggested:

```
assert_backtrackable(Term) :-
    ( assertz(Term, Ref)
    ; erase(Ref),
         fail
    ).

```

The page on [assertz/2](https://www.swi-prolog.org/pldoc/man?predicate=assertz/2) says that when assertion is made, a reference is created and unified with some handle that can be used to access the clause through e.g. [erase/1](https://www.swi-prolog.org/pldoc/man?predicate=erase/1). But when I attempt to use it, I get a warning about singleton `Ref` at consult-time and and error about “Arguments are not sufficiently instantiated” in [erase/1](https://www.swi-prolog.org/pldoc/man?predicate=erase/1) at runtime. Any thoughts?

---

<div class="post-metadata">

**Author:** ![grossdan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/grossdan/32/23_2.png) [@grossdan](https://swi-prolog.discourse.group/u/grossdan)\
**Post date:** [March 23, 2021, 5:53am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/9 "2021-03-23T05:53:03Z")

</div>

> [@jan](#):
>
> ```prolog
> assert_backtrackable(Term) :-
> ( assertz(Term, Ref)
> ; erase(Ref),
> fail
> ).
> 
> ```

I may be blanking here on something – but how (or why) is Ref bound when the execution thread gets to erase after backtracking away from assertz(Term, Ref) …

What is the general mechanism of variable binding doing here

Edit:

There are a few things going on – at least in theory:

1. assertz has a side effect – an asserted Term w/ an allocated reference, that is not undone upon backtracking
2. Ref is bound to that reference and the binding is not undone
3. erase undoes the term referred to by the bound Ref

What is the status of Ref after erase – is it still bound to the now, non-existing Term? – say, if yet another backtracking branch is disjointedly added.

Dan

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 23, 2021, 7:42am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/10 "2021-03-23T07:42:33Z")

</div>

> [@aminemarref](#):
>
> Does transaction work with both dynamic and thread\_local predicates?

AFAIK, yes. Transactions are thread-specific anyway, so this is more or less automatic.

> [@aminemarref](#):
>
> Can it co-exist with tabling?

Not yet. Well, there are some bits and pieces I’m exploring with people at CMU. Ultimately I’d like that to work seamlessly. There are quite a few scenarios though and it isn’t all that clear how these should be handled.

> [@aminemarref](#):
>
> I can only get one solution for the goal inside the transaction? Why is that?

Yes. Why? Well, the interaction between going in/out of the transaction and rollback/commit seem pretty complicated. It might be doable but it certainly wasn’t the original intend with transactions. They are primarily there to get atomic state transitions. But, there are alternatives: start a thread running a transaction and while inside the transaction, make it talk to a thread outside the transaction. Now we can exchange answers and ask the thread inside the transaction to backtrack, finish with success or failure, etc.

Another option might be combining engines and transactions. That might not be possible right now though ☹

> [@jan](#):
>
> ```prolog
> assert_backtrackable(Term) :-
> ( assertz(Term, Ref)
> ; erase(Ref),
> fail
> ).
> 
> ```

Sorry. Should be (still not tested)

```prolog
assert_backtrackable(Term) :-
    assertz(Term, Ref),
    ( true
    ; erase(Ref),
         fail
    ).

```

---

<div class="post-metadata">

**Author:** ![aminemarref](https://avatars.discourse-cdn.com/v4/letter/a/7feea3/32.png) [@aminemarref](https://swi-prolog.discourse.group/u/aminemarref)\
**Post date:** [March 23, 2021, 8:37am UTC](https://swi-prolog.discourse.group/t/save-and-load-knowledgebase/3755/11 "2021-03-23T08:37:05Z")

</div>

Many thanks Jan for the detailed explanation 🙂

Also, the backtrackable assert is working great.
