According to
Wikipedia,
"Well Founded Semantics is one definition of how we can make
conclusions from a set of logical rules". Well Founded Semantics (WFS)
defines a three valued logic representing true,
false and something that is neither true or false. This latter
value is often referred to as bottom, undefined or
unknown. SWI-Prolog uses undefined/0.
Well Founded Semantics allows for reasoning about programs with
contradictions or multiple answer sets. It allows for obtaining
true/false results for literals that do not depend on the sub program
that has no unambiguous solution, propagating the notion of
undefined to literals that cannot be resolved otherwise and
obtaining the residual program that expresses why an answer is
not unambiguous.
The notion of Well Founded Semantics is only relevant if the
program uses negation as implemented by tnot/1.
The argument of
tnot/1,
as the name implies, must be a goal associated with a tabled predicate
(see table/1).
In a nutshell, resolving a goal that implies
tnot/1
is implemented as follows:
Consider the following partial body term:
...,
tnot(p),
q.
- If p has an unconditional answer in its table, fail.
- Else, delay the negation. If an unconditional answer
arrives at some time, resume with failure.
- If at the end of the traditional tabled evaluation we can still not
decide on p, execute the continuation (q
above) while maintaining the delay list set to
tnot(p)
. If executing the continuation results in an answer
for some tabled predicate, record this answer as a
conditional answer, in this case with the condition
tnot(p)
.
- If a conditional answer is added to a table, it is propagated to its
followers, say f, adding the pair {f,answer}
to the delay list. If this leads to an answer, the answer is conditional
on this pair.
- After the continuations of all unresolved tnot/1
calls have been executed the various tables may have conditional answers
in addition to normal answers.
- If there are negative literals that have neither conditional answers
nor unconditional answers, the condition
tnot(g)
is true.
This conclusion is propagated by simplifying the conditions for all
answers that depend on tnot(g)
. This may result in a
definite false condition, in which case the answer is removed
or a definite true condition in which case the answer is made
unconditional. Both events can make other conditional answers definitely
true or false, etc.
- At the end of the simplifying process some answers may still be
conditional. A final answer completion step analyses the graph
of depending nodes, eliminating positive loops, e.g., “p
:- q. q :- p” . The answers in
such a loop are removed, possibly leading to more simplification. This
process is executed until fixed point is reached, i.e., no
further positive loops exist and no further simplification is possible.
The above process may complete without any remaining conditional
answers, in which case we are back in the normal Prolog world. It is
also possible that some answers remain conditional. The most obvious
case is represented by undefined/0.
The toplevel responds with
undefined instead of true if an answer is conditional.
- undefined
- Unknown represents neither
true
nor false
in
the well formed model. It is implemented as
:- table undefined/0.
undefined :- tnot(undefined).
Solving a set of predicates under
well formed semantics results in a
residual program. This program contains clauses for all tabled
predicates with condition answers where each clause head represents and
answer and each clause body its condition. The condition is a
disjunction of conjunctions where each literal is either a tabled goal
or tnot/1
of a tabled goal. The remaining model has at least a cycle through a
negative literal (tnot/1)
and has no single solution in the stable model semantics, i.e.,
it either expresses a contradiction (as undefined/0,
i.e., there is no stable model) or a multiple stable models as in the
program below, where both
{p} and {q} are stable models.
:- table p/0, q/0.
p :- tnot(q).
q :- tnot(p).
Note that it is possible that some literals have the same truth value
in all stable models but are still undefined under the stable
model semantics.
The residual program is an explanation of why an answer is undefined.
SWI-Prolog offers the following predicates to access the residual
program.
- call_residual_program(:Goal,
-Program)
- True when Goal is an answer according to the Well Founded
Semantics. If Program is the empty list, Goal is
unconditionally true. Otherwise this is a program as described by delays_residual_program/2.
- call_delays(:Goal,
-Condition)
- True when Goal is an answer that is true when Condition can
be satisfied. If Condition is
true
, Answer
is unconditional. Otherwise it is a conjunction of goals, each of which
is associated with a tabled predicate.
- delays_residual_program(:Condition,
-Program)
- Program is a list of clauses that represents the connected program
associated with Condition. Each clause head represents a
conditional answer from a table and each corresponding clause body is
the condition that must hold for this answer to be true. The body is a
disjunction of conjunctions. Each leaf in this condition is either a
term
tnot(Goal)
or a plain Goal. Each
Goal is associated with a tabled predicate. The program
always contains at least one cycle that involves tnot/1.
The toplevel supports two modes for reporting that it is undefined
whether the current answer is true. The mode is selected by the Prolog
flag
toplevel_list_wfs_residual_program.
If true
, the toplevel uses call_delays/2
and delays_residual_program/2
to find the conditional answers used and the residual program
associated with these answers. It then prints the residual program,
followed by the answer and the conditional answers. For undefined/0,
this results in the following output:
?- undefined.
% WFS residual program
undefined :-
tnot(undefined).
undefined.
If the toplevel_list_wfs_residual_program
is false, any undefined answer is a conjunction with undefined/0.
See the program and output below.
:- table p/0, q/0.
p :- tnot(q).
q :- tnot(p).
?- p.
% WFS residual program
p :-
tnot(q).
q :-
tnot(p).
p.
?- set_prolog_flag(toplevel_list_wfs_residual_program, false).
true.
?- p.
undefined.