# Unexplained behaviour wrt the well founded semantics - Part 4

**URL:** <https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377>\
**Category:** Help!\
**Created:** [September 3, 2021, 7:37pm UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377 "2021-09-03T19:37:58Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Christophe\_Rey](https://avatars.discourse-cdn.com/v4/letter/c/c6cbf5/32.png) [@Christophe\_Rey](https://swi-prolog.discourse.group/u/Christophe_Rey)\
**Post date:** [September 3, 2021, 7:37pm UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/1 "2021-09-03T19:37:58Z")

</div>

Hi Jan, Hi all,

I come back with something else I do not understand related to the well founded semantics (WFS).

I’m using version: 8.3.29-4-g836862aeb

I would like to differenciate atoms that are undefined (according to WFS), from those that are true, from those that are false. Thus I define the following predicates:

```prolog
isUndefined(Atom):- Atom,tnot(Atom).
	
isTrue(Atom):-	\+ (Atom,tnot(Atom)), Atom.
	
isFalse(Atom):- \+ (Atom,tnot(Atom)), \+Atom.

```

The idea is the following:

- isUndefined(Atom) returns true if and only if Atom returns undefined
- isTrue(Atom) returns true if and only if Atom returns true
- isFalse(Atom) returns true if and only if Atom returns false

The implementation given above seems to be correct and to behave well with a ground Atom. When Atom is not ground however, the expected result is not obtained as illustrated below. I have the following program:

```prolog
:- table p/1.

q(1).
r(3).	
	
p(X) :- r(X),tnot(p(X)).
p(X) :- q(X).	

```

What I’m getting is:

```prolog
?- p(1). p(2). p(3).

true.

false.

% WFS residual program
    p(3) :-
        tnot(p(3)).
p(3).

?- isTrue(p(1)). isFalse(p(1)). isUndefined(p(1)).

true.

false.

false.

?- isTrue(p(2)). isFalse(p(2)). isUndefined(p(2)).

false.

true.

false.

?- isTrue(p(3)). isFalse(p(3)). isUndefined(p(3)).

false.

false.

% WFS residual program
    p(3) :-
        tnot(p(3)).
(tnot(p(3)), p(3)).

?- findall(X,p(X),Result).
Result = [3, 1].

?- isTrue(p(X)).
false.

```

Everything is correct except the last result where I expected

```prolog
X = 1

```

Is there something I have not understood ?  
Thanks in advance

Best regards

---

<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:** [September 4, 2021, 8:13am UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/2 "2021-09-04T08:13:27Z")

</div>

> [@Christophe\_Rey](#):
>
> Everything is correct except the last result where I expected

It is not so hard to explain:

```prolog
?- p(X), tnot(p(X)).
% WFS residual program
    p(3) :-
        tnot(p(3)).
X = 3,
(tnot(p(3)), p(3)) ;

```

And thus the NAF negation `\+/1` is false. I thought you could maybe work around this using not\_exists/1, but this too seems to fail. Don’t know why (yet). What does work is

```prolog
isTrue(Atom):-	Atom, \+ (Atom,tnot(Atom)).

```

A simpler way to distinguish undefined from true results is to use call\_delays/2.

---

<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:** [September 4, 2021, 12:08pm UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/3 "2021-09-04T12:08:28Z")

</div>

The most sensible definitions I can come up with are

```prolog
isUndefined(Atom) :- call_delays(Atom, Delays), Delays \== true.
isTrue(Atom) :- call_delays(Atom, true).
isFalse(Atom) :- \+ isTrue(Atom).

```

That works fairly well, except that isFalse/1 requires a ground argument to avoid floundering. You need constructive negation. sCASP can do that. Still learning, I tried. sCASP on

```prolog
q(1).
r(3).

p(X) :- r(X), not p(X).
p(X) :- q(X).

```

gives “no models” on every query though because our knowledge base is inconsistent. If we delete the inconsistent first clause for p/1 we get

```prolog
?- not p(X).

MODEL:
{ not q(X | {X \= 1}), not p(X | {X \= 1}) }

BINDINGS: 
X \= 1 ?

```

Trying to get my head around the advantages and disadvantages of WFS, ASP and sCASP …

---

<div class="post-metadata">

**Author:** ![Christophe\_Rey](https://avatars.discourse-cdn.com/v4/letter/c/c6cbf5/32.png) [@Christophe\_Rey](https://swi-prolog.discourse.group/u/Christophe_Rey)\
**Post date:** [September 4, 2021, 12:38pm UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/4 "2021-09-04T12:38:11Z")

</div>

Thank you very much Jan for your quick answer !

> ```prolog
> ?- p(X), tnot(p(X)).
> % WFS residual program
> p(3) :-
> tnot(p(3)).
> X = 3,
> (tnot(p(3)), p(3)) ;
> 
> ```
> 
> And thus the NAF negation `\+/1` is false.

I understand my mistake !

> What does work is
> 
> ```prolog
> isTrue(Atom):-	Atom, \+ (Atom,tnot(Atom)).
> 
> ```

Clever !

> A simpler way to distinguish undefined from true results is to use [call\_delays/2](https://www.swi-prolog.org/pldoc/doc_for?object=call_delays/2).

I realize I am not comfortable with this predicate call\_delays, and also with others like call\_residual\_program/2, delays\_residual\_program/2, not\_exists/1, tabled\_call/1, current\_table/2. So I have to go back to work. 🙂

Thank you !

---

<div class="post-metadata">

**Author:** ![Christophe\_Rey](https://avatars.discourse-cdn.com/v4/letter/c/c6cbf5/32.png) [@Christophe\_Rey](https://swi-prolog.discourse.group/u/Christophe_Rey)\
**Post date:** [September 4, 2021, 12:49pm UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/5 "2021-09-04T12:49:39Z")

</div>

I think I have understood the purpose of call\_delays now. Just to be sure with my own words : call\_delays(Atom, Delays) is globally true, only if we have: (Delays is true) implies (Atom is true). Is that ok ?

> The most sensible definitions I can come up with are
> 
> ```prolog
> isUndefined(Atom) :- call_delays(Atom, Delays), Delays \== true.
> isTrue(Atom) :- call_delays(Atom, true).
> isFalse(Atom) :- \+ isTrue(Atom).
> 
> ```

I agree with your definitions. I just cannot see the difference with:

```prolog
isUndefined(Atom):- Atom,tnot(Atom).
isTrue(Atom):- Atom, \+ (Atom,tnot(Atom)).
isFalse(Atom):- \+Atom.

```

Is it a performance issue ?  
Anyway, there is still the necessity to give a ground argument to isFalse/1.

> You need constructive negation. sCASP can do that. Still learning, I tried. sCASP on
> 
> ```prolog
> q(1).
> r(3).
> 
> p(X) :- r(X), not p(X).
> p(X) :- q(X).
> 
> ```
> 
> gives “no models” on every query though because our knowledge base is inconsistent. If we delete the inconsistent first clause for p/1 we get
> 
> ```prolog
> ?- not p(X).
> 
> MODEL:
> { not q(X | {X \= 1}), not p(X | {X \= 1}) }
> 
> BINDINGS: 
> X \= 1 ?
> 
> ```

This is very interesting ! I was not aware of sCASP and its constructive negation. It would be great to include it inside WFS… Maybe some papers are dealing of this (?).

> Trying to get my head around the advantages and disadvantages of WFS, ASP and sCASP …

I would be very interested in reading about your conclusions about this topic. In a future paper ?

Thank you very much Jan !

---

<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:** [September 4, 2021, 1:08pm UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/6 "2021-09-04T13:08:40Z")

</div>

> [@Christophe\_Rey](#):
>
> call\_delays(Atom, Delays) is globally true, only if we have: (Delays is true) implies (Atom is true). Is that ok ?

I think so, though I’m not 100% sure I get what you are saying. call\_delays/2 is true if call(Atom) is true under the conditions imposed by Delays. If Delays is `true`, this means call(Atom) is unconditionally true. Otherwise it is an opaque representation of the _delays_ (i.e., condition under which the atom is true). Does that make sense?

> [@Christophe\_Rey](#):
>
> Is it a performance issue ?

Yes. Although the impact may be fairly small as tnot always works on an already complete table. You’d have the measure (and maybe performance is irrelevant to you anyway 🙂 ).

> [@Christophe\_Rey](#):
>
> `isTrue(Atom):- Atom, \+ (Atom,tnot(Atom)).`

Of, this is the same as `isTrue(Atom) :- Atom, \+ tnot(Atom).` 🙂

> [@Christophe\_Rey](#):
>
> It would be great to include it inside WFS…

CASP, implementing stable model semantics and tabling with WFS are quite different beasts. I not a logician …

> [@Christophe\_Rey](#):
>
> Maybe some papers are dealing of this (?).

Search Google Scholar for “scasp gupta” and you have most papers. sCASP was written by Joaquin Arias. Gopal Gupta has co-authored most of the papers concerning sCASP and its predecessor sASP. The SWI-Prolog port as at [GitHub - JanWielemaker/sCASP: top-down interpreter for ASP programs with constraints](https://github.com/JanWielemaker/sCASP). The original version in Ciao is primarily a stand-alone system. I’m working on turning this into an embedded language, i.e., allow solving a specific query under sCASP semantics.

---

<div class="post-metadata">

**Author:** ![Christophe\_Rey](https://avatars.discourse-cdn.com/v4/letter/c/c6cbf5/32.png) [@Christophe\_Rey](https://swi-prolog.discourse.group/u/Christophe_Rey)\
**Post date:** [September 5, 2021, 7:18am UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/7 "2021-09-05T07:18:48Z")

</div>

> [@jan](#):
>
> > [@Christophe\_Rey](#):
> >
> > call\_delays(Atom, Delays) is globally true, only if we have: (Delays is true) implies (Atom is true). Is that ok ?
> 
> I think so, though I’m not 100% sure I get what you are saying. call\_delays/2 is true if call(Atom) is true under the conditions imposed by Delays. If Delays is `true`, this means call(Atom) is unconditionally true. Otherwise it is an opaque representation of the _delays_ (i.e., condition under which the atom is true). Does that make sense?

Yes !

> [@jan](#):
>
> > [@Christophe\_Rey](#):
> >
> > Is it a performance issue ?
> 
> Yes. Although the impact may be fairly small as tnot always works on an already complete table. You’d have the measure (and maybe performance is irrelevant to you anyway 🙂 ).

Yes, performance is of great interest for me. So I keep your solution !

> [@jan](#):
>
> > [@Christophe\_Rey](#):
> >
> > `isTrue(Atom):- Atom, \+ (Atom,tnot(Atom)).`
> 
> Of, this is the same as `isTrue(Atom) :- Atom, \+ tnot(Atom).` 🙂

You’re right, of course.

> [@jan](#):
>
> > [@Christophe\_Rey](#):
> >
> > It would be great to include it inside WFS…
> 
> CASP, implementing stable model semantics and tabling with WFS are quite different beasts. I not a logician …

I meant including constructive negation only into WFS. But maybe this is inherently associated to stable models. I’m not a logician either.

> [@jan](#):
>
> > [@Christophe\_Rey](#):
> >
> > Maybe some papers are dealing of this (?).
> 
> Search Google Scholar for “scasp gupta” and you have most papers. sCASP was written by Joaquin Arias. Gopal Gupta has co-authored most of the papers concerning sCASP and its predecessor sASP. The SWI-Prolog port as at [GitHub - JanWielemaker/sCASP: top-down interpreter for ASP programs with constraints](https://github.com/JanWielemaker/sCASP). The original version in Ciao is primarily a stand-alone system. I’m working on turning this into an embedded language, i.e., allow solving a specific query under sCASP semantics.

That will be very useful to have such an embedded language.

Thanks a lot Jan once again for your answer and for all your work !!

---

<div class="post-metadata">

**Author:** ![Christophe\_Rey](https://avatars.discourse-cdn.com/v4/letter/c/c6cbf5/32.png) [@Christophe\_Rey](https://swi-prolog.discourse.group/u/Christophe_Rey)\
**Post date:** [September 16, 2021, 2:29pm UTC](https://swi-prolog.discourse.group/t/unexplained-behaviour-wrt-the-well-founded-semantics-part-4/4377/8 "2021-09-16T14:29:59Z")

</div>

Hi Jan,

just a little remark on a past post:

> [@jan](#):
>
> ```prolog
> isUndefined(Atom) :- call_delays(Atom, Delays), Delays \== true.
> isTrue(Atom) :- call_delays(Atom, true).
> isFalse(Atom) :- \+ isTrue(Atom).
> 
> ```

After a few tests, my colleague Sylvain Lapeyrade found a problem with the last two rules :

```prolog
isTrue(Atom) :- call_delays(Atom, true).
isFalse(Atom) :- \+ isTrue(Atom).

```

since if Atom is undefined, then isFalse(Atom) is true (whereas we would like false). So we changed by

```prolog
isUndefined(Atom) :- call_delays(Atom, Delays), Delays \== true.
isTrue(Atom) :- call_delays(Atom, true).
isFalse(Atom) :- \+ Atom.

```

It seems to be ok.

Best regards

Christophe
