[comp.lang.prolog] PROLOG Digest V5 #31

PROLOG-REQUEST@SUSHI.STANFORD.EDU (Chuck Restivo, The Moderator) (04/25/87)

PROLOG Digest            Monday, 27 Apr 1987       Volume 5 : Issue 31
Today's Topics:
               Implementation - Cut & Meta-interpreter,
                     Programming - Shortest Path
----------------------------------------------------------------------

Date: Mon, 20 Apr 87 09:52:05 PST
From: Fernando Pereira <pereira@stinson.ai.sri.com>
Subject: Cut in metainterpreters

The best published source of a meta-circular interpreter for cut is
Richard O'Keefe's paper ``On the treatment of Cuts in Prolog
Source-Level Cuts'', 1985 IEEE Symposium on Logic Programming, pp.
68-72. For those who don't have access to this paper, here is the
interpreter

do_goal(Goal) :-
        system_predicate(Goal), !,
        call(Goal).
do_goal(Goal) :-
        clause(Goal, Body),
        do_body(Body, AfterCut, HadCut),
        (  HadCut = yes,
                !,              % cuts the whole do_goal
                do_body(AfterCut)
        ;  HadCut = no
        ).

do_body(Body) :-
        do_body(Body, AfterCut, HadCut),
        (  HadCut = yes,
                !,              % cuts the whole do_body
                do_body(AfterCut)
        ;  HadCut = no
        ).

do_body((!,AfterCut), AfterCut, yes) :- !.
do_body((Goal,Body), AfterCut, HadCut) :- !,
        do_goal(Goal),
        do_body(Body, AfterCut, HadCut).
do_body(!, true, yes).
do_body((Disj1;Disj2), AfterCut, HadCut) :-
        do_body(Disj1, AfterCut, HadCut).
do_body((Disj1;Disj2), AfterCut, HadCut) :- !,
        do_body(Disj2, AfterCut, HadCut).
do_body(Goal, true, no) :-
        do_goal(Goal).

Incidentally, the first interpreter of this kind I saw was the first
version of the interpreter in DEC-10 Prolog, written by David H. D. Warren
(more recent versions use something like ancestral cut).

-- Fernando Pereira

------------------------------

Date: 20 Apr 87 21:17:54 GMT
From: MorrisseyKA <ihnp4!drutx!druhi!kam@ucbvax.Berkeley.EDU>
Subject: cut for metainterpreter

One solution is:

        execute(true) :- !.
        execute((P,Q)) :- !, execute(P), execute(Q).
        execute(P) :- clause((P:-Q)), execute(Q).
        execute(P) :- P.

