2159 lines
66 KiB
Perl
2159 lines
66 KiB
Perl
|
/***************************************************************************/
|
||
|
/* */
|
||
|
/* The SLG System */
|
||
|
/* Authors: Weidong Chen and David Scott Warren */
|
||
|
/* Copyright (C) 1993 Southern Methodist University */
|
||
|
/* 1993 SUNY at Stony Brook */
|
||
|
/* See file COPYRIGHT_SLG for copying policies and disclaimer. */
|
||
|
/* */
|
||
|
/***************************************************************************/
|
||
|
|
||
|
/*==========================================================================
|
||
|
File : slg.pl
|
||
|
Last Modification : November 14, 2007 by Fabrizio Riguzzi
|
||
|
===========================================================================*/
|
||
|
|
||
|
/* ----------- beginning of system dependent features ---------------------
|
||
|
To run the SLG system under a version of Prolog other than Quintus,
|
||
|
comment out the following Quintus-specific code, and include the code
|
||
|
for the Prolog you are running.
|
||
|
*/
|
||
|
|
||
|
% Quintus
|
||
|
/* Begin Quintus specific code */
|
||
|
% :- use_module(library(basics)).
|
||
|
% :- dynamic 'slg$prolog'/1, 'slg$tab'/2.
|
||
|
% :- dynamic slg_expanding/0.
|
||
|
% :- dynamic wfs_trace/0.
|
||
|
/* End Quintus specific code */
|
||
|
|
||
|
% Sicstus
|
||
|
/* Begin Sicstus specific code */
|
||
|
append([],L,L).
|
||
|
append([X|L1],L2,[X|L3]) :- append(L1,L2,L3).
|
||
|
|
||
|
member(X,[X|_]).
|
||
|
member(X,[_|L]) :- member(X,L).
|
||
|
|
||
|
memberchk(X,[X|_]) :- !.
|
||
|
memberchk(X,[_|L]) :- memberchk(X,L).
|
||
|
|
||
|
:- dynamic 'slg$prolog'/1, 'slg$tab'/2.
|
||
|
:- dynamic slg_expanding/0.
|
||
|
:- dynamic wfs_trace/0.
|
||
|
/* End Sicstus specific code */
|
||
|
|
||
|
% XSB
|
||
|
/* Begin XSB specific code */
|
||
|
/* To compile this under xsb, you must allocate more than the default stack
|
||
|
space when running xsb. E.g. use % xsb -m 2000
|
||
|
*/
|
||
|
%:- import member/2, memberchk/2, append/3, ground/1 from basics.
|
||
|
%:- import numbervars/3 from num_vars.
|
||
|
|
||
|
%:- dynamic slg_expanding/0.
|
||
|
%:- dynamic 'slg$prolog'/1, 'slg$tab'/2.
|
||
|
%:- dynamic wfs_trace/0.
|
||
|
/* End XSB specific code */
|
||
|
|
||
|
/* -------------- end of system dependent features ----------------------- */
|
||
|
|
||
|
/* -------------- beginning of slg_load routines -------------------------
|
||
|
An input file may contain three kinds of directives (in addition to
|
||
|
regular Prolog clauses and commands):
|
||
|
|
||
|
a) :- default(prolog).
|
||
|
:- default(tabled).
|
||
|
All predicates defined from now on are prolog (tabled) predicates
|
||
|
unless specified otherwise later.
|
||
|
b) :- tabled pred_name/arity.
|
||
|
pred_name/arity is a tabled predicate. A comma separated list
|
||
|
is also acceptable.
|
||
|
|
||
|
c) :- prolog pred_name/arity.
|
||
|
pred_name/arity is a prolog predicate. A comma separated list
|
||
|
is also acceptable.
|
||
|
|
||
|
Besides Prolog clauses, we allow general clauses where the body is a
|
||
|
universal disjunction of literals. Such clauses are specified in the form
|
||
|
Head <-- Body.
|
||
|
(Maybe <-- can be viewed as "All".) The head must be an atom of a tabled
|
||
|
predicate and the body should be a disjunction of literals (separated by ';')
|
||
|
and should not contain cut. The head must be ground whenever it is called.
|
||
|
All variables in the body that do not occur in the head are universally
|
||
|
quantified.
|
||
|
|
||
|
There is NO support for module facilities. In particular, ALL TABLED
|
||
|
PREDICATES SHOULD BE DEFINED IN MODULE 'user'.
|
||
|
*/
|
||
|
|
||
|
:- op(1200,xfx,<--).
|
||
|
:- op(1150,fx,[(tabled),(prolog),(default)]).
|
||
|
:- op(900,xfx,<-).
|
||
|
|
||
|
:- assert('slg$tabled'(0,0)), retractall('slg$tabled'(_,_)).
|
||
|
:- assert('slg$default'((prolog))).
|
||
|
|
||
|
do_term_expansion(end_of_file,_) :- !,
|
||
|
retractall('slg$default'(_)),
|
||
|
assert('slg$default'((prolog))),
|
||
|
retractall('slg$prolog'(_)),
|
||
|
retractall('slg$tab'(_,_)),
|
||
|
fail.
|
||
|
do_term_expansion((:-Com),Clauses) :- !,
|
||
|
expand_command(Com,Clauses).
|
||
|
do_term_expansion((H-->B),NewClause) :- !,
|
||
|
\+ slg_expanding,
|
||
|
assert(slg_expanding),
|
||
|
expand_term((H-->B),Clause),
|
||
|
retractall(slg_expanding),
|
||
|
do_term_expansion(Clause,NewClause).
|
||
|
do_term_expansion((Head <-- Body),Clauses) :- !,
|
||
|
functor(Head,P,A),
|
||
|
Pred = P/A,
|
||
|
( 'slg$tab'(P,A) ->
|
||
|
convert_univ_clause(Head,Body,Clauses)
|
||
|
; 'slg$prolog'(Pred) ->
|
||
|
write('Error: Prolog predicate '), write(Pred),
|
||
|
write(' in clauses with universal disjunction.'),nl,
|
||
|
write(' Clause ignored: '), write((Head <-- Body)), nl,
|
||
|
Clauses = []
|
||
|
; 'slg$default'(Default),
|
||
|
( Default == (prolog) ->
|
||
|
write('Error: Prolog predicate '), write(Pred),
|
||
|
write(' in clauses with universal disjunction.'),nl,
|
||
|
write(' Clause ignored: '), write((Head <-- Body)), nl,
|
||
|
Clauses = []
|
||
|
; assert('slg$tab'(P,A)),
|
||
|
retractall('slg$tabled'(P,A)),
|
||
|
assert('slg$tabled'(P,A)),
|
||
|
functor(NewHead,P,A),
|
||
|
Clauses = [(:- retractall('slg$tabled'(P,A)), assert('slg$tabled'(P,A))),
|
||
|
(NewHead :- slg(NewHead))|RestClauses],
|
||
|
convert_univ_clause(Head,Body,RestClauses)
|
||
|
)
|
||
|
).
|
||
|
do_term_expansion(Clause,Clauses) :-
|
||
|
( Clause = (Head :- Body) -> true; Head = Clause, Body = true ),
|
||
|
functor(Head,P,A),
|
||
|
Pred = P/A,
|
||
|
( 'slg$tab'(P,A) ->
|
||
|
convert_tabled_clause(Head,Body,Clauses)
|
||
|
; 'slg$prolog'(Pred) ->
|
||
|
Clauses = Clause
|
||
|
; 'slg$default'(Default),
|
||
|
( Default == (prolog) ->
|
||
|
Clauses = Clause
|
||
|
; ( 'slg$tab'(P,A) ->
|
||
|
convert_tabled_clause(Head,Body,Clauses)
|
||
|
; assert('slg$tab'(P,A)),
|
||
|
retractall('slg$tabled'(P,A)),
|
||
|
assert('slg$tabled'(P,A)),
|
||
|
functor(NewHead,P,A),
|
||
|
Clauses = [(:- retractall('slg$tabled'(P,A)), assert('slg$tabled'(P,A))),
|
||
|
(NewHead :- slg(NewHead))|RestClauses],
|
||
|
convert_tabled_clause(Head,Body,RestClauses)
|
||
|
)
|
||
|
)
|
||
|
).
|
||
|
expand_command(tabled(Preds),Clauses) :-
|
||
|
expand_command_table(Preds,Clauses,[]).
|
||
|
expand_command(prolog(Preds),Clauses) :-
|
||
|
expand_command_prolog(Preds,Clauses,[]).
|
||
|
expand_command(multifile(Preds),(:-multifile(NewPreds))) :-
|
||
|
add_table_preds(Preds,NewPreds,[]).
|
||
|
expand_command(dynamic(Preds),(:-dynamic(NewPreds))) :-
|
||
|
add_table_preds(Preds,NewPreds,[]).
|
||
|
expand_command(default(D),[]) :-
|
||
|
( (D == (prolog); D == (tabled)) ->
|
||
|
retractall('slg$default'(_)),
|
||
|
assert('slg$default'(D))
|
||
|
; write('Warning: illegal default '),
|
||
|
write(D),
|
||
|
write(' ignored.'),
|
||
|
nl
|
||
|
).
|
||
|
|
||
|
expand_command_table((Pred,Preds),Clauses0,Clauses) :- !,
|
||
|
expand_command_table_one(Pred,Clauses0,Clauses1),
|
||
|
expand_command_table(Preds,Clauses1,Clauses).
|
||
|
expand_command_table(Pred,Clauses0,Clauses) :-
|
||
|
expand_command_table_one(Pred,Clauses0,Clauses).
|
||
|
|
||
|
expand_command_table_one(Pspec,Clauses0,Clauses) :-
|
||
|
( Pspec = P/A -> true; P = Pspec, A = 0 ),
|
||
|
Pred = P/A,
|
||
|
functor(H,P,A),
|
||
|
( ( predicate_property(H,built_in); slg_built_in(H) ) ->
|
||
|
write('ERROR: Cannot table built_in '),
|
||
|
write(Pred), nl,
|
||
|
Clauses0 = Clauses
|
||
|
; 'slg$prolog'(Pred) ->
|
||
|
write('ERROR: '),
|
||
|
write(Pred),
|
||
|
write(' assumed to be a Prolog predicate'),
|
||
|
nl,
|
||
|
tab(7),
|
||
|
write('But later declared a tabled predicate.'),
|
||
|
nl,
|
||
|
Clauses0 = Clauses
|
||
|
; 'slg$tab'(P,A) ->
|
||
|
Clauses0 = Clauses
|
||
|
; assert('slg$tab'(P,A)),
|
||
|
retractall('slg$tabled'(P,A)),
|
||
|
assert('slg$tabled'(P,A)),
|
||
|
Clauses0 = [(:- retractall('slg$tabled'(P,A)), assert('slg$tabled'(P,A))),
|
||
|
(H :- slg(H))|Clauses]
|
||
|
).
|
||
|
|
||
|
expand_command_prolog((Pred,Preds),Clauses0,Clauses) :- !,
|
||
|
expand_command_prolog_one(Pred,Clauses0,Clauses1),
|
||
|
expand_command_prolog(Preds,Clauses1,Clauses).
|
||
|
expand_command_prolog(Pred,Clauses0,Clauses) :-
|
||
|
expand_command_prolog_one(Pred,Clauses0,Clauses).
|
||
|
|
||
|
expand_command_prolog_one(Pspec,Clauses0,Clauses) :-
|
||
|
( Pspec = P/A -> true; P = Pspec, A = 0 ),
|
||
|
Pred = P/A,
|
||
|
( 'slg$tab'(P,A) ->
|
||
|
write('ERROR: '),
|
||
|
write(Pred),
|
||
|
write(' assumed to be a tabled predicate'),
|
||
|
nl,
|
||
|
tab(7),
|
||
|
write('But later declared a Prolog predicate.'),
|
||
|
nl,
|
||
|
Clauses0 = Clauses
|
||
|
; retractall('slg$tab'(P,A)),
|
||
|
retractall('slg$tabled'(P,A)),
|
||
|
( 'slg$prolog'(Pred) ->
|
||
|
true
|
||
|
; assert('slg$prolog'(Pred))
|
||
|
),
|
||
|
Clauses0 = [(:- retractall('slg$tabled'(P,A)))|Clauses]
|
||
|
).
|
||
|
|
||
|
add_table_preds(Preds,NewPreds0,NewPreds) :-
|
||
|
( Preds == [] ->
|
||
|
NewPreds0 = NewPreds
|
||
|
; Preds = [P|Ps] ->
|
||
|
add_table_preds(P,NewPreds0,NewPreds1),
|
||
|
add_table_preds(Ps,NewPreds1,NewPreds)
|
||
|
; Preds = (P,Ps) ->
|
||
|
add_table_preds(P,NewPreds0,NewPreds1),
|
||
|
add_table_preds(Ps,NewPreds1,NewPreds)
|
||
|
; ( Preds = P/A -> true; P = Preds, A = 0 ),
|
||
|
( 'slg$tab'(P,A) ->
|
||
|
name(P,Pl),
|
||
|
name(NewP,[115,108,103,36|Pl]), % 'slg$'
|
||
|
NewA is A+1,
|
||
|
NewPreds0 = [P/A,NewP/NewA|NewPreds]
|
||
|
; NewPreds0 = [P/A|NewPreds]
|
||
|
)
|
||
|
).
|
||
|
|
||
|
convert_tabled_clause(Head,Body,Clauses0) :-
|
||
|
conj_to_list(Body,Blist),
|
||
|
extract_guard(Blist,Guard,[],Nbody,Clauses0,Clauses),
|
||
|
list_to_conj(Guard,Gconj),
|
||
|
new_slg_head(Head,Nbody,NewHead),
|
||
|
( Gconj == true ->
|
||
|
Clauses = [NewHead]
|
||
|
; Clauses = [(NewHead :- Gconj)]
|
||
|
).
|
||
|
|
||
|
convert_univ_clause(Head,Body,Clauses) :-
|
||
|
disj_to_list(Body,Blist),
|
||
|
new_slg_head(Head,all(Blist),NewHead),
|
||
|
Clauses = [(NewHead :- ( ground0(Head) ->
|
||
|
true
|
||
|
; write('Error: Non-ground call '),
|
||
|
write(Head),
|
||
|
write(' in a clause with universal disjunction.'),
|
||
|
nl
|
||
|
))].
|
||
|
|
||
|
ground0(X) :- ground(X).
|
||
|
|
||
|
conj_to_list(Term,List) :-
|
||
|
conj_to_list(Term,List,[]).
|
||
|
conj_to_list(Term,List0,List) :-
|
||
|
( Term = (T1,T2) ->
|
||
|
conj_to_list(T1,List0,List1),
|
||
|
conj_to_list(T2,List1,List)
|
||
|
; Term == true ->
|
||
|
List0 = List
|
||
|
; List0 = [Term|List]
|
||
|
).
|
||
|
|
||
|
disj_to_list(Term,List) :-
|
||
|
disj_to_list(Term,List,[]).
|
||
|
disj_to_list(Term,List0,List) :-
|
||
|
( Term = (T1;T2) ->
|
||
|
disj_to_list(T1,List0,List1),
|
||
|
disj_to_list(T2,List1,List)
|
||
|
; Term == true ->
|
||
|
List0 = List
|
||
|
; List0 = [Term|List]
|
||
|
).
|
||
|
|
||
|
extract_guard([],G,G,[],Cls,Cls).
|
||
|
extract_guard([Lit|List],G0,G,Rest,Cls0,Cls) :-
|
||
|
( Lit = (\+N) ->
|
||
|
Nlit = N
|
||
|
; Nlit = Lit
|
||
|
),
|
||
|
( ( predicate_property(Nlit,built_in); slg_built_in(Nlit) ) ->
|
||
|
G0 = [Lit|G1],
|
||
|
extract_guard(List,G1,G,Rest,Cls0,Cls)
|
||
|
; functor(Nlit,P,A),
|
||
|
Pred = P/A,
|
||
|
( 'slg$tab'(P,A) ->
|
||
|
G0 = G,
|
||
|
Rest = [Lit|List],
|
||
|
Cls0 = Cls
|
||
|
; 'slg$prolog'(Pred) ->
|
||
|
G0 = [Lit|G1],
|
||
|
extract_guard(List,G1,G,Rest,Cls0,Cls)
|
||
|
; 'slg$default'((prolog)) ->
|
||
|
G0 = [Lit|G1],
|
||
|
assert('slg$prolog'(Pred)),
|
||
|
Cls0 = [(:- 'slg$prolog'(Pred) -> true; assert('slg$prolog'(Pred)))|Cls1],
|
||
|
extract_guard(List,G1,G,Rest,Cls1,Cls)
|
||
|
; 'slg$default'((tabled)) ->
|
||
|
G0 = G,
|
||
|
Rest = [Lit|List],
|
||
|
assert('slg$tab'(P,A)),
|
||
|
retractall('slg$tabled'(P,A)),
|
||
|
assert('slg$tabled'(P,A)),
|
||
|
functor(Head,P,A),
|
||
|
Cls0 = [(:- retractall('slg$tabled'(P,A)), assert('slg$tabled'(P,A))),
|
||
|
(Head :- slg(Head))|Cls]
|
||
|
)
|
||
|
).
|
||
|
|
||
|
list_to_conj([],true).
|
||
|
list_to_conj([Lit|List],G0) :-
|
||
|
( List == [] ->
|
||
|
G0 = Lit
|
||
|
; G0 = (Lit,G),
|
||
|
list_to_conj(List,G)
|
||
|
).
|
||
|
|
||
|
new_slg_head(Head,Body,NewHead) :-
|
||
|
functor(Head,P,A),
|
||
|
name(P,Pl),
|
||
|
name(Npred,[115,108,103,36|Pl]), % 'slg$'
|
||
|
Narity is A+1,
|
||
|
functor(NewHead,Npred,Narity),
|
||
|
arg(Narity,NewHead,Body),
|
||
|
put_in_args(0,A,Head,NewHead).
|
||
|
|
||
|
put_in_args(A,A,_,_).
|
||
|
put_in_args(A0,A,Head,NewHead) :-
|
||
|
A0 < A,
|
||
|
A1 is A0+1,
|
||
|
arg(A1,Head,Arg),
|
||
|
arg(A1,NewHead,Arg),
|
||
|
put_in_args(A1,A,Head,NewHead).
|
||
|
|
||
|
slg_built_in(slg(_)).
|
||
|
slg_built_in(_<-_).
|
||
|
slg_built_in(slgall(_,_)).
|
||
|
slg_built_in(slgall(_,_,_,_)).
|
||
|
slg_built_in(emptytable(_)).
|
||
|
slg_built_in(st(_,_)).
|
||
|
slg_built_in(stnot(_,_)).
|
||
|
slg_built_in(stall(_,_,_)).
|
||
|
slg_built_in(stall(_,_,_,_,_)).
|
||
|
slg_built_in(stselect(_,_,_,_)).
|
||
|
slg_built_in(stselect(_,_,_,_,_,_)).
|
||
|
slg_built_in(xtrace).
|
||
|
slg_built_in(xnotrace).
|
||
|
|
||
|
/* ----------------- end of slg_load routines --------------------------- */
|
||
|
|
||
|
/* SLG tracing:
|
||
|
xtrace: turns SLG trace on, which prints out tables at various
|
||
|
points
|
||
|
xnotrace: turns off SLG trace
|
||
|
*/
|
||
|
xtrace :-
|
||
|
( wfs_trace ->
|
||
|
true
|
||
|
; assert(wfs_trace)
|
||
|
).
|
||
|
xnotrace :-
|
||
|
( wfs_trace ->
|
||
|
retractall(wfs_trace)
|
||
|
; true
|
||
|
).
|
||
|
|
||
|
/* isprolog(Call): Call is a Prolog subgoal */
|
||
|
isprolog(Call) :-
|
||
|
functor(Call,P,A),
|
||
|
\+ 'slg$tabled'(P,A).
|
||
|
|
||
|
/* slg(Call):
|
||
|
It returns all true answers of Call under the well-founded semantics
|
||
|
one by one.
|
||
|
*/
|
||
|
slg(Call) :-
|
||
|
( isprolog(Call) ->
|
||
|
call(Call)
|
||
|
; oldt(Call,Tab),
|
||
|
ground(Call,Ggoal),
|
||
|
find(Tab,Ggoal,Ent),
|
||
|
ent_to_anss(Ent,Anss),
|
||
|
member_anss(d(Call,[]),Anss)
|
||
|
).
|
||
|
|
||
|
/* Call<-Cons:
|
||
|
It returns all true or undefined answers of Call one by one. In
|
||
|
case of a true answer, Cons = []. For an undefined answer,
|
||
|
Cons is a list of delayed literals.
|
||
|
*/
|
||
|
Call<-Cons :-
|
||
|
( isprolog(Call) ->
|
||
|
call(Call),
|
||
|
Cons = []
|
||
|
; oldt(Call,Tab),
|
||
|
ground(Call,Ggoal),
|
||
|
find(Tab,Ggoal,Ent),
|
||
|
ent_to_anss(Ent,Anss),
|
||
|
member_anss(d(Call,Cons),Anss)
|
||
|
).
|
||
|
|
||
|
/* emptytable(EmptTab): creates an initial empty stable.
|
||
|
*/
|
||
|
emptytable(0:[]).
|
||
|
|
||
|
/* slgall(Call,Anss):
|
||
|
slgall(Call,Anss,N0-Tab0,N-Tab):
|
||
|
If Call is a prolog call, findall is used, and Tab = Tab0;
|
||
|
If Call is an atom of a tabled predicate, SLG evaluation
|
||
|
is carried out.
|
||
|
*/
|
||
|
slgall(Call,Anss) :-
|
||
|
slgall(Call,Anss,0:[],_).
|
||
|
slgall(Call,Anss,N0:Tab0,N:Tab) :-
|
||
|
( isprolog(Call) ->
|
||
|
findall(Call,Call,Anss),
|
||
|
N = N0, Tab = Tab0
|
||
|
; ground(Call,Ggoal),
|
||
|
( find(Tab0,Ggoal,Ent) ->
|
||
|
ent_to_anss(Ent,Answers),
|
||
|
Tab = Tab0
|
||
|
; new_init_call(Call,Ggoal,Ent,[],S1,1,Dfn1),
|
||
|
add_tab_ent(Ggoal,Ent,Tab0,Tab1),
|
||
|
oldt(Call,Ggoal,Tab1,Tab,S1,_S,Dfn1,_Dfn,maxint-maxint,_Dep,N0:[],N:_TP),
|
||
|
find(Tab,Ggoal,NewEnt),
|
||
|
ent_to_anss(NewEnt,Answers)
|
||
|
),
|
||
|
ansstree_to_list(Answers,Anss,[])
|
||
|
).
|
||
|
|
||
|
/* st(Call,PSM):
|
||
|
stnot(Call,PSM):
|
||
|
It finds a stable model in which Call must be true (false).
|
||
|
Call must be ground.
|
||
|
*/
|
||
|
st(Call,PSM) :-
|
||
|
st_true_false(Call,true,PSM).
|
||
|
stnot(Call,PSM) :-
|
||
|
st_true_false(Call,false,PSM).
|
||
|
|
||
|
st_true_false(Call,Val,PSM) :-
|
||
|
( isprolog(Call) ->
|
||
|
PSM = [],
|
||
|
call(Call)
|
||
|
; ground(Call) ->
|
||
|
wfs_newcall(Call,[],Tab1,0,_),
|
||
|
find(Tab1,Call,Ent),
|
||
|
ent_to_anss(Ent,Anss),
|
||
|
( succeeded(Anss) ->
|
||
|
( Val == true ->
|
||
|
PSM = []
|
||
|
; fail
|
||
|
)
|
||
|
; failed(Anss) ->
|
||
|
( Val == false ->
|
||
|
PSM = []
|
||
|
; fail
|
||
|
)
|
||
|
; assume_one(Call,Val,Tab1,Tab2,[],Abd1,A0,A1),
|
||
|
collect_unds(Anss,A1,A),
|
||
|
st(A0,A,Tab2,Tab3,Abd1,Abd,[],DAbd,[],_Plits),
|
||
|
final_check(Abd,Tab3,_Tab,DAbd,PSM)
|
||
|
)
|
||
|
; write('Error: non-ground call '),
|
||
|
write(Call),
|
||
|
write(' in st/2.'),
|
||
|
nl,
|
||
|
fail
|
||
|
).
|
||
|
|
||
|
/* stall(Call,Anss,PSM):
|
||
|
stall(Call,Anss,PSM,Tab0,Tab):
|
||
|
It computes a partial stable model PSM and collects all
|
||
|
answers of Call in that model.
|
||
|
*/
|
||
|
stall(Call,Anss,PSM) :-
|
||
|
stall(Call,Anss,PSM,0:[],_).
|
||
|
|
||
|
stall(Call,Anss,PSM,N0:Tab0,N:Tab) :-
|
||
|
( isprolog(Call) ->
|
||
|
findall(Call,Call,Anss),
|
||
|
PSM = [], N = N0, Tab = Tab0
|
||
|
; ground(Call,Ggoal),
|
||
|
( find(Tab0,Ggoal,Ent) ->
|
||
|
Tab1 = Tab0, N = N0
|
||
|
; wfs_newcall(Call,Tab0,Tab1,N0,N),
|
||
|
find(Tab1,Ggoal,Ent)
|
||
|
),
|
||
|
ent_to_delay(Ent,Delay),
|
||
|
( Delay == false ->
|
||
|
Fent = Ent, PSM = [], Tab = Tab1
|
||
|
; ent_to_anss(Ent,Anss0),
|
||
|
collect_unds(Anss0,A0,A),
|
||
|
st(A0,A,Tab1,Tab2,[],Abd,[],DAbd,[],_Plits),
|
||
|
final_check(Abd,Tab2,Tab,DAbd,PSM),
|
||
|
find(Tab,Ggoal,Fent)
|
||
|
),
|
||
|
ent_to_anss(Fent,Anss1),
|
||
|
ansstree_to_list(Anss1,Anss,[])
|
||
|
).
|
||
|
|
||
|
/* stselect(Call,PSM0,Anss,PSM):
|
||
|
stselect(Call,PSM0,Anss,PSM,N0:Tab0,N:Tab):
|
||
|
It computes a partial stable model PSM in which all ground
|
||
|
literals in PSM0 are true, and returns all answers of Call
|
||
|
in the partial stable model. Call must be an atom of a tabled
|
||
|
or stable predicate.
|
||
|
*/
|
||
|
stselect(Call,PSM0,Anss,PSM) :-
|
||
|
stselect(Call,PSM0,Anss,PSM,0:[],_).
|
||
|
|
||
|
stselect(Call,PSM0,Anss,PSM,N0:Tab0,N:Tab) :-
|
||
|
( isprolog(Call) ->
|
||
|
write('Error: Prolog predicate '),
|
||
|
write(Call),
|
||
|
write('stselect.'),
|
||
|
fail
|
||
|
; wfsoldt(Call,PSM0,Ent,Tab0,Tab1,N0,N),
|
||
|
ent_to_delay(Ent,Delay),
|
||
|
assume_list(PSM0,true,Tab1,Tab2,[],Abd0,A0,A1),
|
||
|
( Delay == false ->
|
||
|
A1 = A2
|
||
|
; ent_to_anss(Ent,Anss0),
|
||
|
collect_unds(Anss0,A1,A2)
|
||
|
),
|
||
|
st(A0,A2,Tab2,Tab3,Abd0,Abd,[],DAbd,[],_Plits),
|
||
|
final_check(Abd,Tab3,Tab,DAbd,PSM),
|
||
|
ground(Call,Ggoal),
|
||
|
find(Tab,Ggoal,Fent),
|
||
|
ent_to_anss(Fent,Anss1),
|
||
|
ansstree_to_list(Anss1,Anss,[])
|
||
|
).
|
||
|
|
||
|
wfsoldt(Call,PSM0,Ent,Tab0,Tab,N0,N) :-
|
||
|
ground(Call,Ggoal),
|
||
|
( find(Tab0,Ggoal,Ent) ->
|
||
|
Tab1 = Tab0, N1 = N0
|
||
|
; wfs_newcall(Call,Tab0,Tab1,N0,N1),
|
||
|
find(Tab1,Ggoal,Ent)
|
||
|
),
|
||
|
wfsoldt_ground(PSM0,Tab1,Tab,N1,N).
|
||
|
|
||
|
wfsoldt_ground([],Tab,Tab,N,N).
|
||
|
wfsoldt_ground([A|PSM],Tab0,Tab,N0,N) :-
|
||
|
( ground(A) ->
|
||
|
true
|
||
|
; write('Error: non-ground assumption in stable model selection: '),
|
||
|
write(A), nl, fail
|
||
|
),
|
||
|
( A = (\+G) ->
|
||
|
true
|
||
|
; A = G
|
||
|
),
|
||
|
( isprolog(G) ->
|
||
|
Tab1 = Tab0, N1 = N0,
|
||
|
call(A)
|
||
|
; find(Tab0,G,_) ->
|
||
|
Tab1 = Tab0, N1 = N0
|
||
|
; wfs_newcall(G,Tab0,Tab1,N0,N1)
|
||
|
),
|
||
|
wfsoldt_ground(PSM,Tab1,Tab,N1,N).
|
||
|
|
||
|
wfs_newcall(Call,Tab0,Tab,N0,N) :-
|
||
|
new_init_call(Call,Ggoal,Ent0,[],S1,1,Dfn1),
|
||
|
add_tab_ent(Ggoal,Ent0,Tab0,Tab1),
|
||
|
oldt(Call,Ggoal,Tab1,Tab,S1,_S,Dfn1,_Dfn,maxint-maxint,_Dep,N0:[],N:_TP).
|
||
|
|
||
|
/* collect_unds(Anss,A0,A):
|
||
|
collects all delayed literals in answers Anss in a open-ended difference
|
||
|
list A0/A. These delayed literals are assumed either false or true in the
|
||
|
stable model computation.
|
||
|
*/
|
||
|
collect_unds([],A,A).
|
||
|
collect_unds(l(_GH,Lanss),A1,A) :-
|
||
|
collect_unds_lanss(Lanss,A1,A).
|
||
|
collect_unds(n2(T1,_,T2),A1,A) :-
|
||
|
collect_unds(T1,A1,A2),
|
||
|
collect_unds(T2,A2,A).
|
||
|
collect_unds(n3(T1,_,T2,_,T3),A1,A) :-
|
||
|
collect_unds(T1,A1,A2),
|
||
|
collect_unds(T2,A2,A3),
|
||
|
collect_unds(T3,A3,A).
|
||
|
|
||
|
collect_unds_lanss([],A,A).
|
||
|
collect_unds_lanss([d(_,D)|Lanss],A1,A) :-
|
||
|
collect_unds_list(D,A1,A2),
|
||
|
collect_unds_lanss(Lanss,A2,A).
|
||
|
|
||
|
collect_unds_list([],A,A).
|
||
|
collect_unds_list([Lit|D],[Lit|A1],A) :-
|
||
|
collect_unds_list(D,A1,A).
|
||
|
|
||
|
/* st(A0,A,Tab0,Tab,Abd0,Abd,DAbd0,DAbd,Plits0,Plits):
|
||
|
A0/A is an open-ended difference list containing a list of
|
||
|
delayed literals. st tries for each delayed literal to
|
||
|
assume that it is true or false and checks to see if
|
||
|
it leads to a partial stable model. Propagation of assumed
|
||
|
truth values is carried out as much as possible. It will
|
||
|
fail if the relevant program contains p :- \+p.
|
||
|
|
||
|
Abd0/Abd is an accumulator for a table of assumed truth
|
||
|
values. They are checked against the table Tab0/Tab for
|
||
|
consistency later in check_consistency. DAbd0/DAbd is an
|
||
|
accumulator for truth values of undefined literals that
|
||
|
are derived from assumed truth values of other literals.
|
||
|
Plits0/Plits is an accumulator for avoiding positive
|
||
|
infinite loops in processing positive delayed literals.
|
||
|
*/
|
||
|
st(A0,A,Tab0,Tab,Abd0,Abd,DAbd0,DAbd,Plits0,Plits) :-
|
||
|
( % empty difference list
|
||
|
A0 == A ->
|
||
|
Tab = Tab0, Abd = Abd0, DAbd = DAbd0, Plits = Plits0
|
||
|
; A0 = [Lit|A1],
|
||
|
( % non-ground negative literals
|
||
|
Lit = (Ggoal - (\+GH)) ->
|
||
|
write('Error: cannot handle non-ground negative literals: '),
|
||
|
write(\+GH), nl, fail
|
||
|
; % positive undefined literal
|
||
|
Lit = Ggoal-GH ->
|
||
|
( % encountered before
|
||
|
find(Plits0,Lit,_) ->
|
||
|
st(A1,A,Tab0,Tab,Abd0,Abd,DAbd0,DAbd,Plits0,Plits)
|
||
|
; % otherwise, process undefined literals it depends upon
|
||
|
addkey(Plits0,Lit,_,Plits1),
|
||
|
find(Tab0,Ggoal,Ent),
|
||
|
ent_to_anss(Ent,Anss),
|
||
|
find(Anss,GH,Lanss),
|
||
|
collect_unds_lanss(Lanss,A,NewA),
|
||
|
st(A1,NewA,Tab0,Tab,Abd0,Abd,DAbd0,DAbd,Plits1,Plits)
|
||
|
)
|
||
|
; % negative undefined literal
|
||
|
Lit = (\+G) ->
|
||
|
( % has been assumed or derived to be true or false
|
||
|
( find(Abd0,G,_Val); find(DAbd0,G,_) ) ->
|
||
|
st(A1,A,Tab0,Tab,Abd0,Abd,DAbd0,DAbd,Plits0,Plits)
|
||
|
; find(Tab0,G,Gent),
|
||
|
ent_to_anss(Gent,Ganss),
|
||
|
( % found to be false already
|
||
|
failed(Ganss) ->
|
||
|
addkey(DAbd0,G,false,DAbd1),
|
||
|
st(A1,A,Tab0,Tab,Abd0,Abd,DAbd1,DAbd,Plits0,Plits)
|
||
|
; % found to be true already
|
||
|
succeeded(Ganss) ->
|
||
|
addkey(DAbd0,G,true,DAbd1),
|
||
|
st(A1,A,Tab0,Tab,Abd0,Abd,DAbd1,DAbd,Plits0,Plits)
|
||
|
; % create a choice point
|
||
|
addkey(Abd0,G,Val,Abd1),
|
||
|
( Ganss = l(G,[d(G,Ds)]), memberchk(\+G,Ds) ->
|
||
|
Val = false
|
||
|
; ( Val = false; Val = true )
|
||
|
),
|
||
|
propagate_forward(G,Val,Tab0,Tab1,Abd1),
|
||
|
A = [G-G|NewA], % make sure delayed literals of G are checked
|
||
|
propagate_backward(G,Val,Ganss,Tab1,Tab2,Abd1,Abd2,NewA,NNA),
|
||
|
st(A1,NNA,Tab2,Tab,Abd2,Abd,DAbd0,DAbd,Plits0,Plits)
|
||
|
)
|
||
|
)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
/* propagate_forward(G,Val,Tab0,Tab,Abd):
|
||
|
G has been assumed to be Val, and this information is propagated
|
||
|
using simplification or forward chaining links as much as
|
||
|
possible.
|
||
|
*/
|
||
|
propagate_forward(G,Val,Tab0,Tab,Abd) :-
|
||
|
updatevs(Tab0,G,Ent0,Ent,Tab1),
|
||
|
Ent0 = e(Nodes,ANegs,Anss,Delay,Comp,Gdfn,Slist0),
|
||
|
Ent = e(Nodes,ANegs,Anss,Delay,Comp,Gdfn,Slist),
|
||
|
extract_known_by_abd(Slist0,Val,[],Slist,[],Klist),
|
||
|
simplify(Klist,Tab1,Tab,Abd).
|
||
|
|
||
|
/* The forward chaining is such that negative literals can fail
|
||
|
or succeed by assumption, and positive literals can fail
|
||
|
by assumption, but cannot succeed by assumption.
|
||
|
This avoids the construction of supported models that are
|
||
|
not stable.
|
||
|
*/
|
||
|
extract_known_by_abd([],_,Slist,Slist,Klist,Klist).
|
||
|
extract_known_by_abd([Link|Links],Val,Slist0,Slist,Klist0,Klist) :-
|
||
|
( Link = (_ : (\+ _)) ->
|
||
|
( Val == false ->
|
||
|
Slist1 = Slist0,
|
||
|
Klist1 = [succ-Link|Klist0]
|
||
|
; Val == true ->
|
||
|
Slist1 = Slist0,
|
||
|
Klist1 = [fail-Link|Klist0]
|
||
|
; Slist1 = [Link|Slist0],
|
||
|
Klist1 = Klist0
|
||
|
)
|
||
|
; % Link = (_ : _-GH) ->
|
||
|
( Val = false ->
|
||
|
Slist1 = Slist0,
|
||
|
Klist1 = [fail-Link|Klist0]
|
||
|
; % Val = true ->
|
||
|
Slist1 = [Link|Slist0],
|
||
|
Klist1 = Klist0
|
||
|
)
|
||
|
),
|
||
|
extract_known_by_abd(Links,Val,Slist1,Slist,Klist1,Klist).
|
||
|
|
||
|
/* propagate_backward(G,Val,Ganss,Tab0,Tab,Abd0,Abd,A,NewA):
|
||
|
It tried to propagate the Val of G backward through answers
|
||
|
if possible. If G is assumed to be true, and G has only one
|
||
|
answer clause, then all literals in the body of the answer
|
||
|
clause must be true. If G is assumed to be false, then all
|
||
|
literals in answer clauses of G that have a single literal
|
||
|
are assumed to be false too. Otherwise, it is no-op.
|
||
|
*/
|
||
|
propagate_backward(G,Val,Ganss,Tab0,Tab,Abd0,Abd,A,NewA) :-
|
||
|
( Ganss = l(G,Lanss) ->
|
||
|
( Val == true, Lanss = [d(G,Ds)] ->
|
||
|
assume_list(Ds,true,Tab0,Tab,Abd0,Abd,A,NewA)
|
||
|
; Val == false, findall(Lit,member(d(G,[Lit]),Lanss),Ds) ->
|
||
|
assume_list(Ds,false,Tab0,Tab,Abd0,Abd,A,NewA)
|
||
|
; Tab = Tab0, Abd = Abd0, A = NewA
|
||
|
)
|
||
|
; Tab = Tab0, Abd = Abd0, A = NewA
|
||
|
).
|
||
|
|
||
|
assume_list([],_Val,Tab,Tab,Abd,Abd,A,A).
|
||
|
assume_list([Lit|Lits],Val,Tab0,Tab,Abd0,Abd,A0,A) :-
|
||
|
assume_one(Lit,Val,Tab0,Tab1,Abd0,Abd1,A0,A1),
|
||
|
assume_list(Lits,Val,Tab1,Tab,Abd1,Abd,A1,A).
|
||
|
|
||
|
/* assume_one(Lit,Val,Tab0,Tab,Abd0,Abd,A0,A):
|
||
|
Due to back propagation, Lit is assumed to be Val.
|
||
|
However, this assumption is carried out only if
|
||
|
Lit is a delayed literal of a ground call or most
|
||
|
general call.
|
||
|
*/
|
||
|
assume_one(Ggoal-GH,_Val,Tab0,Tab,Abd0,Abd,A0,A) :-
|
||
|
Ggoal \== GH,
|
||
|
!,
|
||
|
Tab = Tab0, Abd = Abd0, A = A0.
|
||
|
assume_one(Lit,Val,Tab0,Tab,Abd0,Abd,A0,A) :-
|
||
|
( Lit = G-G ->
|
||
|
GVal = Val
|
||
|
; Lit = (\+G) ->
|
||
|
( Val == true -> GVal = false; GVal = true )
|
||
|
; Lit = G ->
|
||
|
GVal = Val
|
||
|
),
|
||
|
( find(Abd0,G,V) -> % already assumed
|
||
|
( V == GVal ->
|
||
|
Tab = Tab0, Abd = Abd0, A = A0
|
||
|
; fail
|
||
|
)
|
||
|
; find(Tab0,G,Gent),
|
||
|
ent_to_anss(Gent,Ganss),
|
||
|
( failed(Ganss) -> % already known
|
||
|
( GVal == true ->
|
||
|
fail
|
||
|
; Tab = Tab0, Abd = Abd0, A = A0
|
||
|
)
|
||
|
; succeeded(Ganss) -> % already known
|
||
|
( GVal == false ->
|
||
|
fail
|
||
|
; Tab = Tab0, Abd = Abd0, A = A0
|
||
|
)
|
||
|
; addkey(Abd0,G,GVal,Abd1), % otherwise, propagate
|
||
|
propagate_forward(G,GVal,Tab0,Tab1,Abd1),
|
||
|
A0 = [G-G|A1],
|
||
|
propagate_backward(G,Ganss,GVal,Tab1,Tab,Abd1,Abd,A1,A)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
final_check(Abd,Tab0,Tab,DAbd,Alist) :-
|
||
|
check_consistency(Abd,Tab0,Tab,Alist0,Alist1),
|
||
|
add_dabd(DAbd,Alist1,[]),
|
||
|
sort(Alist0,Sorted),
|
||
|
listval_to_listlit(Sorted,Alist).
|
||
|
|
||
|
listval_to_listlit([],[]).
|
||
|
listval_to_listlit([Val|Vlist],[Lit|Llist]) :-
|
||
|
val_to_lit(Val,Lit),
|
||
|
listval_to_listlit(Vlist,Llist).
|
||
|
|
||
|
val_to_lit(G-true,G).
|
||
|
val_to_lit(G-false,\+G).
|
||
|
|
||
|
/* check_consistency(Abd,Tab0,Tab,Alist0,Alist):
|
||
|
A proposition may be assumed to be true, but no true answer
|
||
|
is derived at the end, which is inconsistency. A proposition
|
||
|
may be assumed to be false, but has a true answer. The latter
|
||
|
case is checked when the true answer is derived. Here Abd
|
||
|
indicates the assumed truth values, and answers in Tab0
|
||
|
indicate the derived values by a fixpoint computation of
|
||
|
forward chaining.
|
||
|
|
||
|
Also at the end of a fixpoint computation, a subgoal may
|
||
|
have only delayed answers with positive literals. These
|
||
|
have to be deleted in order for Tab0/Tab to be used
|
||
|
correctly later.
|
||
|
*/
|
||
|
check_consistency([],Tab,Tab,Alist,Alist).
|
||
|
check_consistency(l(G,Val),Tab0,Tab,Alist0,Alist) :-
|
||
|
updatevs(Tab0,G,Ent0,Ent,Tab),
|
||
|
Ent0 = e(Nodes,ANegs,Anss0,_Delay,Comp,Dfn,Slist),
|
||
|
Ent = e(Nodes,ANegs,Anss,false,Comp,Dfn,Slist),
|
||
|
( Val == true ->
|
||
|
succeeded(Anss0),
|
||
|
Anss = l(G,[d(G,[])]), % delete answers with positive delays
|
||
|
Alist0 = [G-Val|Alist]
|
||
|
; % Val == false ->
|
||
|
Anss = [],
|
||
|
Alist0 = [G-Val|Alist]
|
||
|
).
|
||
|
check_consistency(n2(T1,_,T2),Tab0,Tab,Alist0,Alist) :-
|
||
|
check_consistency(T1,Tab0,Tab1,Alist0,Alist1),
|
||
|
check_consistency(T2,Tab1,Tab,Alist1,Alist).
|
||
|
check_consistency(n3(T1,_,T2,_,T3),Tab0,Tab,Alist0,Alist) :-
|
||
|
check_consistency(T1,Tab0,Tab1,Alist0,Alist1),
|
||
|
check_consistency(T2,Tab1,Tab2,Alist1,Alist2),
|
||
|
check_consistency(T3,Tab2,Tab,Alist2,Alist).
|
||
|
|
||
|
add_dabd([],Alist,Alist).
|
||
|
add_dabd(l(G,Val),[G-Val|Alist],Alist).
|
||
|
add_dabd(n2(T1,_,T2),Alist0,Alist) :-
|
||
|
add_dabd(T1,Alist0,Alist1),
|
||
|
add_dabd(T2,Alist1,Alist).
|
||
|
add_dabd(n3(T1,_,T2,_,T3),Alist0,Alist) :-
|
||
|
add_dabd(T1,Alist0,Alist1),
|
||
|
add_dabd(T2,Alist1,Alist2),
|
||
|
add_dabd(T3,Alist2,Alist).
|
||
|
|
||
|
/* oldt(QueryAtom,Table): top level call for SLG resolution.
|
||
|
It returns a table consisting of answers for each relevant
|
||
|
subgoal. For stable predicates, it basically extract the
|
||
|
relevant set of ground clauses by solving Prolog predicates
|
||
|
and other well-founded predicates.
|
||
|
*/
|
||
|
oldt(Call,Tab) :-
|
||
|
new_init_call(Call,Ggoal,Ent,[],S1,1,Dfn1),
|
||
|
add_tab_ent(Ggoal,Ent,[],Tab1),
|
||
|
oldt(Call,Ggoal,Tab1,Tab,S1,_S,Dfn1,_Dfn,maxint-maxint,_Dep,0:[],_TP),
|
||
|
( wfs_trace ->
|
||
|
nl, write('Final '), display_table(Tab), nl
|
||
|
; true
|
||
|
).
|
||
|
|
||
|
/* oldt(Call,Ggoal,Tab0,Tab,Stack0,Stack,DFN0,DFN,Dep0,Dep,TP0,TP)
|
||
|
explores the initial set of edges, i.e., all the
|
||
|
program clauses for Call. Ggoal is of the form
|
||
|
Gcall-Gdfn, where Gcall is numbervar of Call and Gdfn
|
||
|
is the depth-first number of Gcall. Tab0/Tab,Stack0/Stack,
|
||
|
DFN0/DFN, and Dep0/Dep are accumulators for the table,
|
||
|
the stack of subgoals, the DFN counter, and the dependencies.
|
||
|
TP0/TP is the accumulator for newly created clauses during
|
||
|
the processing of general clauss with universal disjunctions
|
||
|
in the body. These clauses are created in order to guarantee
|
||
|
polynomial data complexity in processing clauses with
|
||
|
universal disjuntions in the body of a clause. The newly
|
||
|
created propositions are represented by numbers.
|
||
|
*/
|
||
|
oldt(Call,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
( number(Call) ->
|
||
|
TP0 = (_ : Tcl),
|
||
|
find(Tcl,Call,Clause),
|
||
|
edge_oldt(Clause,Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1)
|
||
|
; findall(rule(d(Call,[]),Body),
|
||
|
(new_slg_head(Call,Body,NewHead),call(NewHead)),
|
||
|
Frames),
|
||
|
map_oldt(Frames,Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1)
|
||
|
),
|
||
|
comp_tab_ent(Ggoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
map_oldt([],_Ggoal,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
map_oldt([Clause|Frames],Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
edge_oldt(Clause,Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
map_oldt(Frames,Ggoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
/* edge_oldt(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
Clause may be one of the following forms:
|
||
|
rule(d(H,Dlist),Blist)
|
||
|
rule(d(H,all(Dlist)),all(Blist))
|
||
|
where the second form is for general clauses with a universal
|
||
|
disjunction of literals in the body. Dlist is a list of delayed
|
||
|
literals, and Blist is the list of literals to be solved.
|
||
|
Clause represents a directed edge from Ggoal to the left most
|
||
|
subgoal in Blist.
|
||
|
*/
|
||
|
edge_oldt(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
Clause = rule(Ans,B),
|
||
|
( B == [] ->
|
||
|
ans_edge(Ans,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
; B = [Lit|_] ->
|
||
|
( Lit = (\+N) ->
|
||
|
neg_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
; pos_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
)
|
||
|
; B = all(Bl) ->
|
||
|
( Bl == [] ->
|
||
|
ans_edge(Ans,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
; Bl = [Lit|_],
|
||
|
( Lit = (\+N) ->
|
||
|
aneg_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
; apos_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
ans_edge(Ans,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
( add_ans(Tab0,Ggoal,Ans,Nodes,Mode,Tab1) ->
|
||
|
( Mode = new_head ->
|
||
|
returned_ans(Ans,Ggoal,RAns),
|
||
|
map_nodes(Nodes,RAns,Tab1,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
; Mode = no_new_head ->
|
||
|
Tab = Tab1, S = S0, Dfn = Dfn0, Dep = Dep0, TP = TP0
|
||
|
)
|
||
|
; Tab = Tab0, S = S0, Dfn = Dfn0, Dep = Dep0, TP = TP0
|
||
|
).
|
||
|
|
||
|
neg_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
Clause = rule(_,[\+N|_]),
|
||
|
( ground(N) -> true
|
||
|
; write('Flounder: '), write(\+N), nl, fail
|
||
|
),
|
||
|
Node = (Ggoal:Clause),
|
||
|
Ngoal = N, % N is already ground
|
||
|
( isprolog(N) -> % if N is a Prolog predicate
|
||
|
( call(N) -> % then just call
|
||
|
Tab = Tab0, S = S0, Dfn = Dfn0, Dep = Dep0, TP = TP0
|
||
|
; apply_subst(Node,d(\+ N,[]),Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
)
|
||
|
; ( find(Tab0,Ngoal,Nent) ->
|
||
|
Tab2 = Tab0, S2 = S0, Dfn2 = Dfn0, Dep1 = Dep0, TP1 = TP0
|
||
|
; new_init_call(N,Ngoal,Ent,S0,S1,Dfn0,Dfn1),
|
||
|
add_tab_ent(Ngoal,Ent,Tab0,Tab1),
|
||
|
oldt(N,Ngoal,Tab1,Tab2,S1,S2,Dfn1,Dfn2,maxint-maxint,Ndep,TP0,TP1),
|
||
|
compute_mins(Dep0,Ndep,pos,Dep1),
|
||
|
find(Tab2,Ngoal,Nent)
|
||
|
),
|
||
|
ent_to_comp(Nent,Ncomp),
|
||
|
ent_to_anss(Nent,Nanss),
|
||
|
( succeeded(Nanss) ->
|
||
|
Tab = Tab2, S = S2, Dfn = Dfn2, Dep = Dep1, TP = TP1
|
||
|
; failed(Nanss), Ncomp == true ->
|
||
|
apply_subst(Node,d(\+N,[]),Tab2,Tab,S2,S,Dfn2,Dfn,Dep1,Dep,TP1,TP)
|
||
|
; apply_subst(Node,d(\+N,[\+N]),Tab2,Tab,S2,S,Dfn2,Dfn,Dep1,Dep,TP1,TP)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
pos_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
Clause = rule(_H,[N|_B]),
|
||
|
Node = (Ggoal:Clause),
|
||
|
ground(N,Ngoal),
|
||
|
( isprolog(N) ->
|
||
|
findall(d(N,[]),call(N),Nanss),
|
||
|
map_anss_list(Nanss,Node,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
; ( find(Tab0,Ngoal,Nent) ->
|
||
|
ent_to_comp(Nent,Ncomp),
|
||
|
ent_to_anss(Nent,Nanss),
|
||
|
( Ncomp \== true ->
|
||
|
update_lookup_mins(Ggoal,Node,Ngoal,pos,Tab0,Tab1,Dep0,Dep1),
|
||
|
map_anss(Nanss,Node,Ngoal,Tab1,Tab,S0,S,Dfn0,Dfn,Dep1,Dep,TP0,TP)
|
||
|
; % N is completed.
|
||
|
map_anss(Nanss,Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
)
|
||
|
; % otherwise N is new
|
||
|
new_pos_call(Ngoal,Node,Ent,S0,S1,Dfn0,Dfn1),
|
||
|
add_tab_ent(Ngoal,Ent,Tab0,Tab1),
|
||
|
oldt(N,Ngoal,Tab1,Tab2,S1,S,Dfn1,Dfn,maxint-maxint,Ndep,TP0,TP),
|
||
|
update_solution_mins(Ggoal,Ngoal,pos,Tab2,Tab,Ndep,Dep0,Dep)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
aneg_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
Clause = rule(_H,all([\+N|_B])),
|
||
|
Node = (Ggoal:Clause),
|
||
|
ground(N,Ngoal),
|
||
|
( isprolog(N) ->
|
||
|
findall(d(N,[]),call(N),Nanss),
|
||
|
return_to_disj_list(Nanss,Node,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
; ( find(Tab0,Ngoal,Nent) ->
|
||
|
ent_to_comp(Nent,Ncomp),
|
||
|
ent_to_anss(Nent,Nanss),
|
||
|
( Ncomp \== true ->
|
||
|
update_lookup_mins(Ggoal,Node,Ngoal,aneg,Tab0,Tab,Dep0,Dep),
|
||
|
S = S0, Dfn = Dfn0, TP = TP0
|
||
|
; % N is completed.
|
||
|
return_to_disj(Nanss,Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
)
|
||
|
; % otherwise N is new
|
||
|
new_aneg_call(Ngoal,Node,Ent,S0,S1,Dfn0,Dfn1),
|
||
|
add_tab_ent(Ngoal,Ent,Tab0,Tab1),
|
||
|
oldt(N,Ngoal,Tab1,Tab2,S1,S,Dfn1,Dfn,maxint-maxint,Ndep,TP0,TP),
|
||
|
update_solution_mins(Ggoal,Ngoal,pos,Tab2,Tab,Ndep,Dep0,Dep)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
apos_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
Clause = rule(d(H,D),all([N|B])),
|
||
|
( ground(N) -> true
|
||
|
; write('Flounder in a universal disjunction: '),
|
||
|
write(N),
|
||
|
nl,
|
||
|
fail
|
||
|
),
|
||
|
pos_edge(rule(d(H,[]),[N]),Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
edge_oldt(rule(d(H,D),all(B)),Ggoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
apply_subst(Ggoal:Cl,d(An,Vr),Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
copy_term(Cl,rule(d(Ac,Vc),Body)),
|
||
|
( Body = [Call|NBody] ->
|
||
|
Call = An,
|
||
|
append(Vr,Vc,Vn)
|
||
|
; Body = all([Call|Calls]),
|
||
|
% Call = An, % An is the numbervar-ed version of Call.
|
||
|
( Vc == [] ->
|
||
|
Vn = all(Vr)
|
||
|
; Vc = all(Vc0),
|
||
|
append(Vr,Vc0,Vn0),
|
||
|
Vn = all(Vn0)
|
||
|
),
|
||
|
NBody = all(Calls)
|
||
|
),
|
||
|
edge_oldt(rule(d(Ac,Vn),NBody),Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP).
|
||
|
|
||
|
/* map_nodes(Nodes,Ans,....):
|
||
|
return Ans to each of the waiting nodes in Nodes, where a node
|
||
|
is of the form Ggoal:Clause.
|
||
|
*/
|
||
|
map_nodes([],_Ans,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
map_nodes([Node|Nodes],Ans,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
apply_subst(Node,Ans,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
map_nodes(Nodes,Ans,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
map_anss([],_Node,_Ngoal,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
map_anss(l(_GH,Lanss),Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
( Lanss == [] ->
|
||
|
Tab = Tab0, S = S0, Dfn = Dfn0, Dep = Dep0, TP = TP0
|
||
|
; Lanss = [Ans|_],
|
||
|
returned_ans(Ans,Ngoal,RAns),
|
||
|
apply_subst(Node,RAns,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
).
|
||
|
map_anss(n2(T1,_,T2),Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
map_anss(T1,Node,Ngoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
map_anss(T2,Node,Ngoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
map_anss(n3(T1,_,T2,_,T3),Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
map_anss(T1,Node,Ngoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
map_anss(T2,Node,Ngoal,Tab1,Tab2,S1,S2,Dfn1,Dfn2,Dep1,Dep2,TP1,TP2),
|
||
|
map_anss(T3,Node,Ngoal,Tab2,Tab,S2,S,Dfn2,Dfn,Dep2,Dep,TP2,TP).
|
||
|
|
||
|
map_anss_list([],_Node,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
map_anss_list([Ans|Lanss],Node,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
apply_subst(Node,Ans,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
map_anss_list(Lanss,Node,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
/* return_to_disj(Nanss,Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
Nanss: an answer table for Ngoal
|
||
|
Node: is of the form (Ggoal:Clause), where Clause is of the form
|
||
|
rule(d(H,D),all([\+N|B]))
|
||
|
It carries out resolution of each answer with Clause, and constructs
|
||
|
a new clause rule(Head,NBody), where the body is basically a
|
||
|
conjunction of all the resolvents. If a resolvent is a disjunction
|
||
|
or a non-ground literal, a new proposition is created (which is
|
||
|
actually represented by a number), which has a clause whose body
|
||
|
is the resolvent.
|
||
|
*/
|
||
|
return_to_disj(Nanss,Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
Node = (Ggoal : Clause),
|
||
|
Clause = rule(Head,all(Body)),
|
||
|
TP0 = (N0 : Tcl0),
|
||
|
negative_return_all(Nanss,Body,Ngoal,NBody,[],N0,N,Tcl0,Tcl),
|
||
|
TP1 = (N : Tcl),
|
||
|
edge_oldt(rule(Head,NBody),Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP1,TP).
|
||
|
|
||
|
negative_return_all([],_Body,_Ngoal,NBody,NBody,N,N,Tcl,Tcl).
|
||
|
negative_return_all(l(_GH,Lanss),Body,Ngoal,NBody0,NBody,N0,N,Tcl0,Tcl) :-
|
||
|
( Lanss == [] ->
|
||
|
NBody0 = NBody, N = N0, Tcl = Tcl0
|
||
|
; Lanss = [Ans|_],
|
||
|
negative_return_one(Ans,Body,Ngoal,NBody0,NBody,N0,N,Tcl0,Tcl)
|
||
|
).
|
||
|
negative_return_all(n2(T1,_,T2),Body,Ngoal,NBody0,NBody,N0,N,Tcl0,Tcl) :-
|
||
|
negative_return_all(T1,Body,Ngoal,NBody0,NBody1,N0,N1,Tcl0,Tcl1),
|
||
|
negative_return_all(T2,Body,Ngoal,NBody1,NBody,N1,N,Tcl1,Tcl).
|
||
|
negative_return_all(n3(T1,_,T2,_,T3),Body,Ngoal,NBody0,NBody,N0,N,Tcl0,Tcl) :-
|
||
|
negative_return_all(T1,Body,Ngoal,NBody0,NBody1,N0,N1,Tcl0,Tcl1),
|
||
|
negative_return_all(T2,Body,Ngoal,NBody1,NBody2,N1,N2,Tcl1,Tcl2),
|
||
|
negative_return_all(T3,Body,Ngoal,NBody2,NBody,N2,N,Tcl2,Tcl).
|
||
|
|
||
|
negative_return_one(d(H,Tv),Body,Ngoal,NBody0,NBody,N0,N,Tcl0,Tcl) :-
|
||
|
copy_term(Body,[\+Call|Bs]),
|
||
|
H = Call,
|
||
|
( Tv == [] -> % no delay
|
||
|
( (Bs = [Lit], ground(Lit)) -> % resovlent is a ground literal
|
||
|
NBody0 = [Lit|NBody],
|
||
|
N = N0, Tcl = Tcl0
|
||
|
; Lit = N0, % otherwise, replace it with a number
|
||
|
N is N0+1,
|
||
|
NBody0 = [Lit|NBody],
|
||
|
Clause = rule(d(Lit,[]),all(Bs)),
|
||
|
add_tab_ent(Lit,Clause,Tcl0,Tcl)
|
||
|
)
|
||
|
; ( ground(H) -> % if there is delay, always replace with number
|
||
|
NewTv = [\+H]
|
||
|
; ground(H,GH),
|
||
|
NewTv = [Ngoal - (\+GH)]
|
||
|
),
|
||
|
Lit = N0,
|
||
|
N is N0+1,
|
||
|
NBody0 = [Lit|NBody],
|
||
|
Clause = rule(d(Lit,all(NewTv)),all(Bs)),
|
||
|
add_tab_ent(Lit,Clause,Tcl0,Tcl)
|
||
|
).
|
||
|
|
||
|
return_to_disj_list(Lanss,Node,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
Node = (Ggoal : Clause),
|
||
|
Clause = rule(Head,all(Body)),
|
||
|
TP0 = (N0 : Tcl0),
|
||
|
negative_return_list(Lanss,Body,NBody,[],N0,N,Tcl0,Tcl),
|
||
|
TP1 = (N : Tcl),
|
||
|
edge_oldt(rule(Head,NBody),Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP1,TP).
|
||
|
|
||
|
negative_return_list([],_Body,NBody,NBody,N,N,Tcl,Tcl).
|
||
|
negative_return_list([d(H,[])|Lanss],Body,NBody0,NBody,N0,N,Tcl0,Tcl) :-
|
||
|
copy_term(Body,[\+Call|Bs]),
|
||
|
H = Call,
|
||
|
( Bs = [Lit], ground(Lit) ->
|
||
|
NBody0 = [Lit|NBody1],
|
||
|
N1 = N0, Tcl1 = Tcl0
|
||
|
; Lit = N0,
|
||
|
N1 is N0+1,
|
||
|
NBody0 = [Lit|NBody1],
|
||
|
Clause = rule(d(Lit,[]),all(Bs)),
|
||
|
add_tab_ent(Lit,Clause,Tcl0,Tcl1)
|
||
|
),
|
||
|
negative_return_list(Lanss,Body,NBody1,NBody,N1,N,Tcl1,Tcl).
|
||
|
|
||
|
/* comp_tab_ent(Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP)
|
||
|
check if Ggoal and subgoals on top of it on the stack are
|
||
|
completely evaluated.
|
||
|
*/
|
||
|
comp_tab_ent(Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
( Dep0 == maxint-maxint ->
|
||
|
process_pos_scc(Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep,TP0,TP)
|
||
|
; update_mins(Ggoal,Dep0,pos,Tab0,Tab1,Gdfn,Gdep),
|
||
|
Gdep = Gpmin-Gnmin,
|
||
|
( Gdfn @=< Gpmin, Gnmin == maxint ->
|
||
|
process_pos_scc(Ggoal,Tab1,Tab,S0,S,Dfn0,Dfn,Dep,TP0,TP)
|
||
|
; Gdfn @=< Gpmin, Gdfn @=< Gnmin ->
|
||
|
process_neg_scc(Ggoal,Tab1,Tab,S0,S,Dfn0,Dfn,Dep,TP0,TP)
|
||
|
; Tab = Tab1, S0 = S, Dfn = Dfn0, Dep = Gdep, TP = TP0
|
||
|
)
|
||
|
).
|
||
|
|
||
|
process_pos_scc(Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep,TP0,TP) :-
|
||
|
( wfs_trace ->
|
||
|
write('Stack: '), nl, display_stack(S0,Tab0),
|
||
|
write('Completed call found: '), write(Ggoal), nl,
|
||
|
display_table(Tab0),
|
||
|
write('Completing calls ......'), nl, nl
|
||
|
; true
|
||
|
),
|
||
|
pop_subgoals(Ggoal,S0,S1,[],Scc),
|
||
|
complete_comp(Scc,Tab0,Tab1,Alist,[]),
|
||
|
return_aneg_nodes(Alist,Tab1,Tab,S1,S,Dfn0,Dfn,maxint-maxint,Dep,TP0,TP).
|
||
|
|
||
|
/* pop_subgoals(Ggoal,S0,S,Scc0,Scc)
|
||
|
pop off the stack subgoals up to and including Ggoal
|
||
|
*/
|
||
|
pop_subgoals(Ggoal,S0,S,Scc0,Scc) :-
|
||
|
S0 = [Sent|S1],
|
||
|
( Ggoal == Sent ->
|
||
|
S = S1,
|
||
|
Scc = [Sent|Scc0]
|
||
|
; pop_subgoals(Ggoal,S1,S,[Sent|Scc0],Scc)
|
||
|
).
|
||
|
|
||
|
/* complete_comp(Scc,Tab0,Tab,Alist0,Alist):
|
||
|
process the list Scc of subgoals that are
|
||
|
completely evaluated.
|
||
|
*/
|
||
|
complete_comp([],Tab,Tab,Alist,Alist).
|
||
|
complete_comp([Ggoal|Scc],Tab0,Tab,Alist0,Alist) :-
|
||
|
complete_one(Ggoal,Tab0,Tab1,Alist0,Alist1),
|
||
|
complete_comp(Scc,Tab1,Tab,Alist1,Alist).
|
||
|
|
||
|
/* complete_one(Ggoal,Tab0,Tab,Alist0,Alist)
|
||
|
process one subgoal that has been completely
|
||
|
evaluated:
|
||
|
1. set its Nodes and Negs to [] and Comp to true;
|
||
|
2. simplify its answers and set up links
|
||
|
for further simplification later;
|
||
|
3. use the truth value of Ggoal to simplify
|
||
|
answers of other complete subgoals (possibly
|
||
|
including itself).
|
||
|
4. set Alist0/Alist: a list of negation nodes with
|
||
|
universal disjunctions with associated answers
|
||
|
for the selected negative literal.
|
||
|
*/
|
||
|
complete_one(Ggoal,Tab0,Tab,Alist0,Alist) :-
|
||
|
updatevs(Tab0,Ggoal,Ent0,Ent,Tab1),
|
||
|
Ent0 = e(_Nodes,ANegs,Anss0,Delay,_Comp,Gdfn,Slist0),
|
||
|
Ent = e([],[],Anss,Delay,true,Gdfn,Slist),
|
||
|
( Delay == true ->
|
||
|
reduce_ans(Anss0,Anss,Tab0),
|
||
|
setup_simp_links(Anss,Ggoal,Slist0,Slist1,Tab1,Tab2)
|
||
|
; % Delay == false
|
||
|
Anss = Anss0,
|
||
|
Tab2 = Tab1,
|
||
|
Slist1 = Slist0
|
||
|
),
|
||
|
extract_known(Ggoal,Anss,Slist1,Slist,Klist),
|
||
|
simplify(Klist,Tab2,Tab,[]),
|
||
|
( ANegs == [] ->
|
||
|
Alist0 = Alist
|
||
|
; Alist0 = [(Anss,Ggoal)-ANegs|Alist]
|
||
|
).
|
||
|
|
||
|
setup_simp_links([],_,Slist,Slist,Tab,Tab).
|
||
|
setup_simp_links(l(GH,Lanss),Ggoal,Slist0,Slist,Tab0,Tab) :-
|
||
|
setup_simp_links_list(Lanss,Ggoal-GH,Ggoal,Slist0,Slist,Tab0,Tab).
|
||
|
setup_simp_links(n2(T1,_,T2),Ggoal,Slist0,Slist,Tab0,Tab) :-
|
||
|
setup_simp_links(T1,Ggoal,Slist0,Slist1,Tab0,Tab1),
|
||
|
setup_simp_links(T2,Ggoal,Slist1,Slist,Tab1,Tab).
|
||
|
setup_simp_links(n3(T1,_,T2,_,T3),Ggoal,Slist0,Slist,Tab0,Tab) :-
|
||
|
setup_simp_links(T1,Ggoal,Slist0,Slist1,Tab0,Tab1),
|
||
|
setup_simp_links(T2,Ggoal,Slist1,Slist2,Tab1,Tab2),
|
||
|
setup_simp_links(T3,Ggoal,Slist2,Slist,Tab2,Tab).
|
||
|
|
||
|
/* setup_simp_link_list(Lanss,Ggoal-GH,Ggoal,Slist0,Slist,Tab0,Tab)
|
||
|
Ggoal-GH is to tell what portion of answers of Ggoal can be
|
||
|
simplified.
|
||
|
*/
|
||
|
setup_simp_links_list([],_,_,Slist,Slist,Tab,Tab).
|
||
|
setup_simp_links_list([d(_,D)|Anss],GHead,Ggoal,Slist0,Slist,Tab0,Tab) :-
|
||
|
( D = all(Ds) ->
|
||
|
true
|
||
|
; Ds = D
|
||
|
),
|
||
|
links_from_one_delay(Ds,GHead,Ggoal,Slist0,Slist1,Tab0,Tab1),
|
||
|
setup_simp_links_list(Anss,GHead,Ggoal,Slist1,Slist,Tab1,Tab).
|
||
|
|
||
|
/* A link ((Ggoal-GH):Lit) in an entry for Ngoal means that
|
||
|
the literal Lit in an answer with head GH in Ggoal can
|
||
|
be potentially simplified if we know answers for Ngoal.
|
||
|
*/
|
||
|
links_from_one_delay([],_,_,Slist,Slist,Tab,Tab).
|
||
|
links_from_one_delay([D|Ds],GHead,Ggoal,Slist0,Slist,Tab0,Tab) :-
|
||
|
( D = (\+ Ngoal) ->
|
||
|
( Ggoal == Ngoal ->
|
||
|
Tab1 = Tab0,
|
||
|
Slist1 = [GHead:D|Slist0]
|
||
|
; add_link_to_ent(Tab0,Ngoal,GHead:D,Tab1),
|
||
|
Slist1 = Slist0
|
||
|
)
|
||
|
; D = (Ngoal-_) ->
|
||
|
( Ggoal == Ngoal ->
|
||
|
Slist1 = [GHead:D|Slist0],
|
||
|
Tab1 = Tab0
|
||
|
; Slist1 = Slist0,
|
||
|
add_link_to_ent(Tab0,Ngoal,GHead:D,Tab1)
|
||
|
)
|
||
|
),
|
||
|
links_from_one_delay(Ds,GHead,Ggoal,Slist1,Slist,Tab1,Tab).
|
||
|
|
||
|
/* extract_known(Ggoal,Anss,Links,Slist,Klist):
|
||
|
Given Ggoal and its answers Anss, and its
|
||
|
simplification Links, it partitioned Links
|
||
|
into Slist and Klist of links, where Klist
|
||
|
is a list of links that are known to be either
|
||
|
true or false.
|
||
|
|
||
|
Klist is either of the form Val-Links, or a
|
||
|
list of the form Val-Link. In case of non-ground
|
||
|
calls, the corresponding portion of Anss has to
|
||
|
be searched.
|
||
|
*/
|
||
|
extract_known(Ggoal,Anss,Links,Slist,Klist) :-
|
||
|
( failed(Anss) ->
|
||
|
Klist = fail-Links,
|
||
|
Slist = []
|
||
|
; Anss = l(GH,Lanss) ->
|
||
|
( Ggoal == GH -> % Ground or most general call
|
||
|
( memberchk(d(_,[]),Lanss) ->
|
||
|
Klist = succ-Links,
|
||
|
Slist = []
|
||
|
; Klist = [],
|
||
|
Slist = Links
|
||
|
)
|
||
|
; % non-ground call
|
||
|
extract_known_anss(Links,Anss,[],Slist,[],Klist)
|
||
|
)
|
||
|
; % non-ground call
|
||
|
extract_known_anss(Links,Anss,[],Slist,[],Klist)
|
||
|
).
|
||
|
|
||
|
extract_known_anss([],_,Slist,Slist,Klist,Klist).
|
||
|
extract_known_anss([Link|Links],Anss,Slist0,Slist,Klist0,Klist) :-
|
||
|
Link = (_:Lit),
|
||
|
extract_lit_val(Lit,Anss,true,Val),
|
||
|
( Val == undefined ->
|
||
|
Slist1 = [Link|Slist0],
|
||
|
Klist1 = Klist0
|
||
|
; Slist1 = Slist0,
|
||
|
Klist1 = [Val-Link|Klist0]
|
||
|
),
|
||
|
extract_known_anss(Links,Anss,Slist1,Slist,Klist1,Klist).
|
||
|
|
||
|
/* extract_lit_val(Lit,Anss,Comp,Val):
|
||
|
extract the truth value of Lit according to Anss and Comp.
|
||
|
In case of a non-ground calls, the corresponding portion
|
||
|
of Anss has to be searched.
|
||
|
*/
|
||
|
extract_lit_val(Lit,Anss,Comp,Val) :-
|
||
|
( Lit = (\+ _) ->
|
||
|
( succeeded(Anss) ->
|
||
|
Val = fail
|
||
|
; failed(Anss), Comp == true ->
|
||
|
Val = succ
|
||
|
; Val = undefined
|
||
|
)
|
||
|
; Lit = (_ - (\+GH)) ->
|
||
|
( find(Anss,GH,Lanss) ->
|
||
|
( (\+ \+ memberchk(d(GH,[]),Lanss)) ->
|
||
|
Val = fail
|
||
|
; Lanss == [], Comp == true ->
|
||
|
Val = succ
|
||
|
; Val = undefined
|
||
|
)
|
||
|
; ( Comp == true ->
|
||
|
Val = succ
|
||
|
; Val = undefined
|
||
|
)
|
||
|
)
|
||
|
; Lit = (_-GH) ->
|
||
|
( find(Anss,GH,Lanss) ->
|
||
|
( (\+ \+ memberchk(d(GH,[]),Lanss)) ->
|
||
|
Val = succ
|
||
|
; Lanss == [], Comp == true ->
|
||
|
Val = fail
|
||
|
; Val = undefined
|
||
|
)
|
||
|
; ( Comp == true ->
|
||
|
Val = fail
|
||
|
; Val = undefined
|
||
|
)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
/* simplify(KnownLinks,Tab0,Tab,Abd):
|
||
|
Given a list of KnownLinks, Tab0 and Abd,
|
||
|
it tries to simplify answers according to
|
||
|
KnownLinks. When a subgoal is found to be
|
||
|
true or false according to answers,
|
||
|
consistency with assumed truth values in Abd
|
||
|
is checked.
|
||
|
*/
|
||
|
simplify([],Tab,Tab,_Abd).
|
||
|
simplify([Val-Link|Klist],Tab0,Tab,Abd) :-
|
||
|
simplify_one(Val,Link,Tab0,Tab1,Abd),
|
||
|
simplify(Klist,Tab1,Tab,Abd).
|
||
|
simplify(Val-Links,Tab0,Tab,Abd) :-
|
||
|
simplify_list(Links,Val,Tab0,Tab,Abd).
|
||
|
|
||
|
simplify_list([],_,Tab,Tab,_Abd).
|
||
|
simplify_list([Link|Links],Val,Tab0,Tab,Abd) :-
|
||
|
Link = (_ : Lit),
|
||
|
( ( Lit = (\+_); Lit = (_ - (\+_)) ) ->
|
||
|
( Val = fail -> LVal = succ; LVal = fail )
|
||
|
; LVal = Val
|
||
|
),
|
||
|
simplify_one(LVal,Link,Tab0,Tab1,Abd),
|
||
|
simplify_list(Links,Val,Tab1,Tab,Abd).
|
||
|
|
||
|
simplify_one(Val,Link,Tab0,Tab,Abd) :-
|
||
|
Link = ((Ngoal - GH) : Lit),
|
||
|
updatevs(Tab0,Ngoal,Ent0,Ent,Tab1),
|
||
|
Ent0 = e(Nodes,ANegs,Anss0,Delay,Comp,Dfn,Slist0),
|
||
|
Ent = e(Nodes,ANegs,Anss,Delay,Comp,Dfn,Slist),
|
||
|
( updatevs(Anss0,GH,Lanss0,Lanss,Anss) ->
|
||
|
simplify_anss(Lanss0,Val,Lit,[],Lanss,C),
|
||
|
( C == true ->
|
||
|
( find(Abd,GH,Aval) ->
|
||
|
( Aval == true, Lanss == [] -> % deduced result inconsistent with assumption
|
||
|
fail
|
||
|
; Aval == false, memberchk( d(_ , []), Lanss) ->
|
||
|
fail
|
||
|
; true
|
||
|
)
|
||
|
; true
|
||
|
),
|
||
|
extract_known(Ngoal,Anss,Slist0,Slist,Klist),
|
||
|
simplify(Klist,Tab1,Tab,Abd)
|
||
|
; Tab = Tab0
|
||
|
)
|
||
|
; Tab = Tab0
|
||
|
).
|
||
|
|
||
|
/* simplify_anss(List,Val,Lit,Lanss0,Lanss,C):
|
||
|
Given a List of answers, Val of Lit, it
|
||
|
simplifies the List and construct a new list
|
||
|
Lanss0/Lanss of answers. C is unified with true
|
||
|
if some simplification is carried out.
|
||
|
|
||
|
As soon as a true answer is detected, all
|
||
|
other answers with the same head are deleted.
|
||
|
*/
|
||
|
simplify_anss([],_,_,Anss,Anss,_).
|
||
|
simplify_anss([Ans|Rest],Val,Lit,Anss0,Anss,C) :-
|
||
|
( simplified_ans(Ans,Val,Lit,NewAns,C) ->
|
||
|
( NewAns = d(_,[]) ->
|
||
|
Anss = [NewAns]
|
||
|
; Anss1 = [NewAns|Anss0],
|
||
|
simplify_anss(Rest,Val,Lit,Anss1,Anss,C)
|
||
|
)
|
||
|
; C = true,
|
||
|
simplify_anss(Rest,Val,Lit,Anss0,Anss,C)
|
||
|
).
|
||
|
|
||
|
simplified_ans(Ans,Val,Lit,NewAns,C) :-
|
||
|
Ans = d(H,Ds),
|
||
|
( Ds == [] ->
|
||
|
NewAns = Ans
|
||
|
; Ds = all(Dlist) ->
|
||
|
( Val == fail ->
|
||
|
delete_lit(Dlist,Lit,NewDlist,[],C),
|
||
|
( NewDlist == [] ->
|
||
|
fail
|
||
|
; NewAns = d(H,all(NewDlist))
|
||
|
)
|
||
|
; % Val == succ ->
|
||
|
( memberchk(Lit,Dlist) ->
|
||
|
NewAns = d(H,[]),
|
||
|
C = true
|
||
|
; NewAns = Ans
|
||
|
)
|
||
|
)
|
||
|
; % Ds is a conjunction
|
||
|
( Val == fail ->
|
||
|
( memberchk(Lit,Ds) ->
|
||
|
fail
|
||
|
; NewAns = Ans
|
||
|
)
|
||
|
; % Val == succ ->
|
||
|
delete_lit(Ds,Lit,NewDs,[],C),
|
||
|
NewAns = d(H,NewDs)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
/* delete_lit(Delays,Lit,Ds0,Ds,C):
|
||
|
deletes Lit from Delays. Delays is
|
||
|
a list of delayed literals and it
|
||
|
is guaranteed to have no duplicates.
|
||
|
*/
|
||
|
delete_lit([],_,Ds,Ds,_).
|
||
|
delete_lit([D|Rest],Lit,Ds0,Ds,C) :-
|
||
|
( D == Lit ->
|
||
|
Ds0 = Rest,
|
||
|
C = true
|
||
|
; Ds0 = [D|Ds1],
|
||
|
delete_lit(Rest,Lit,Ds1,Ds,C)
|
||
|
).
|
||
|
|
||
|
% return answers to negative nodes within universal disjunctions
|
||
|
return_aneg_nodes([],Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
return_aneg_nodes([(Anss,Ngoal)-ANegs|Alist],Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
map_anegs(ANegs,Anss,Ngoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
return_aneg_nodes(Alist,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
map_anegs([],_Anss,_Ngoal,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
map_anegs([Node|ANegs],Anss,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
return_to_disj(Anss,Node,Ngoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
map_anegs(ANegs,Anss,Ngoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
/* process a component of subgoals that may be involved in
|
||
|
negative loops.
|
||
|
*/
|
||
|
process_neg_scc(Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep,TP0,TP) :-
|
||
|
( wfs_trace ->
|
||
|
write('Stack: '), nl, display_stack(S0,Tab0),
|
||
|
write('Possible negative loop: '), write(Ggoal), nl,
|
||
|
display_table(Tab0)
|
||
|
; true
|
||
|
),
|
||
|
extract_subgoals(Ggoal,S0,Scc,[]),
|
||
|
reset_nmin(Scc,Tab0,Tab1,Ds,[]),
|
||
|
( wfs_trace ->
|
||
|
write('Delaying: '), display_dlist(Ds)
|
||
|
; true
|
||
|
),
|
||
|
delay_and_cont(Ds,Tab1,Tab2,S0,S1,Dfn0,Dfn1,maxint-maxint,Dep1,TP0,TP1),
|
||
|
recomp_scc(Scc,Tab2,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
/* extract_subgoals(Ggoal,S0,Scc0,Scc)
|
||
|
extract subgoals that may be involved in negative loops,
|
||
|
but leave the stack of subgoals intact.
|
||
|
*/
|
||
|
extract_subgoals(Ggoal,[Sent|S],[Sent|Scc0],Scc) :-
|
||
|
( Ggoal == Sent ->
|
||
|
Scc0 = Scc
|
||
|
; extract_subgoals(Ggoal,S,Scc0,Scc)
|
||
|
).
|
||
|
|
||
|
/* reset_nmin(Scc,Tab0,Tab,Dnodes0,Dnodes)
|
||
|
reset NegLink and collect all waiting nodes that need to be
|
||
|
delayed. Dnodes0/Dnodes is a difference list.
|
||
|
*/
|
||
|
reset_nmin([],Tab,Tab,Ds,Ds).
|
||
|
reset_nmin([Ggoal|Scc],Tab0,Tab,Ds0,Ds) :-
|
||
|
get_and_reset_negs(Tab0,Ggoal,ANegs,Tab1),
|
||
|
( ANegs == [] ->
|
||
|
Ds0 = Ds1
|
||
|
; Ds0 = [Ggoal-ANegs|Ds1]
|
||
|
),
|
||
|
reset_nmin(Scc,Tab1,Tab,Ds1,Ds).
|
||
|
|
||
|
delay_and_cont([],Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
delay_and_cont([Ggoal-Negs|Dnodes],Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
map_nodes(Negs,d(\+Ggoal,[\+Ggoal]),Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
delay_and_cont(Dnodes,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
recomp_scc([],Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP).
|
||
|
recomp_scc([Ggoal|Scc],Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP) :-
|
||
|
comp_tab_ent(Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1),
|
||
|
recomp_scc(Scc,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
|
||
|
|
||
|
/* routines for incremental update of dependency information
|
||
|
*/
|
||
|
|
||
|
/* update_mins(Ggoal,Dep,Sign,Tab0,Tab,Gdfn,Gdep)
|
||
|
update the PosLink and NegLink of Ggoal according to
|
||
|
Dep and Sign
|
||
|
*/
|
||
|
update_mins(Ggoal,Dep,Sign,Tab0,Tab,Gdfn,Gdep) :-
|
||
|
Ent0 = e(Nodes,ANegs,Anss,Delay,Comp,Gdfn:Gdep0,Slist),
|
||
|
Ent = e(Nodes,ANegs,Anss,Delay,Comp,Gdfn:Gdep,Slist),
|
||
|
updatevs(Tab0,Ggoal,Ent0,Ent,Tab),
|
||
|
compute_mins(Gdep0,Dep,Sign,Gdep).
|
||
|
|
||
|
/* update_lookup_mins(Ggoal,Node,Ngoal,Sign,Tab0,Tab,Dep0,Dep)
|
||
|
There is a lookup edge (Node) from Ggoal to Ngoal
|
||
|
with Sign. It adds Node to the corresponding waiting list
|
||
|
in Ngoal and then update the dependencies of Ggoal.
|
||
|
*/
|
||
|
update_lookup_mins(Ggoal,Node,Ngoal,Sign,Tab0,Tab,Dep0,Dep) :-
|
||
|
updatevs(Tab0,Ngoal,Ent0,Ent,Tab1),
|
||
|
( Sign == pos ->
|
||
|
pos_to_newent(Ent0,Ent,Node)
|
||
|
; Sign == aneg ->
|
||
|
aneg_to_newent(Ent0,Ent,Node)
|
||
|
),
|
||
|
Ent0 = e(_,_,_,_,_,_Ndfn:Ndep,_),
|
||
|
compute_mins(Dep0,Ndep,Sign,Dep),
|
||
|
update_mins(Ggoal,Ndep,Sign,Tab1,Tab,_,_).
|
||
|
|
||
|
/* update_solution_mins(Ggoal,Ngoal,Sign,Tab0,Tab,Ndep,Dep0,Dep)
|
||
|
There is an edge with Sign from Ggoal to Ngoal, where Ngoal is
|
||
|
a new subgoal. Ndep is the final dependency information of
|
||
|
Ngoal. Dep0/Dep is for the most recent enclosing new call.
|
||
|
This predicate is called after Ngoal is solved.
|
||
|
*/
|
||
|
update_solution_mins(Ggoal,Ngoal,Sign,Tab0,Tab,Ndep,Dep0,Dep) :-
|
||
|
find(Tab0,Ngoal,Nent),
|
||
|
ent_to_comp(Nent,Ncomp),
|
||
|
( Ncomp == true ->
|
||
|
( Ndep == maxint-maxint ->
|
||
|
Tab = Tab0, Dep = Dep0
|
||
|
; update_mins(Ggoal,Ndep,pos,Tab0,Tab,_,_),
|
||
|
compute_mins(Dep0,Ndep,pos,Dep)
|
||
|
)
|
||
|
; update_mins(Ggoal,Ndep,Sign,Tab0,Tab,_,_),
|
||
|
compute_mins(Dep0,Ndep,Sign,Dep)
|
||
|
).
|
||
|
|
||
|
compute_mins(Gpmin-Gnmin,Npmin-Nnmin,Sign,Newpmin-Newnmin) :-
|
||
|
( Sign == pos ->
|
||
|
min(Gpmin,Npmin,Newpmin),
|
||
|
min(Gnmin,Nnmin,Newnmin)
|
||
|
; % (Sign == neg; Sign == aneg) ->
|
||
|
Newpmin=Gpmin,
|
||
|
min(Gnmin,Npmin,Imin),
|
||
|
min(Imin,Nnmin,Newnmin)
|
||
|
).
|
||
|
|
||
|
min(X,Y,M) :- ( X @< Y -> M=X; M=Y ).
|
||
|
|
||
|
%%%%%%%%%%%%%%% Local table manipulation predicates %%%%%%%%%%
|
||
|
|
||
|
/* Table Entry Structure:
|
||
|
For each Call, its table entry is identified with its number-vared
|
||
|
version -- Ggoal. Its value is a term of the form
|
||
|
|
||
|
e(Nodes,ANegs,Anss,Delay,Comp,Dfn:Dep,Slist)
|
||
|
|
||
|
where
|
||
|
Nodes: positive suspension list
|
||
|
ANegs: negative suspension list (for universal disjunction clauss)
|
||
|
Anss: another table.
|
||
|
Delay: whether Anss contains any answer with delay
|
||
|
Comp: whether Call is completely evaluated or not
|
||
|
Dfn: depth-first number of Gcall
|
||
|
Dep: (PosLink-NegLink) --- dependency information
|
||
|
Slist: a list of nodes whose answers may be simplified
|
||
|
if the truth value of Ggoal is known. Each element of Slist
|
||
|
is of the form (Ngoal-GH):Literal.
|
||
|
Stack Entry Structure:
|
||
|
Ggoal
|
||
|
*/
|
||
|
|
||
|
/* routines for accessing individual fields of an entry
|
||
|
*/
|
||
|
ent_to_nodes(e(Nodes,_,_,_,_,_,_),Nodes).
|
||
|
ent_to_anegs(e(_,ANegs,_,_,_,_,_),ANegs).
|
||
|
ent_to_anss(e(_,_,Anss,_,_,_,_),Anss).
|
||
|
ent_to_delay(e(_,_,_,Delay,_,_,_),Delay).
|
||
|
ent_to_comp(e(_,_,_,_,Comp,_,_),Comp).
|
||
|
ent_to_dfn(e(_,_,_,_,_,Dfn,_),Dfn).
|
||
|
ent_to_slist(e(_,_,_,_,_,_,Slist),Slist).
|
||
|
|
||
|
get_and_reset_negs(Tab0,Ggoal,ANegs,Tab) :-
|
||
|
Ent0 = e(Nodes,ANegs,Anss,Delay,Comp,Gdfn: (Gpmin - _),Slist),
|
||
|
Ent = e(Nodes,[],Anss,Delay,Comp,Gdfn:Gpmin-maxint,Slist),
|
||
|
updatevs(Tab0,Ggoal,Ent0,Ent,Tab).
|
||
|
|
||
|
/* adding a new table entry
|
||
|
*/
|
||
|
add_tab_ent(Ggoal,Ent,Tab0,Tab) :-
|
||
|
addkey(Tab0,Ggoal,Ent,Tab).
|
||
|
|
||
|
/* The following three routines are for creating
|
||
|
new calls
|
||
|
*/
|
||
|
/* a new call with empty suspensions
|
||
|
*/
|
||
|
new_init_call(Call,Ggoal,Ent,S0,S,Dfn0,Dfn) :-
|
||
|
ground(Call,Ggoal),
|
||
|
S = [Ggoal|S0],
|
||
|
Dfn is Dfn0+1,
|
||
|
Ent = e([],[],[],false,false,Dfn0:Dfn0-maxint,[]).
|
||
|
|
||
|
/* a new call with an initial negative suspension from
|
||
|
inside a universal disjunction
|
||
|
*/
|
||
|
new_aneg_call(Ngoal,Neg,Ent,S0,S,Dfn0,Dfn) :-
|
||
|
S = [Ngoal|S0],
|
||
|
Dfn is Dfn0+1,
|
||
|
Ent = e([],[Neg],[],false,false,Dfn0:Dfn0-maxint,[]).
|
||
|
|
||
|
/* a new call with an initial positive suspension
|
||
|
*/
|
||
|
new_pos_call(Ngoal,Node,Ent,S0,S,Dfn0,Dfn) :-
|
||
|
S = [Ngoal|S0],
|
||
|
Dfn is Dfn0+1,
|
||
|
Ent = e([Node],[],[],false,false,Dfn0:Dfn0-maxint,[]).
|
||
|
|
||
|
/* routines for adding more information to a
|
||
|
table entry.
|
||
|
*/
|
||
|
aneg_to_newent(Ent0,Ent,ANeg) :-
|
||
|
Ent0 = e(Nodes,ANegs,Anss,Delay,Comp,Dfn,Slist),
|
||
|
Ent = e(Nodes,[ANeg|ANegs],Anss,Delay,Comp,Dfn,Slist).
|
||
|
|
||
|
pos_to_newent(Ent0,Ent,Node) :-
|
||
|
Ent0 = e(Nodes,ANegs,Anss,Delay,Comp,Dfn,Slist),
|
||
|
Ent = e([Node|Nodes],ANegs,Anss,Delay,Comp,Dfn,Slist).
|
||
|
|
||
|
add_link_to_ent(Tab0,Ggoal,Link,Tab) :-
|
||
|
updatevs(Tab0,Ggoal,Ent0,Ent,Tab),
|
||
|
link_to_newent(Ent0,Ent,Link).
|
||
|
|
||
|
link_to_newent(Ent0,Ent,Link) :-
|
||
|
Ent0 = e(Nodes,ANegs,Anss,Delay,Comp,Dfn,Slist),
|
||
|
Ent = e(Nodes,ANegs,Anss,Delay,Comp,Dfn,[Link|Slist]).
|
||
|
|
||
|
/* routines for manipulating answers */
|
||
|
ansstree_to_list([],L,L).
|
||
|
ansstree_to_list(l(_GH,Lanss),L0,L) :-
|
||
|
attach(Lanss,L0,L).
|
||
|
ansstree_to_list(n2(T1,_M,T2),L0,L) :-
|
||
|
ansstree_to_list(T1,L0,L1),
|
||
|
ansstree_to_list(T2,L1,L).
|
||
|
ansstree_to_list(n3(T1,_M2,T2,_M3,T3),L0,L) :-
|
||
|
ansstree_to_list(T1,L0,L1),
|
||
|
ansstree_to_list(T2,L1,L2),
|
||
|
ansstree_to_list(T3,L2,L).
|
||
|
|
||
|
attach([],L,L).
|
||
|
attach([d(H,B)|R],[X|L0],L) :-
|
||
|
( B == [] ->
|
||
|
X = H
|
||
|
; X = (H <- B)
|
||
|
),
|
||
|
attach(R,L0,L).
|
||
|
|
||
|
member_anss(Ans,Anss) :-
|
||
|
member_anss_1(Anss,Ans).
|
||
|
|
||
|
member_anss_1(l(_,Lanss),Ans) :-
|
||
|
member(Ans,Lanss).
|
||
|
member_anss_1(n2(T1,_,T2),Ans) :-
|
||
|
( member_anss_1(T1,Ans)
|
||
|
; member_anss_1(T2,Ans)
|
||
|
).
|
||
|
member_anss_1(n3(T1,_,T2,_,T3),Ans) :-
|
||
|
( member_anss_1(T1,Ans)
|
||
|
; member_anss_1(T2,Ans)
|
||
|
; member_anss_1(T3,Ans)
|
||
|
).
|
||
|
|
||
|
/* failed(Anss): Anss is empty */
|
||
|
failed([]).
|
||
|
failed(l(_,[])).
|
||
|
|
||
|
/* succeeded(Anss): Anss contains a single definite answer */
|
||
|
succeeded(l(_,Lanss)) :-
|
||
|
memberchk(d(_,[]),Lanss).
|
||
|
|
||
|
/* add_ans(Tab0,Goal,Ans,Nodes,Mode,Tab):
|
||
|
If Ans is not subsumed by any existing answer then
|
||
|
Ans is added to Anss(Goal);
|
||
|
If some existing answer also has head H then
|
||
|
Mode = no_new_head
|
||
|
else
|
||
|
Mode = new_head
|
||
|
else
|
||
|
fail.
|
||
|
*/
|
||
|
add_ans(Tab0,Ggoal,Ans,Nodes,Mode,Tab) :-
|
||
|
updatevs(Tab0,Ggoal,Ent0,Ent,Tab),
|
||
|
Ans = d(H,Ds),
|
||
|
( Ds == [] ->
|
||
|
new_ans_ent(Ent0,Ent,Ans,Nodes,Mode)
|
||
|
; setof(X,member(X,Ds),NewDs),
|
||
|
new_ans_ent(Ent0,Ent,d(H,NewDs),Nodes,Mode)
|
||
|
).
|
||
|
|
||
|
new_ans_ent(Ent0,Ent,Ans,Nodes,Mode) :-
|
||
|
Ent0 = e(Nodes,ANegs,Anss0,Delay0,Comp,Dfn,Slist),
|
||
|
Ent = e(Nodes,ANegs,Anss,Delay,Comp,Dfn,Slist),
|
||
|
Ans = d(H,D),
|
||
|
ground(H,GH),
|
||
|
( updatevs(Anss0,GH,Lanss0,Lanss,Anss) ->
|
||
|
( D == [] ->
|
||
|
\+(memberchk(d(_,[]),Lanss0)),
|
||
|
Lanss = [Ans]
|
||
|
; not_subsumed_ans(Ans,Lanss0),
|
||
|
Lanss = [Ans|Lanss0]
|
||
|
),
|
||
|
Mode = no_new_head
|
||
|
; addkey(Anss0,GH,[Ans],Anss),
|
||
|
Mode = new_head
|
||
|
),
|
||
|
( D == [] ->
|
||
|
Delay = Delay0
|
||
|
; Delay = true
|
||
|
).
|
||
|
|
||
|
/* returned_ans(Ans,Ggoal,RAns):
|
||
|
determines whether SLG resolution or SLG factoring should
|
||
|
be applied.
|
||
|
*/
|
||
|
returned_ans(d(H,Tv),Ggoal,d(H,NewTv)) :-
|
||
|
( Tv = [] ->
|
||
|
NewTv = []
|
||
|
; ground(H,GH),
|
||
|
NewTv = [Ggoal-GH]
|
||
|
).
|
||
|
|
||
|
% reduce a list of answers, by reducing delay list, and by subsumption
|
||
|
reduce_ans(Anss0,Anss,Tab) :-
|
||
|
reduce_completed_ans(Anss0,Anss,Tab).
|
||
|
|
||
|
% simplify all the delay lists in a list of answers.
|
||
|
reduce_completed_ans([],[],_Tab).
|
||
|
reduce_completed_ans(l(GH,Lanss0),l(GH,Lanss),Tab) :-
|
||
|
reduce_completed_anslist(Lanss0,[],Lanss,Tab).
|
||
|
reduce_completed_ans(n2(T1,M,T2),n2(NT1,M,NT2),Tab) :-
|
||
|
reduce_completed_ans(T1,NT1,Tab),
|
||
|
reduce_completed_ans(T2,NT2,Tab).
|
||
|
reduce_completed_ans(n3(T1,M2,T2,M3,T3),n3(NT1,M2,NT2,M3,NT3),Tab) :-
|
||
|
reduce_completed_ans(T1,NT1,Tab),
|
||
|
reduce_completed_ans(T2,NT2,Tab),
|
||
|
reduce_completed_ans(T3,NT3,Tab).
|
||
|
|
||
|
reduce_completed_anslist([],Lanss,Lanss,_Tab).
|
||
|
reduce_completed_anslist([d(G,D0)|List],Lanss0,Lanss,Tab) :-
|
||
|
( D0 = all(Dlist1) ->
|
||
|
( filter_delays(Dlist1,[],Dlist,disj,V,Tab) ->
|
||
|
( V == true -> % true answer
|
||
|
Lanss = [d(G,[])]
|
||
|
; Dlist == [] -> % false answer, ignore
|
||
|
reduce_completed_anslist(List,Lanss0,Lanss,Tab)
|
||
|
; reduce_completed_anslist(List,[d(G,all(Dlist))|Lanss0],Lanss,Tab)
|
||
|
)
|
||
|
; reduce_completed_anslist(List,Lanss0,Lanss,Tab)
|
||
|
)
|
||
|
; ( filter_delays(D0,[],D,conj,_V,Tab) ->
|
||
|
( D == [] ->
|
||
|
Lanss = [d(G,[])]
|
||
|
; reduce_completed_anslist(List,[d(G,D)|Lanss0],Lanss,Tab)
|
||
|
)
|
||
|
; reduce_completed_anslist(List,Lanss0,Lanss,Tab)
|
||
|
)
|
||
|
).
|
||
|
|
||
|
% simplify a delay list by the completed table: delete true negations,
|
||
|
% fail if a false one.
|
||
|
filter_delays([],Fds,Fds,_DC,_V,_Tab).
|
||
|
filter_delays([Lit|Ds],Fds0,Fds,DC,V,Tab) :-
|
||
|
lit_to_call(Lit,Gcall),
|
||
|
find(Tab,Gcall,Gent),
|
||
|
ent_to_comp(Gent,Gcomp),
|
||
|
ent_to_anss(Gent,Ganss),
|
||
|
extract_lit_val(Lit,Ganss,Gcomp,Val),
|
||
|
( Val == succ ->
|
||
|
( DC == conj ->
|
||
|
filter_delays(Ds,Fds0,Fds,DC,V,Tab)
|
||
|
; DC == disj ->
|
||
|
V = true
|
||
|
)
|
||
|
; Val == fail ->
|
||
|
( DC == conj ->
|
||
|
fail
|
||
|
; DC == disj ->
|
||
|
filter_delays(Ds,Fds0,Fds,DC,V,Tab)
|
||
|
)
|
||
|
; % Val == undefined
|
||
|
filter_delays(Ds,[Lit|Fds0],Fds,DC,V,Tab)
|
||
|
).
|
||
|
|
||
|
lit_to_call(\+G,G).
|
||
|
lit_to_call(Gcall-_,Gcall).
|
||
|
|
||
|
not_subsumed_ans(Ans,Lanss0) :-
|
||
|
\+
|
||
|
( numbervars(Ans,0,_),
|
||
|
subsumed_ans1(Ans,Lanss0)
|
||
|
).
|
||
|
|
||
|
% succeed if answer is subsumed by any in list1 or 2.
|
||
|
subsumed_ans(Tv,List1,List2) :-
|
||
|
\+
|
||
|
(numbervars(Tv,0,_),
|
||
|
\+ subsumed_ans1(Tv,List1),
|
||
|
\+ subsumed_ans1(Tv,List2)
|
||
|
).
|
||
|
|
||
|
% check if a delay is subsumed one of the element in the list
|
||
|
subsumed_ans1(d(T,V),List) :-
|
||
|
member(d(T,V1),List),
|
||
|
( V1 == []
|
||
|
; V = all(LV), V1 = all(LV1) ->
|
||
|
subset(LV,LV1)
|
||
|
; subset(V1,V)
|
||
|
).
|
||
|
|
||
|
/****************** auxiliary routines *******************/
|
||
|
% variantchk/2 finds a variant in a list of atoms.
|
||
|
variantchk(G,[G1|_]) :- variant(G,G1), !.
|
||
|
variantchk(G,[_|L]) :- variantchk(G,L).
|
||
|
|
||
|
variant(A, B) :-
|
||
|
A == B
|
||
|
-> true
|
||
|
; subsumes_chk(A, B),
|
||
|
subsumes_chk(B, A),
|
||
|
A = B.
|
||
|
/*
|
||
|
subsumes_chk(General, Specific) :-
|
||
|
\+ ( numbervars(Specific, 0, _),
|
||
|
\+ General = Specific
|
||
|
).
|
||
|
*/
|
||
|
ground(O,C) :- ground(O) -> C = O ; copy_term(O,C), numbervars(C,0,_).
|
||
|
|
||
|
subset([],_).
|
||
|
subset([E|L1],L2) :- memberchk(E,L2), subset(L1,L2).
|
||
|
|
||
|
reverse([],R,R).
|
||
|
reverse([Goal|Scc],R0,R) :- reverse(Scc,[Goal|R0],R).
|
||
|
|
||
|
/***************** routines for debugging *******************/
|
||
|
|
||
|
% Debugging help: pretty-prints strongly connected components and local table.
|
||
|
display_stack(Stack,Tab) :-
|
||
|
reverse(Stack,[],Rstack),
|
||
|
display_st(Rstack,Tab).
|
||
|
display_st([],_Tab).
|
||
|
display_st([Ggoal|Scc],Tab) :-
|
||
|
find(Tab,Ggoal,Ent),
|
||
|
ent_to_dfn(Ent,Dfn:Pmin-Nmin),
|
||
|
tab(2),
|
||
|
write(Ggoal-Dfn),
|
||
|
write(': '),
|
||
|
write('Pmin='),
|
||
|
write(Pmin),
|
||
|
write('; '),
|
||
|
write('Nmin='),
|
||
|
write(Nmin),
|
||
|
write('; '),
|
||
|
nl,
|
||
|
display_st(Scc,Tab).
|
||
|
|
||
|
display_dlist([]) :- nl,nl.
|
||
|
display_dlist([Ngoal-_|Dlist]) :-
|
||
|
write(\+ Ngoal),
|
||
|
write('; '),
|
||
|
display_dlist(Dlist).
|
||
|
|
||
|
display_table(Tab) :-
|
||
|
write('Table: '),
|
||
|
nl,
|
||
|
write_tab(Tab).
|
||
|
|
||
|
display_final(Tab) :-
|
||
|
write(' Final Set of Answers: '),
|
||
|
nl,
|
||
|
display_final1(Tab).
|
||
|
display_final1([]).
|
||
|
display_final1(l(_,e(_,_,Anss,_,_,_,_))) :-
|
||
|
write_anss(Anss).
|
||
|
display_final1(n2(X,_,Y)) :-
|
||
|
display_final1(X),
|
||
|
display_final1(Y).
|
||
|
display_final1(n3(X,_,Y,_,Z)) :-
|
||
|
display_final1(X),
|
||
|
display_final1(Y),
|
||
|
display_final1(Z).
|
||
|
|
||
|
write_tab([]).
|
||
|
write_tab(l(G,e(Nodes,ANegs,Anss,_,Comp,Dfn:_,_))) :-
|
||
|
write(' Entry: '),
|
||
|
write(G-Dfn),
|
||
|
write(': '),
|
||
|
( Comp == true ->
|
||
|
write('Complete!')
|
||
|
; write('Incomplete!')
|
||
|
),
|
||
|
nl,
|
||
|
( Anss == [] ->
|
||
|
true
|
||
|
; write(' Anss: '),
|
||
|
nl,
|
||
|
write_anss(Anss)
|
||
|
),
|
||
|
( ( Comp == true; Nodes == []) ->
|
||
|
true
|
||
|
; write(' Nodes: '),
|
||
|
write(Nodes),
|
||
|
nl
|
||
|
),
|
||
|
( ( Comp == true; ANegs == []) ->
|
||
|
true
|
||
|
; write(' ANegs: '),
|
||
|
write(ANegs),
|
||
|
nl
|
||
|
).
|
||
|
write_tab(n2(X,_,Y)) :-
|
||
|
write_tab(X),
|
||
|
write_tab(Y).
|
||
|
write_tab(n3(X,_,Y,_,Z)) :-
|
||
|
write_tab(X),
|
||
|
write_tab(Y),
|
||
|
write_tab(Z).
|
||
|
|
||
|
write_anss([]).
|
||
|
write_anss(l(_,Lanss)) :-
|
||
|
write_anss_list(Lanss).
|
||
|
write_anss(n2(T1,_,T2)) :-
|
||
|
write_anss(T1),
|
||
|
write_anss(T2).
|
||
|
write_anss(n3(T1,_,T2,_,T3)) :-
|
||
|
write_anss(T1),
|
||
|
write_anss(T2),
|
||
|
write_anss(T3).
|
||
|
|
||
|
write_anss_list([]).
|
||
|
write_anss_list([Ans|Anss]) :-
|
||
|
write_ans(Ans),
|
||
|
write_anss_list(Anss).
|
||
|
|
||
|
write_ans(d(H,Ds)) :-
|
||
|
write(' '),
|
||
|
write(H),
|
||
|
( Ds == [] ->
|
||
|
true
|
||
|
; write(' :- '),
|
||
|
( Ds = all([D|Ds1]) ->
|
||
|
( D = (_-GH) ->
|
||
|
write(GH)
|
||
|
; write(D)
|
||
|
),
|
||
|
write_delay(Ds1,'; ')
|
||
|
; Ds = [D|Ds1],
|
||
|
( D = (_-GH) ->
|
||
|
write(GH)
|
||
|
; write(D)
|
||
|
),
|
||
|
write_delay(Ds1,', ')
|
||
|
)
|
||
|
),
|
||
|
write('.'),
|
||
|
nl.
|
||
|
write_delay([],_).
|
||
|
write_delay([D|Ds1],Sep) :-
|
||
|
write(Sep),
|
||
|
( D = (_Gcall-GH) ->
|
||
|
write(GH)
|
||
|
; write(D)
|
||
|
),
|
||
|
write_delay(Ds1,Sep).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
/*
|
||
|
This is a set of routines that supports indexed tables. Tables
|
||
|
are sets of key-value_list pairs. With each key is associated a list
|
||
|
of values. It uses 2-3 trees for the index (modified by D.S. Warren
|
||
|
from Ivan Bratko: ``Prolog Programming for Artificial
|
||
|
Intelligence'', Addison Wesley, 1986). Operations are:
|
||
|
|
||
|
Keys must be ground! (so numbervar them)
|
||
|
|
||
|
addkey(Tree,Key,V,Tree1) adds a new Key with value V, returning
|
||
|
new Tree1. Fails if the key is already there.
|
||
|
|
||
|
find(Tree,Key,V) finds the entry with Key and returns associated
|
||
|
values in V.
|
||
|
|
||
|
updatevs(Tree,Key,OldV,NewV,Tree1) replaces value of entry with key
|
||
|
Key and value OldV with NewV.
|
||
|
*/
|
||
|
|
||
|
|
||
|
addkey(Tree,X,V,Tree1) :-
|
||
|
ins2(Tree,X,V,Trees),
|
||
|
cmb0(Trees,Tree1).
|
||
|
addkey([],X,V,l(X,V)).
|
||
|
|
||
|
|
||
|
find(l(X,V),Xs,V) :- X == Xs.
|
||
|
find(n2(T1,M,T2),X,V) :-
|
||
|
M @=< X
|
||
|
-> find(T2,X,V)
|
||
|
; find(T1,X,V).
|
||
|
find(n3(T1,M2,T2,M3,T3),X,V) :-
|
||
|
M2 @=< X
|
||
|
-> (M3 @=< X
|
||
|
-> find(T3,X,V)
|
||
|
; find(T2,X,V)
|
||
|
)
|
||
|
; find(T1,X,V).
|
||
|
|
||
|
|
||
|
% updatevs(Tab0,X,Ov,Nv,Tab) updates Tab0 to Tab, by replacing
|
||
|
% Ov of entry with key X by Nv.
|
||
|
/*
|
||
|
updatevs(Tab0,X,Ov,Nv,Tab) :-
|
||
|
updatevs(Tab0,X,Ov,Nv),
|
||
|
Tab = Tab0.
|
||
|
|
||
|
updatevs(Tab,X,Ov,Nv) :-
|
||
|
( Tab = l(Xs,Ov), Xs == X ->
|
||
|
setarg(2,Tab,Nv)
|
||
|
; Tab = n2(T1,M,T2) ->
|
||
|
( M @=< X ->
|
||
|
updatevs(T2,X,Ov,Nv)
|
||
|
; updatevs(T1,X,Ov,Nv)
|
||
|
)
|
||
|
; Tab = n3(T1,M2,T2,M3,T3) ->
|
||
|
( M2 @=< X ->
|
||
|
( M3 @=< X ->
|
||
|
updatevs(T3,X,Ov,Nv)
|
||
|
; updatevs(T2,X,Ov,Nv)
|
||
|
)
|
||
|
; updatevs(T1,X,Ov,Nv)
|
||
|
)
|
||
|
).
|
||
|
*/
|
||
|
|
||
|
updatevs(l(X,Ov),Xs,Ov,Nv,l(X,Nv)) :- X == Xs.
|
||
|
updatevs(n2(T1,M,T2),X,Ov,Nv,n2(NT1,M,NT2)) :-
|
||
|
M @=< X
|
||
|
-> NT1=T1, updatevs(T2,X,Ov,Nv,NT2)
|
||
|
; NT2=T2, updatevs(T1,X,Ov,Nv,NT1).
|
||
|
updatevs(n3(T1,M2,T2,M3,T3),X,Ov,Nv,n3(NT1,M2,NT2,M3,NT3)) :-
|
||
|
M2 @=< X
|
||
|
-> (M3 @=< X
|
||
|
-> NT2=T2, NT1=T1, updatevs(T3,X,Ov,Nv,NT3)
|
||
|
; NT1=T1, NT3=T3, updatevs(T2,X,Ov,Nv,NT2)
|
||
|
)
|
||
|
; NT2=T2, NT3=T3, updatevs(T1,X,Ov,Nv,NT1).
|
||
|
|
||
|
ins2(n2(T1,M,T2),X,V,Tree) :-
|
||
|
M @=< X
|
||
|
-> ins2(T2,X,V,Tree1),
|
||
|
cmb2(Tree1,T1,M,Tree)
|
||
|
; ins2(T1,X,V,Tree1),
|
||
|
cmb1(Tree1,M,T2,Tree).
|
||
|
ins2(n3(T1,M2,T2,M3,T3),X,V,Tree) :-
|
||
|
M2 @=< X
|
||
|
-> (M3 @=< X
|
||
|
-> ins2(T3,X,V,Tree1),
|
||
|
cmb4(Tree1,T1,M2,T2,M3,Tree)
|
||
|
; ins2(T2,X,V,Tree1),
|
||
|
cmb5(Tree1,T1,M2,M3,T3,Tree)
|
||
|
)
|
||
|
; ins2(T1,X,V,Tree1),
|
||
|
cmb3(Tree1,M2,T2,M3,T3,Tree).
|
||
|
ins2(l(A,V),X,Vn,Tree) :-
|
||
|
A @=< X
|
||
|
-> (X @=< A
|
||
|
-> fail
|
||
|
; Tree = t(l(A,V),X,l(X,Vn))
|
||
|
)
|
||
|
; Tree = t(l(X,Vn),A,l(A,V)).
|
||
|
|
||
|
cmb0(t(Tree),Tree).
|
||
|
cmb0(t(T1,M,T2),n2(T1,M,T2)).
|
||
|
|
||
|
cmb1(t(NT1),M,T2,t(n2(NT1,M,T2))).
|
||
|
cmb1(t(NT1a,Mb,NT1b),M,T2,t(n3(NT1a,Mb,NT1b,M,T2))).
|
||
|
|
||
|
cmb2(t(NT2),T1,M,t(n2(T1,M,NT2))).
|
||
|
cmb2(t(NT2a,Mb,NT2b),T1,M,t(n3(T1,M,NT2a,Mb,NT2b))).
|
||
|
|
||
|
cmb3(t(NT1),M2,T2,M3,T3,t(n3(NT1,M2,T2,M3,T3))).
|
||
|
cmb3(t(NT1a,Mb,NT1b),M2,T2,M3,T3,t(n2(NT1a,Mb,NT1b),M2,n2(T2,M3,T3))).
|
||
|
|
||
|
cmb4(t(NT3),T1,M2,T2,M3,t(n3(T1,M2,T2,M3,NT3))).
|
||
|
cmb4(t(NT3a,Mb,NT3b),T1,M2,T2,M3,t(n2(T1,M2,T2),M3,n2(NT3a,Mb,NT3b))).
|
||
|
|
||
|
cmb5(t(NT2),T1,M2,M3,T3,t(n3(T1,M2,NT2,M3,T3))).
|
||
|
cmb5(t(NT2a,Mb,NT2b),T1,M2,M3,T3,t(n2(T1,M2,NT2a),Mb,n2(NT2b,M3,T3))).
|
||
|
|
||
|
start_slg:- assertz((
|
||
|
term_expansion(X,Y) :- !,
|
||
|
do_term_expansion(X,Y)
|
||
|
)).
|
||
|
|