This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/packages/cplint/lpad.pl

2258 lines
63 KiB
Perl
Raw Normal View History

/*
LPAD and CP-Logic reasoning suite
File lpad.pl
Goal-oriented interpreter for LPADs based on SLG
Copyright (c) 2007, Fabrizio Riguzzi
Based on the SLG System, see below
*/
/***************************************************************************/
/* */
/* 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 1, 1993 by Weidong Chen
===========================================================================
File : lpad.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.
*/
:- module(lpad, [s/2,s/6,
sc/3,sc/7,
p/1,
slg/3,setting/2,set/2
]).
:- dynamic wfs_trace/0.
:-use_module(library(ugraphs)).
:-use_module(library(lists)).
:- use_module(library(charsio)).
:- op(1200,xfx,<--).
:- op(900,xfx,<-).
/* 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) :-
builtin(Call).
/* slg(Call):
It returns all true answers of Call under the well-founded semantics
one by one.
*/
slg(Call,C,D):-
slg(Call,[],C,[],D).
slg(Call,C0,C,D0,D):-
( isprolog(Call) ->
call(Call),
C=C0,
D=D0
; oldt(Call,Tab,C0,C1,D0,D1),
delete(D1,(goal(_),_),D),
ground(Call,Ggoal),
find(Tab,Ggoal,Ent),
ent_to_anss(Ent,Anss),
member_anss(d(Call,Delay),Anss),
(Delay=[]->
C=C1
;
write('Unsound program'),
nl,
C=unsound
)
).
get_new_atom(Atom):-
retract(new_number(N)),
N1 is N+1,
assert(new_number(N1)),
number_atom(N,NA),
atom_concat('$call',NA,Atom).
s(GoalsList,Prob):-
convert_to_goal(GoalsList,Goal),
solve(Goal,Prob).
s(GoalsList,Prob,CPUTime1,0.0,WallTime1,0.0):-
statistics(cputime,[_,_]),
statistics(walltime,[_,_]),
convert_to_goal(GoalsList,Goal),
solve(Goal,Prob),
statistics(cputime,[_,CT1]),
CPUTime1 is CT1/1000,
statistics(walltime,[_,WT1]),
WallTime1 is WT1/1000.
convert_to_goal([Goal],Goal):-Goal \= (\+ _) ,!.
convert_to_goal(GoalsList,Head):-
get_new_atom(Atom),
extract_vars(GoalsList,[],V),
Head=..[Atom|V],
assertz(def_rule(goal(Atom),_,Head,GoalsList)).
solve(Goal,Prob):-
(setof(C,D^slg(Goal,C,D),LDup)->
(member(unsound,LDup)->
format("Unsound program ~n",[]),
Prob=unsound
;
rem_dup_lists(LDup,[],L),
(ground(L)->
build_formula(L,Formula,[],Var),
var2numbers(Var,0,NewVar),
(setting(save_dot,true)->
format("Variables: ~p~n",[Var]),
compute_prob(NewVar,Formula,_Prob,1)
;
compute_prob(NewVar,Formula,Prob,0)
)
;
format("It requires the choice of a head atom from a non ground head~n~p~n",[L]),
Prob=non_ground
)
)
;
Prob=0
).
compute_prob(Var,For,Prob,_):-
compute_prob_term(Var,For,0,Prob).
compute_prob_term(_Var,[],Prob,Prob).
compute_prob_term(Var,[H|T],Prob0,Prob):-
compute_prob_factor(Var,H,1,PF),
Prob1 is Prob0 + PF,
compute_prob_term(Var,T,Prob1,Prob).
compute_prob_factor(_Var,[],PF,PF).
compute_prob_factor(Var,[[N,Value]|T],PF0,PF):-
nth0(N,Var,[_N,_NH,ListProb]),
nth0(Value,ListProb,P),
PF1 is PF0*P,
compute_prob_factor(Var,T,PF1,PF).
sc(Goals,Evidences,Prob):-
convert_to_goal(Goals,Goal),
convert_to_goal(Evidences,Evidence),
solve_cond(Goal,Evidence,Prob).
sc(Goals,Evidences,Prob,CPUTime1,0.0,WallTime1,0.0):-
statistics(cputime,[_,_]),
statistics(walltime,[_,_]),
convert_to_goal(Goals,Goal),
convert_to_goal(Evidences,Evidence),
solve_cond(Goal,Evidence,Prob),
statistics(cputime,[_,CT1]),
CPUTime1 is CT1/1000,
statistics(walltime,[_,WT1]),
WallTime1 is WT1/1000.
solve_cond(Goal,Evidence,Prob):-
(setof(DerivE,D^slg(Evidence,DerivE,D),LDupE)->
rem_dup_lists(LDupE,[],LE),
build_formula(LE,FormulaE,[],VarE),
var2numbers(VarE,0,NewVarE),
compute_prob(NewVarE,FormulaE,ProbE,0),
solve_cond_goals(Goal,LE,ProbGE),
Prob is ProbGE/ProbE
;
format("P(Evidence)=0~n",[]),
Prob=undefined
).
solve_cond_goals(Goals,LE,ProbGE):-
(setof(DerivGE,find_deriv_GE(LE,Goals,DerivGE),LDupGE)->
rem_dup_lists(LDupGE,[],LGE),
build_formula(LGE,FormulaGE,[],VarGE),
var2numbers(VarGE,0,NewVarGE),
call_compute_prob(NewVarGE,FormulaGE,ProbGE)
;
ProbGE=0
).
solve_cond_goals(Goals,LE,0):-
\+ find_deriv_GE(LE,Goals,_DerivGE).
find_deriv_GE(LD,GoalsList,Deriv):-
member(D,LD),
slg(GoalsList,D,DerivDup,[],_Def),
remove_duplicates(DerivDup,Deriv).
call_compute_prob(NewVarGE,FormulaGE,ProbGE):-
(setting(save_dot,true)->
format("Variables: ~p~n",[NewVarGE]),
compute_prob(NewVarGE,FormulaGE,ProbGE,1)
;
compute_prob(NewVarGE,FormulaGE,ProbGE,0)
).
/* 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,[])
).
/* oldt(QueryAtom,Table,C0,C,D0,D): 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,C0,C,D0,D) :-
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,C0,C1,D0,D,PC),
add_PC_to_C(PC,C1,C),
( wfs_trace ->
nl, write('Final '), display_table(Tab), nl
; true
).
/* oldt(Call,Ggoal,Tab0,Tab,Stack0,Stack,DFN0,DFN,Dep0,Dep,TP0,TP,C0,C,D0,D,PC)
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.
C0/C are accumulators for disjunctive clauses used in the derivation of Call:
they are list of triples (N,R,S) where N is the number of the head atom used
(starting from 0), R is the number of the rule used (starting from 1) and
S is the substitution of the variables in the head atom used. S is a list
of elements of the form Varname=Term.
D0/D are accumulators for definite clauses: they are list of couples (R,S),
where R is a rule number and S is a substitution.
PC is a list of disjunctive rules selected but not used in the derivation,
they are added to the C set afterwards if they are consistent with C
(PC stands for Possible C, i.e., possible additions to the C set).
*/
oldt(Call,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D,PC) :-
( number(Call) ->
TP0 = (_ : Tcl),
find(Tcl,Call,Clause),
edge_oldt(Clause,Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1,
C0,C,D0,D)
; find_rules(Call,Frames,C0,PC),
map_oldt(Frames,Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1,
C0,C,D0,D)
),
comp_tab_ent(Ggoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP).
/* find_rules(Call,Frames,C,PossC)
finds rules for Call. Frames is the list of clauses that resolve with Call. It
is a list of terms of the form
rule(d(Call,[]),Body,R,N,S)
C is the current set of disjunctive clauses together with the head selected
PossC is the list of possible disjunctive clauses together with the head
selected: they are the clauses with an head that does not unify with Call. It
is a list of terms of the form
rule(d(Call,[]),Body,R,N,S)
*/
find_rules(Call,Frames,C,PossC):-
findall(rule(d(Call,[]),Body,def(N),_,Subs,_),def_rule(N,Subs,Call,Body),Fr1),
find_disj_rules(Call,Fr2,C,PossC),
append(Fr1,Fr2,Frames).
/* find_disj_rules(Call,Fr,C,PossC):-
finds disjunctive rules for Call.
*/
find_disj_rules(Call,Fr,C,[]):-
findall(rule(d(Call,[]),Body,R,N,S,LH),
find_rule(Call,(R,S,N),Body,LH),Fr).
find_disj_rulesold(Call,Fr,C,PossC):-
findall(rule(d(Call,[]),Body,R,S,N,LH),
find_rule(Call,(R,S,N),Body,LH),LD),
(setof((R,LH),(Call,Body,S,N)^member(rule(d(Call,[]),Body,R,S,N,LH),LD),LR)->
choose_rules(LR,LD,[],Fr,C,[],PossC)
;
Fr=[],
PossC=[]
).
/* choose_rules(LR,LD,Fr0,Fr,C,PossC0,PossC)
LR is a list of couples (R,LH) where R is a disjunctive rule number and LH is
a list of head atoms numbers, from 0 to length(head)-1
LD is the list of disjunctive clauses resolving with Call. Its elements are
of the form
rule(d(Call,[]),Body,R,N,S)
Fr0/Fr are accumulators for the matching disjunctive clauses
PossC0/PossC are accumulators for the additional disjunctive clauses
*/
choose_rules([],Fr,Fr,_C,PC,PC).
choose_rules([rule(d(Call,[]),Body,R,S,N1,LH)|LD],Fr0,Fr,C,PC0,PC):-
member(N,LH),
(N=N1->
% the selected head resolves with Call
consistent(N,R,S,C),
Fr=[rule(d(Call,[]),Body,R,N,S)|Fr1],
PC=PC1
;
% the selected head does not resolve with Call
consistent(N,R,S,C),
Fr=[rule(d('$null',[]),Body,R,N,S)|Fr1],
PC=PC1
),
choose_rules(LD,Fr0,Fr1,C,PC0,PC1).
choose_rulesold([],_LD,Fr,Fr,_C,PC,PC).
choose_rulesold([(R,LH)|LR],LD,Fr0,Fr,C,PC0,PC):-
member(N,LH),
(member(rule(d(Call,[]),Body,R,S,N,LH),LD)->
% the selected head resolves with Call
consistent(N,R,S,C),
Fr=[rule(d(Call,[]),Body,R,N,S)|Fr1],
PC=PC1
;
% the selected head does not resolve with Call
findall(S,member(rule(d(Call,[]),Body,R,S,_N,LH),LD),LS),
% this is done to handle the case in which there are
% multiple instances of rule R with different substitutions
(merge_subs(LS,S)->
% all the substitutions are consistent, their merge is used
consistent(N,R,S,C),
Fr=Fr1,
PC=[rule(d(_Call,[]),Body,R,N,S)|PC1]
;
% the substitutions are inconsistent, the empty substitution is used
rule(R,S,_LH,_Head,_Body),
consistent(N,R,S,C),
Fr=Fr1,
PC=[rule(d(_Call,[]),Body,R,N,S)|PC1]
)
),
choose_rules(LR,LD,Fr0,Fr1,C,PC0,PC1).
merge_subs([],_S).
merge_subs([S|ST],S):-
merge_subs(ST,S).
merge_subs([],_Call,_S).
merge_subs([(S,Call)|ST],Call,S):-
merge_subs(ST,Call,S).
/* consistent(N,R,S,C)
head N of rule R with substitution S is consistent with C
*/
consistent(_N,_R,_S,[]):-!.
consistent(N,R,S,[(_N,R1,_S)|T]):-
% different rule
R\=R1,!,
consistent(N,R,S,T).
consistent(N,R,S,[(N,R,_S)|T]):-
% same rule, same head
consistent(N,R,S,T).
consistent(N,R,S,[(N1,R,S1)|T]):-
% same rule, different head
N\=N1,
% different substitutions
dif(S,S1),
consistent(N,R,S,T).
map_oldt([],_Ggoal,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP,C,C,D,D).
map_oldt([Clause|Frames],Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,
C0,C,D0,D) :-
edge_oldt(Clause,Ggoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1,
C0,C1,D0,D1),
map_oldt(Frames,Ggoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP,C1,C,D1,D).
/* 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,C0,C,D0,D) :-
Clause = rule(Ans,B,Rule,Number,Sub,LH),
( B == [] ->
ans_edge(rule(Ans,B,Rule,Number,Sub,LH),Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D)
; B = [Lit|_] ->
( Lit = (\+N) ->
neg_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D)
; pos_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D)
)
; 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)
)
)
).
/* add_ans_to_C(rule(Head,Body,R,N,S),C0,C,D0,D):-
adds rule rule(Head,Body,R,N,S) to the C set if it is disjunctive
or to the D set if it is definite. The rule is added only if it is consistent
with the current C set
*/
add_ans_to_C(rule(_,_,def(N),_,S,_),C,C,D,[(N,S)|D],true):-!.
add_ans_to_C(rule(_Ans,_B,R,N,S,LH),C0,C,D,D,HeadSelected):-
member(N1,LH),
(N1=N->
HeadSelected=true
;
HeadSelected=false
),
\+ already_present_with_a_different_head(N1,R,S,C0),
(already_present_with_the_same_head(N1,R,S,C0)->
C=C0
;
C=[(N1,R,S)|C0]
).
/* already_present_with_the_same_head(N,R,S,C)
succeeds if rule R is present in C with head N and substitution S
*/
already_present_with_the_same_head(N,R,S,[(N,R,S)|_T]):-!.
already_present_with_the_same_head(N,R,S,[(_N,_R,_S)|T]):-!,
already_present_with_the_same_head(N,R,S,T).
/* already_present_with_a_different_head(N,R,S,C)
succeeds if rule R is present in C with susbtitution S and a head different
from N
*/
already_present_with_a_different_head(N,R,S,[(N1,R,S1)|_T]):-
different_head(N,N1,S,S1),!.
already_present_with_a_different_head(N,R,S,[(_N1,_R1,_S1)|T]):-
already_present_with_a_different_head(N,R,S,T).
different_head(N,N1,S,S1):-
N\=N1,S=S1, !.
/* add_PC_to_C(PossC,C0,C)
adds the rules in PossC to C if they are consistent with it, otherwise it
fails
*/
add_PC_to_C([],C,C).
add_PC_to_C([rule(H,B,R,N,S)|T],C0,C):-
add_ans_to_C(rule(H,B,R,N,S),C0,C1,[],[]),
add_PC_to_C(T,C1,C).
ans_edge(rule(Ans,B,Rule,Number,Sub,LH),Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
add_ans_to_C(rule(Ans,B,Rule,Number,Sub,LH),C0,C1,D0,D1,HeadSelected),
(HeadSelected=false->
Tab = Tab0, S = S0, Dfn = Dfn0, Dep = Dep0, TP = TP0, C=C1, D=D1
;
(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,C1,C,D1,D)
;
Mode = no_new_head ->
Tab = Tab1, S = S0, Dfn = Dfn0, Dep = Dep0,
TP = TP0, C=C1, D=D1
)
;
Tab = Tab0, S = S0, Dfn = Dfn0, Dep = Dep0, TP = TP0, C=C1, D=D1
)
).
neg_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
Clause = rule(_,[\+N|_],_R,_N,_Sub,_LH),
( 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, C=C0, D=D0, TP = TP0
; apply_subst(Node,d(\+ N,[]),Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C1,D0,D)
)
; ( find(Tab0,Ngoal,Nent) ->
Tab2 = Tab0, S2 = S0, Dfn2 = Dfn0, Dep2 = Dep0, TP2 = TP0, C2=C0, D2=D0
; 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,TP2,C0,C1,D0,D2,PC),
add_PC_to_C(PC,C1,C2),
compute_mins(Dep0,Ndep,pos,Dep2),
find(Tab2,Ngoal,Nent)
),
ent_to_comp(Nent,Ncomp),
ent_to_anss(Nent,Nanss),
( succeeded(Nanss) ->
Tab = Tab2, S = S2, Dfn = Dfn2, Dep = Dep2, TP = TP2, C =C2, D=D2
; failed(Nanss), Ncomp == true ->
apply_subst(Node,d(\+N,[]),Tab2,Tab,S2,S,Dfn2,Dfn,Dep2,Dep,TP2,TP,C2,C,D2,D)
; apply_subst(Node,d(\+N,[\+N]),Tab2,Tab,S2,S,Dfn2,Dfn,Dep2,Dep,TP2,TP,C2,C,D2,D)
)
).
pos_edge(Clause,Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
Clause = rule(_H,[N|_B],_R,_N,_Sub,_LH),
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,C0,C,D0,D)
; ( 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,C0,C,D0,D)
; % N is completed.
map_anss(Nanss,Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D)
)
; % 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,C0,C1,D0,D,PC),
add_PC_to_C(PC,C1,C),
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,C0,C,D0,D) :-
copy_term(Cl,rule(d(Ac,Vc),Body,R,N,Sub,LH)),
( 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,R,N,Sub,LH),Ggoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D).
/* 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,C,C,D,D).
map_nodes([Node|Nodes],Ans,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
apply_subst(Node,Ans,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1,C0,C1,D0,D1),
map_nodes(Nodes,Ans,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP,C1,C,D1,D).
map_anss([],_Node,_Ngoal,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP,C,C,D,D).
map_anss(l(_GH,Lanss),Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
( Lanss == [] ->
Tab = Tab0, S = S0, Dfn = Dfn0, Dep = Dep0, TP = TP0, C=C0, D=D0
; Lanss = [Ans|_],
returned_ans(Ans,Ngoal,RAns),
apply_subst(Node,RAns,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D)
).
map_anss(n2(T1,_,T2),Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
map_anss(T1,Node,Ngoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1,C0,C1,D0,D1),
map_anss(T2,Node,Ngoal,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP,C1,C,D1,D).
map_anss(n3(T1,_,T2,_,T3),Node,Ngoal,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
map_anss(T1,Node,Ngoal,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1,C0,C1,D0,D1),
map_anss(T2,Node,Ngoal,Tab1,Tab2,S1,S2,Dfn1,Dfn2,Dep1,Dep2,TP1,TP2,C1,C2,D1,D2),
map_anss(T3,Node,Ngoal,Tab2,Tab,S2,S,Dfn2,Dfn,Dep2,Dep,TP2,TP,C2,C,D2,D).
map_anss_list([],_Node,Tab,Tab,S,S,Dfn,Dfn,Dep,Dep,TP,TP,C,C,D,D).
map_anss_list([Ans|Lanss],Node,Tab0,Tab,S0,S,Dfn0,Dfn,Dep0,Dep,TP0,TP,C0,C,D0,D) :-
apply_subst(Node,Ans,Tab0,Tab1,S0,S1,Dfn0,Dfn1,Dep0,Dep1,TP0,TP1,C0,C1,D0,D1),
map_anss_list(Lanss,Node,Tab1,Tab,S1,S,Dfn1,Dfn,Dep1,Dep,TP1,TP,C1,C,D1,D).
/* 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([],X,V,l(X,V)):-!.
addkey(Tree,X,V,Tree1) :-
ins2(Tree,X,V,Trees),
cmb0(Trees,Tree1).
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))).
:-dynamic rule/5,def_rule/4,setting/2.
/* start of list of parameters that can be set by the user with
set(Parameter,Value) */
setting(epsilon_parsing,0.00001).
setting(save_dot,false).
setting(ground_body,false).
/* find_rule(G,(R,S,N),Body,C) takes a goal G and the current C set and
returns the index R of a disjunctive rule resolving with G together with
the index N of the resolving head, the substitution S and the Body of the
rule */
find_rule(H,(R,S,N),Body,LH):-
rule(R,S,_,Head,Body),
member_head(H,Head,0,N),
length(Head,NH),
listN(0,NH,LH).
find_rule(H,(R,S,Number),Body,C):-
rule(R,S,_,uniform(H:1/_Num,_P,Number),Body),
not_already_present_with_a_different_head(Number,R,S,C).
not_already_present_with_a_different_head(_N,_R,_S,[]).
not_already_present_with_a_different_head(N,R,S,[(N1,R,S1)|T]):-
not_different(N,N1,S,S1),!,
not_already_present_with_a_different_head(N,R,S,T).
not_already_present_with_a_different_head(N,R,S,[(_N1,R1,_S1)|T]):-
R\==R1,
not_already_present_with_a_different_head(N,R,S,T).
not_different(N,N,S,S).
not_different(_N,_N1,S,S1):-
S\=S1,!.
not_different(N,N1,S,S1):-
N\=N1,!,
dif(S,S1).
not_different(N,N,S,S).
member_head(H,[(H:_P)|_T],N,N).
member_head(H,[(_H:_P)|T],NIn,NOut):-
N1 is NIn+1,
member_head(H,T,N1,NOut).
/* rem_dup_lists removes the C sets that are a superset of
another C sets further on in the list of C sets */
rem_dup_lists([],L,L).
rem_dup_lists([H|T],L0,L):-
(member_subset(H,T);member_subset(H,L0)),!,
rem_dup_lists(T,L0,L).
rem_dup_lists([H|T],L0,L):-
rem_dup_lists(T,[H|L0],L).
member_subset(E,[H|_T]):-
subset_my(H,E),!.
member_subset(E,[_H|T]):-
member_subset(E,T).
rem_dup_lists_tab([],L,L).
rem_dup_lists_tab([(H,_Tab)|T],L0,L):-
(member_subset_tab(H,T);member_subset_tab(H,L0)),!,
rem_dup_lists_tab(T,L0,L).
rem_dup_lists_tab([(H,Tab)|T],L0,L):-
rem_dup_lists_tab(T,[(H,Tab)|L0],L).
member_subset_tab(E,[(H,_Tab)|_T]):-
subset_my(H,E),!.
member_subset_tab(E,[_H|T]):-
member_subset_tab(E,T).
/* predicates for building the formula to be converted into a BDD */
/* build_formula(LC,Formula,VarIn,VarOut) takes as input a set of C sets
LC and a list of Variables VarIn and returns the formula and a new list
of variables VarOut
Formula is of the form [Term1,...,Termn]
Termi is of the form [Factor1,...,Factorm]
Factorj is of the form (Var,Value) where Var is the index of
the multivalued variable Var and Value is the index of the value
*/
build_formula([],[],Var,Var).
build_formula([D|TD],[F|TF],VarIn,VarOut):-
build_term(D,F,VarIn,Var1),
build_formula(TD,TF,Var1,VarOut).
build_term([],[],Var,Var).
build_term([(N,R,S)|TC],[[NVar,N]|TF],VarIn,VarOut):-
(nth0_eq(0,NVar,VarIn,(R,S))->
Var1=VarIn
;
append(VarIn,[(R,S)],Var1),
length(VarIn,NVar)
),
build_term(TC,TF,Var1,VarOut).
/* nth0_eq(PosIn,PosOut,List,El) takes as input a List,
an element El and an initial position PosIn and returns in PosOut
the position in the List that contains an element exactly equal to El
*/
nth0_eq(N,N,[H|_T],El):-
H==El,!.
nth0_eq(NIn,NOut,[_H|T],El):-
N1 is NIn+1,
nth0_eq(N1,NOut,T,El).
/* var2numbers converts a list of couples (Rule,Substitution) into a list
of triples (N,NumberOfHeadsAtoms,ListOfProbabilities), where N is an integer
starting from 0 */
var2numbers([],_N,[]).
var2numbers([(R,S)|T],N,[[N,ValNumber,Probs]|TNV]):-
find_probs(R,S,Probs),
length(Probs,ValNumber),
N1 is N+1,
var2numbers(T,N1,TNV).
find_probs(R,S,Probs):-
rule(R,S,_N,Head,_Body),
get_probs(Head,Probs).
get_probs(uniform(_A:1/Num,_P,_Number),ListP):-
Prob is 1/Num,
list_el(Num,Prob,ListP).
get_probs([],[]).
get_probs([_H:P|T],[P1|T1]):-
P1 is P,
get_probs(T,T1).
list_el(0,_P,[]):-!.
list_el(N,P,[P|T]):-
N1 is N-1,
list_el(N1,P,T).
/* end of predicates for building the formula to be converted into a BDD */list_el(0,_P,[]):-!.
/* p(File) parses the file File.cpl. It can be called more than once without
exiting yap */
p(File):-
parse(File).
parse(File):-
atom_concat(File,'.cpl',FilePl),
open(FilePl,read,S),
read_clauses(S,C),
close(S),
retractall(rule(_,_,_,_,_)),
retractall(def_rule(_,_,_,_)),
retractall(new_number(_)),
assert(new_number(0)),
process_clauses(C,1),!.
process_clauses([(end_of_file,[])],_N).
process_clauses([((H:-B),V)|T],N):-
H=uniform(A,P,L),!,
list2and(BL,B),
process_body(BL,V,V1),
remove_vars([P],V1,V2),
append(BL,[length(L,Tot),nth0(Number,L,P)],BL1),
append(V2,['Tot'=Tot],V3),
assertz(rule(N,V3,_NH,uniform(A:1/Tot,L,Number),BL1)),
N1 is N+1,
process_clauses(T,N1).
process_clauses([((H:-B),V)|T],N):-
H=(_;_),!,
list2or(HL1,H),
process_head(HL1,HL),
list2and(BL,B),
process_body(BL,V,V1),
length(HL,LH),
listN(0,LH,NH),
assertz(rule(N,V1,NH,HL,BL)),
N1 is N+1,
process_clauses(T,N1).
process_clauses([((H:-B),V)|T],N):-
H=(_:_),!,
list2or(HL1,H),
process_head(HL1,HL),
list2and(BL,B),
process_body(BL,V,V1),
length(HL,LH),
listN(0,LH,NH),
assertz(rule(N,V1,NH,HL,BL)),
N1 is N+1,
process_clauses(T,N1).
process_clauses([((H:-B),V)|T],N):-!,
list2and(BL,B),
assert(def_rule(N,V,H,BL)),
N1 is N+1,
process_clauses(T,N1).
process_clauses([(H,V)|T],N):-
H=(_;_),!,
list2or(HL1,H),
process_head(HL1,HL),
length(HL,LH),
listN(0,LH,NH),
assertz(rule(N,V,NH,HL,[])),
N1 is N+1,
process_clauses(T,N1).
process_clauses([(H,V)|T],N):-
H=(_:_),!,
list2or(HL1,H),
process_head(HL1,HL),
length(HL,LH),
listN(0,LH,NH),
assertz(rule(N,V,NH,HL,[])),
N1 is N+1,
process_clauses(T,N1).
process_clauses([(H,V)|T],N):-
assert(def_rule(N,V,H,[])),
N1 is N+1,
process_clauses(T,N1).
/* if the annotation in the head are not ground, the null atom is not added
and the eventual formulas are not evaluated */
process_head(HL,NHL):-
(ground_prob(HL)->
process_head_ground(HL,0,NHL)
;
NHL=HL
).
ground_prob([]).
ground_prob([_H:PH|T]):-
ground(PH),
ground_prob(T).
process_head_ground([H:PH],P,[H:PH1|Null]):-
PH1 is PH,
PNull is 1-P-PH1,
setting(epsilon_parsing,Eps),
EpsNeg is - Eps,
PNull > EpsNeg,
(PNull>Eps->
Null=['':PNull]
;
Null=[]
).
process_head_ground([H:PH|T],P,[H:PH1|NT]):-
PH1 is PH,
P1 is P+PH1,
process_head_ground(T,P1,NT).
/* setof must have a goal of the form B^G where B is a term containing the existential variables */
process_body([],V,V).
process_body([setof(A,B^_G,_L)|T],VIn,VOut):-!,
get_var(A,VA),
get_var(B,VB),
remove_vars(VA,VIn,V1),
remove_vars(VB,V1,V2),
process_body(T,V2,VOut).
process_body([setof(A,_G,_L)|T],VIn,VOut):-!,
get_var(A,VA),
remove_vars(VA,VIn,V1),
process_body(T,V1,VOut).
process_body([bagof(A,B^_G,_L)|T],VIn,VOut):-!,
get_var(A,VA),
get_var(B,VB),
remove_vars(VA,VIn,V1),
remove_vars(VB,V1,V2),
process_body(T,V2,VOut).
process_body([bagof(A,_G,_L)|T],VIn,VOut):-!,
get_var(A,VA),
remove_vars(VA,VIn,V1),
process_body(T,V1,VOut).
process_body([_H|T],VIn,VOut):-!,
process_body(T,VIn,VOut).
get_var_list([],[]).
get_var_list([H|T],[H|T1]):-
var(H),!,
get_var_list(T,T1).
get_var_list([H|T],VarOut):-!,
get_var(H,Var),
append(Var,T1,VarOut),
get_var_list(T,T1).
get_var(A,[A]):-
var(A),!.
get_var(A,V):-
A=..[_F|Args],
get_var_list(Args,V).
remove_vars([],V,V).
remove_vars([H|T],VIn,VOut):-
delete_var(H,VIn,V1),
remove_vars(T,V1,VOut).
delete_var(_H,[],[]).
delete_var(V,[VN=Var|T],[VN=Var|T1]):-
V\==Var,!,
delete_var(V,T,T1).
delete_var(_V,[_H|T],T).
read_clauses(S,Clauses):-
(setting(ground_body,true)->
read_clauses_ground_body(S,Clauses)
;
read_clauses_exist_body(S,Clauses)
).
read_clauses_ground_body(S,[(Cl,V)|Out]):-
read_term(S,Cl,[variable_names(V)]),
(Cl=end_of_file->
Out=[]
;
read_clauses_ground_body(S,Out)
).
read_clauses_exist_body(S,[(Cl,V)|Out]):-
read_term(S,Cl,[variable_names(VN)]),
extract_vars_cl(Cl,VN,V),
(Cl=end_of_file->
Out=[]
;
read_clauses_exist_body(S,Out)
).
extract_vars_cl(end_of_file,[]).
extract_vars_cl(Cl,VN,Couples):-
(Cl=(H:-_B)->
true
;
H=Cl
),
extract_vars(H,[],V),
pair(VN,V,Couples).
pair(_VN,[],[]).
pair([VN= _V|TVN],[V|TV],[VN=V|T]):-
pair(TVN,TV,T).
extract_vars(Var,V0,V):-
var(Var),!,
(member_eq(Var,V0)->
V=V0
;
append(V0,[Var],V)
).
extract_vars(Term,V0,V):-
Term=..[_F|Args],
extract_vars_list(Args,V0,V).
extract_vars_list([],V,V).
extract_vars_list([Term|T],V0,V):-
extract_vars(Term,V0,V1),
extract_vars_list(T,V1,V).
listN(N,N,[]):-!.
listN(NIn,N,[NIn|T]):-
N1 is NIn+1,
listN(N1,N,T).
/* end of predicates for parsing an input file containing a program */
/* start of utility predicates */
list2or([X],X):-
X\=;(_,_),!.
list2or([H|T],(H ; Ta)):-!,
list2or(T,Ta).
list2and([X],X):-
X\=(_,_),!.
list2and([H|T],(H,Ta)):-!,
list2and(T,Ta).
member_eq(A,[H|_T]):-
A==H.
member_eq(A,[_H|T]):-
member_eq(A,T).
subset_my([],_).
subset_my([H|T],L):-
member_eq(H,L),
subset_my(T,L).
remove_duplicates_eq([],[]).
remove_duplicates_eq([H|T],T1):-
member_eq(H,T),!,
remove_duplicates_eq(T,T1).
remove_duplicates_eq([H|T],[H|T1]):-
remove_duplicates_eq(T,T1).
builtin(_A is _B).
builtin(_A > _B).
builtin(_A < _B).
builtin(_A >= _B).
builtin(_A =< _B).
builtin(_A =:= _B).
builtin(_A =\= _B).
builtin(true).
builtin(false).
builtin(_A = _B).
builtin(_A==_B).
builtin(_A\=_B).
builtin(_A\==_B).
builtin(length(_L,_N)).
builtin(member(_El,_L)).
builtin(average(_L,_Av)).
builtin(max_list(_L,_Max)).
builtin(min_list(_L,_Max)).
builtin(nth0(_,_,_)).
builtin(nth(_,_,_)).
average(L,Av):-
sum_list(L,Sum),
length(L,N),
Av is Sum/N.
clique([],[]):-!.
clique(Graph,Clique):-
vertices(Graph,Candidates),
extend_cycle(Graph,Candidates,[],[],Clique).
extend_cycle(G,[H|T],Not,CS,CSOut):-
neighbours(H, G, Neigh),
intersection(Neigh,T,NewCand),
intersection(Neigh,Not,NewNot),
extend(G,NewCand,NewNot,[H|CS],CSOut).
extend_cycle(G,[H|T],Not,CS,CSOut):-
extend_cycle(G,T,[H|Not],CS,CSOut).
extend(_G,[],[],CompSub,CompSub):-!.
extend(G,Cand,Not,CS,CSOut):-
extend_cycle(G,Cand,Not,CS,CSOut).
intersection([],_Y,[]).
intersection([H|T],Y,[H|Z]):-
member(H,Y),!,
intersection(T,Y,Z).
intersection([_H|T],Y,Z):-
intersection(T,Y,Z).
/* set(Par,Value) can be used to set the value of a parameter */
set(Parameter,Value):-
retract(setting(Parameter,_)),
assert(setting(Parameter,Value)).
/* end of utility predicates */