It is attributed to Pereira (don't know which one) and can be found on
page 157 of:

        HOW TO SOLVE IT WITH PROLOG
        4th edition
        by H. Coelho, J. Cotta, and L. Pereira
        Lisbon, 1985

-- Karen A. Morrissey

------------------------------

Date: 23 Apr 87 00:01:21 GMT
From: Rafail Ostrovsky <raf@bu-cs.bu.edu>  
Subject: cut for metainterpreter

In response to my question for meta-circular interpreter which
includes cut (without the use of ancesstor cut in the implementation!)
Karen A. Morrissey proposes the "solution" which is just a regular
meta-interpeter, but DOES NOT incorporate cut!

Here is a possible solution which does incorporate cut and is based on
the assumption (which is true for C-Prolog) that cut cuts the goal
choice-point rather then the disjunctive choice-point:

%-------------------------------------------------------
%  /* meta-circular interpreter with cut  */
%-------------------------------------------------------
%  Author: Rafail Ostrovsky                4/20/1987
%-------------------------------------------------------
%
solve(G) :- solve(G,_).
solve(!,X) :- var(X);X=cut.
solve((A,B),C) :- solve(A,C),solve(B,C).
solve(A,C)
    :-  C==cut;
        (system_pred(A),!,A);
        (clause(A,B),solve(B,C1),(var(C1);C1==cut,!,fail)).
%-------------------------------------------------------

I do not know how to implement ancesstor cut (see Shapiro and Sterling
book) without the use of ancesstor cut at the meta-level, and how to
implement meta-circular interpreter with cut without the use of the
above assumption.

-- raf

------------------------------

Date: 22 Apr 87 14:52:00 EST
From: John Cugini <cugini@icst-ecf.arpa>
Subject: shortest path algorithm for graphs

Although this is a fairly classic algorithm, (from Baase, Computer
Algorithms) I thought it might be worth broadcasting.  The essential
idea is that you progress outward from a source node, always taking
the shortest path available, until you stumble across the Target - so
it's a kind of breadth-first search, with the understanding that
length is as measured by the arcs traversed, not simply the number of
nodes.

-- John Cugini

%% Shortest path algorithm

% edges are bi-directional in this example.  For uni-directional
% edges, just put in edge(From, To, Dist) facts, like eg:
/*
edge(c, a, 4).
edge(a, b, 2).
edge(b, c, 3).
edge(b, d, 5).
edge(d, e, 6).
edge(e, a, 1).
*/

edge(N1, N2, Dist) :-
  bi_edge(N1, N2, Dist);
  bi_edge(N2, N1, Dist).

bi_edge(a, b, 2).
bi_edge(b, c, 10).
bi_edge(b, d, 3).
bi_edge(d, c, 4).
bi_edge(d, e, 18).
bi_edge(d, f, 6).
bi_edge(e, f, 7).

% shortpath(N1, N2, Path, Tot_dist) iff Path is the shortest path from
% N1 to N2 (expressed as a list of nodes), with a total distance of
% Tot_dist.  This is the user's top-level predicate.  Only N1 must be
% instantiated.  If N2 is var, shortpaths to all other nodes will be
% found in order of increasing length.

shortpath(N1, N2, Path, Tot_dist) :-
  nonvar(N1),
  seek_next(N1, N2, [node_state(N1, reached, 0, [])], 0, Path, Tot_dist).

% seek_next(From, Target, State_list, Dist_so_far, Path, Tot_dist) seeks the next
% closest node to the source, searching out from From.

seek_next(Target, Target, State_list, Tot_dist, Path, Tot_dist) :-
  build_path(Target, State_list, R_path), 
  reverse(R_path, Path).
seek_next(Here, Target, State_list, Dist_so_far, Path, Tot_dist) :-
  (setof(Next-This_dist, edge(Here, Next, This_dist), Adj_list) ->
     process_all_adj(Here, Adj_list, State_list, Dist_so_far, Mid_list);
     Mid_list = State_list
  ),
% blithe assumption here that no valid path will exceed 1e38 in length.
  get_best_nearby(Mid_list, 1e38, dummy, Best_name, Best_dist, New_list),
  seek_next(Best_name, Target, New_list, Best_dist, Path, Tot_dist).

% process_all_adj(Here, Adj_list, State_list, Dist_so_far, New_list) checks all
% nodes adjacent to Here, and updates their status and distance in the State_list,
% generating a New_list of states.  Each entry in the state-list is of the form:
% 
%     node_state(Name,        % unique identifier of node
%                Status,      % reached, nearby (adjacent to a reached node), or unseen
%                Distance     % shortest distance from source found so far.
%                predecessor  % predecessor node in best-so-far path
%               )

process_all_adj(Here, [Next-This_dist | Rest_adj], State_list, Dist_so_far, New_list) :-
  (get_state(Next, State_list, Status, Dist_next, Predecessor) ->
     true;
     Status = unseen
  ),
  !,
  Poss_dist is Dist_so_far + This_dist,
  adjust_state_list(Status, Poss_dist, Dist_next, Next, Here, State_list, Inter_list),
  process_all_adj(Here, Rest_adj, Inter_list, Dist_so_far, New_list).
process_all_adj(Here, [], State_list, Dist_so_far, State_list).

% adjust_state_list(Status, Poss_dist, Dist_next, Next, Here, State_list, Inter_list)
% updates the single entry for the Next node in the State_list, producing a partially
% updated list, Inter_list.

adjust_state_list(unseen,  Poss_dist, Prev_dist, Next, Here, State_list,
                           [node_state(Next, nearby, Poss_dist, Here) | State_list]).
adjust_state_list(reached, Poss_dist, Prev_dist, Next, Here, State_list, State_list).
adjust_state_list(nearby,  Poss_dist, Prev_dist, Next, Here, State_list, Inter_list) :-
  Poss_dist < Prev_dist ->
    change_state(Next, nearby, Poss_dist, Here, State_list, Inter_list);
    Inter_list = State_list.

% get_best_nearby(State_list, BSF_dist, BSF_name, Best_name, Best_dist,
% New_list) scans the State_list and finds the nearby (but unreached)
% node with the lowest distance from the source, and marks it as
% reached.  BSF distance and name are the Best So Far, as the list is
% scanned.  If this fails, then there are no nearby nodes and the whole
% shortpath predicate fails, indicating that no path exists between the
% requested nodes.

% handle nearby nodes
get_best_nearby([node_state(Name, nearby, Dist, Pred) | Old_rest], 
                BSF_dist, BSF_name, Best_name, Best_dist, [New_elem | New_rest]) :-
  (Dist < BSF_dist ->                                                             
    (New_dist = Dist,     New_name = Name);
    (New_dist = BSF_dist, New_name = BSF_name)
  ),
  !,
  get_best_nearby(Old_rest, New_dist, New_name, Best_name, Best_dist, New_rest),
  (Name = Best_name ->
     New_elem = node_state(Name, reached, Dist, Pred);
     New_elem = node_state(Name, nearby,  Dist, Pred)
  ).

% handle reached nodes
get_best_nearby([Elem | Old_rest], BSF_dist, BSF_name, Best_name, Best_dist, 
                [Elem | New_rest]) :-
  get_best_nearby(Old_rest, BSF_dist, BSF_name, Best_name, Best_dist, New_rest).
                                  
get_best_nearby([], BSF_dist, BSF_name, BSF_name, BSF_dist, []) :-
% again, blithe assumption here that no valid path will exceed 1e38 in length.
  BSF_dist < 0.99e38.

% build_path(This, State_list, Path) constructs Path backwards from This by
% using the Predecessor field in the node_states of State_list.

build_path(This, State_list, [This | Rest]) :-
  get_state(This, State_list, _, _, Pred),
  (Pred = [] -> 
     Rest = [];
     build_path(Pred, State_list, Rest)
  ).

% get_state(Name, State_list, Status, Dist, Predecessor) finds the node_state
% entry in State_list with Name as its name, and returns its Status,
% Distance and Predecessor.

get_state(From, [node_state(From, Status, Dist, Predecessor) | Rest],
                                  Status, Dist, Predecessor) :- !.
get_state(From, [Elem | Rest], Status, Dist, Predecessor) :-
  get_state(From, Rest, Status, Dist, Predecessor).

% change_state(Name, Status, Distance, Predecessor, Old_list, New_list) changes
% the existing entry for Node in the Old_list, to produce the New_list.

change_state(Name, Status, Distance, Predecessor,
             [node_state(Name, _, _, _) | Rest],
             [node_state(Name, Status, Distance, Predecessor) | Rest]) :- !.

change_state(Name, Status, Distance, Predecessor, [Elem | Old_Rest], [Elem | New_Rest]) :-
  change_state(Name, Status, Distance, Predecessor, Old_Rest, New_Rest).

% Standard utilities here

reverse(List1,List2) :- reverse_1(List1, [], List2).
reverse_1([], List2, List2) :- !.
reverse_1([Head | Tail], So_far, List2) :-
  reverse_1(Tail, [Head | So_far], List2).

------------------------------

End of PROLOG Digest
********************