%%% -*- Mode: Prolog; -*- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % $Date: 2011-11-28 14:41:26 +0100 (Mon, 28 Nov 2011) $ % $Revision: 6764 $ % % Main author of this file: % Bernd Gutmann % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% :- module(grounder, [grounder_reset/0, grounder_compute_reachable_atoms/3, grounder_reachable_atom/1, grounder_ground_term_with_reachable_atoms/2, grounder_completion_for_atom/3 ]). :- style_check(all). :- yap_flag(unknown,error). :- use_module('../problog',[probabilistic_fact/3]). :- use_module(termhandling). %======================================================================== %= %======================================================================== :- multifile user:myclause/3. user:myclause(_InterpretationID,Head,Body) :- current_predicate(user:myclause/2), user:myclause(Head,Body). %======================================================================== %= reset the internal state, that is, forget all reachable atoms %======================================================================== grounder_reset :- eraseall(reachable). %======================================================================== %= grounder_reachable_atom(-Atom) %======================================================================== grounder_reachable_atom(Atom) :- recorded(reachable,Atom,_Key). %======================================================================== %= grounder_compute_reachable_atoms(+A,+ID,-Success) %= A is a ground atom %= ID is an interpretation ID %= Success is "true" if there is a proof for A, otherwise "false" %= %= The predicate always succeeds exactly once %= %= This is basically a vanilla meta-interpreter, that follows all %= paths in the SLD tree and records which atoms can be reached %= while proving A. %= the only "speciality" is that the negation of a probilistic %= fact always succeeds %= %= the reachable atoms are stored in the internal database %= under the key "reachable" %======================================================================== grounder_compute_reachable_atoms(A,ID,Success) :- bb_put(dep_proven,false), %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ( % go over all proofs for A in interpretation ID tabled_meta_interpreter(A,ID), bb_put(dep_proven,true), fail; % go to next proof true ), %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% bb_delete(dep_proven,Success). %======================================================================== %= tabled_meta_interpreter(+E, +ID) %= E is a valid Prolog expression %= ID is an interpretation ID %= %= the predicate succeeds if there is a proof for E %= upon backtracking all possible proofs are generated %= the atoms visited while proving E are added to the internal database %= using the key "reachable" %= %= if a ground atom is revisited, it is not proven again %======================================================================== tabled_meta_interpreter((X,Y),ID) :- !, tabled_meta_interpreter(X,ID), tabled_meta_interpreter(Y,ID). tabled_meta_interpreter((X;Y),ID) :- !, ( tabled_meta_interpreter(X,ID); tabled_meta_interpreter(Y,ID) ). tabled_meta_interpreter(\+ X,ID) :- !, ( probabilistic_fact(_, X, _) -> tabled_meta_interpreter(X,ID) % prob. facts can be true/false ; \+ tabled_meta_interpreter(X,ID) ). tabled_meta_interpreter(X,_) :- predicate_property(X,built_in), !, call(X). tabled_meta_interpreter( Atom,ID ) :- ground(Atom), !, ( recorded(reachable,Atom,_) % did we see this atom before? -> true % nothing to do ; % nope, we have to continue proving recorda(reachable,Atom,_), tabled_meta_interpreter_aux_ground_atom(Atom,ID) ). tabled_meta_interpreter(Atom,ID) :- % at this point we know, Atom is non-ground % hence we need to be carefull not to ignore any path in the SLD tree % % we can ignore probabilistic facts and only look for myclauses % since in ProbLog the requirement is that non-ground facts have to be % ground at query time current_predicate(user:myclause/3), user:myclause(ID,Atom,Body), tabled_meta_interpreter(Body,ID), % check whether Atom got grounded now, % if not, complain and give up ( ground(Atom) -> recorda(reachable,Atom,_) ; format(user_error,'Error at running the meta interpreter.~n',[]), format(user_error,'The clauses defined by myclause/2 have to be written in a way such that~n',[]), format(user_error,'each atom in the body of a clause gets fully grounded when it is called.~n',[]), format(user_error,' This is not the case for the atom ~w~3n',[Atom]), throw(meta_interpreter_error(Atom)) ). % note, that on backtracking all alternative proofs will % be followed as well %======================================================================== %= tabled_meta_interpreter_aux_ground_atom(+E, +ID) %= E is a valid Prolog expression %= ID is an interpretation ID %= %= the predicate succeeds if there is a proof for E %= upon backtracking all possible proofs are generated %= the atoms visited while proving E are added to the internal database %= using the key "reachable" %= %= if a ground atom is revisited, it is not proven again %= %= DON'T call this predicate directly, it is a helper predicate for %= tabled_meta_interpreter/2 %======================================================================== tabled_meta_interpreter_aux_ground_atom(Atom,_ID) :- probabilistic_fact(_, Atom, _), !. % probabilistic facts and background knowledge must not have % an atom in common. hence we can savely put that cut above. tabled_meta_interpreter_aux_ground_atom(Atom,ID) :- current_predicate(user:myclause/3), user:myclause(ID,Atom,Body), % find a suitable clause and continue proving % on backtracking we will try all suitable clauses tabled_meta_interpreter(Body,ID). %======================================================================== %= grounder_ground_term_with_reachable_atoms(+T1,-T2) %= T1 is a (possible non-ground) term %= T2 is ground term %= %= generates on backtracking all possible ground instances of T1 %= where atoms are grounded with reachable atoms that have %= been found before by grounder_compute_reachable_atoms/3 %======================================================================== grounder_ground_term_with_reachable_atoms( (X,Y), (X2,Y2)) :- !, grounder_ground_term_with_reachable_atoms(X,X2), grounder_ground_term_with_reachable_atoms(Y,Y2). grounder_ground_term_with_reachable_atoms( (X;Y), (X2;Y2)) :- !, grounder_ground_term_with_reachable_atoms(X,X2), grounder_ground_term_with_reachable_atoms(Y,Y2). grounder_ground_term_with_reachable_atoms( \+X, \+X2) :- !, grounder_ground_term_with_reachable_atoms(X,X2). grounder_ground_term_with_reachable_atoms( false, false) :- !. grounder_ground_term_with_reachable_atoms(X, true) :- predicate_property(X,built_in), !, call(X). grounder_ground_term_with_reachable_atoms(X,'$atom'(X)) :- !, recorded(reachable,X,_). %======================================================================== %= grounder_completion_for_atom(+A,+ID,-X) %= A is %= X is %= ID is %= %= %= %= %======================================================================== grounder_completion_for_atom(Head,InterpretationID,'$atom'(Head)<=>Disjunction) :- % find all clauses findall(Body2,( user:myclause(InterpretationID,Head,Body), grounder_ground_term_with_reachable_atoms(Body,Body2) ),Bodies), Bodies\==[], list_to_disjunction(Bodies,Disjunction).