🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.prolog
Article #3374

Re: Clause fusion

#3374
From: Saumya Debray
Date: Mon, 16 May 1988 19:23
165 lines
6102 bytes
In article <983@cresswell.quintus.UUCP>, Richard A. O'Keefe writes
about the "obvious" optimization of clause fusion:

> The original code was
> 
> 	association(Stack, Key, Val, absent) :-
> 		is_empty(Stack),
> 		!.
> 	association(Stack, Key, Val, Result) :-
> 		pop_stack(Key1, Val1, Stack, Stack1),
> 		Key1 \== Key,
> 		!,
> 		association(Stack1, Key, Val, Result).
> 	association(Stack, Key, Val, present) :-
> 		pop_stack(Key1, Val1, Stack, Stack1),
> 		Key1 == Key,
> 		Val1 = Val,
> 		!.
> 	association(Stack, Key, Val, conflict).

Richard points out several problems with a proposed solution.  It
turns out, however, that the problems are not with Clause Fusion itself,
but rather with some subsequent transformations involving moving cuts
around a clause.  It shouldn't be surprising that this is not an easy
thing to do in general.

It's interesting to transform this code a little at a time, to
see what assumptions are necessary at each step in order to preserve
equivalence.  To reduce clutter as far as possible in what follows,
I'll consider only the clauses that Richard fused, and ignore the first
clause in this definition.
--------------------
STEP 1: No assumptions: all we can do is move some arguments
from the head to just beyond the neck of the clause, to get:

 	association(Stack, Key, Val, Result) :-
 		(pop_stack(Key1, Val1, Stack, Stack1),
 		 Key1 \== Key,
 		 !,
 		 association(Stack1, Key, Val, Result)
		) ;
	     (Result = present,	  		     
 		 pop_stack(Key1, Val1, Stack, Stack1),
 		 Key1 == Key,
 		 Val1 = Val,
 		 !
		) ;
		Result = conflict.
--------------------
STEP 2: Assume that association/4 is always called with its
last argument free.  This allows us to delay the unification
"Result = present" in the second disjunct, to get the disjuncts:

   (pop_stack( ... ), Key1 \== Key, ... ) ;
   (pop_stack( ... ), Key1 == Key, Val1 = Val, !, Result = present) ;
   Result = conflict.
--------------------
STEP 3: Assume pop_stack/4 is free of side effects.  This allows us to
factor the literal for pop_stack/4 in the disjunct, producing

 	association(Stack, Key, Val, Result) :-
          (pop_stack(Key1, Val1, Stack, Stack1),
 		 (Key1 \== Key,
 		  !,
 		  association(Stack1, Key, Val, Result)
		 ) ;
	      ( /* Key1 == Key, */
 		  Val1 = Val,
 		  !
            Result = present
		 )			
          ) ;
          Result = conflict.

We can't do much more at this point.  Note that the inner disjunction
can't really be converted to an if-then-else, because the cut is necessary
to cut away the alternative "Result = conflict" in the outer disjunct.
Notice also that even if pop_stack/4 is known to be deterministic, we can't
transform this to

	   pop_stack( ... ) ->
	        ( ... ) ;
		   Result = conflict.

(Consider the case where pop_stack/4 succeeds, "Key1 \== Key" fails, and
 "Val1 = Val" fails.)
--------------------
STEP 4: Richard assumed, in addition, that association/4 is always called
with its first three arguments ground terms.  Looking at the code, it seems
to me that the predicate is intended to search a (given) stack for a value
associated with a (given) key, so I think it's more reasonable to assume
that the third argument to association/4 is also free in each call to it.
If we make this assumption, then the unification "Val1 = Val" (after
"Key1 == Key") can also be delayed until after the cut.  This transforms
the inner disjunct to

	   	  (Key1 \== Key, !, association( ... ) ) ;
	   	  (!, Val1 = Val, Result = present)

This, with some easy massaging, yields

 	association(Stack, Key, Val, Result) :-
          (pop_stack(Key1, Val1, Stack, Stack1),
 		 (Key1 \== Key ->
 		     association(Stack1, Key, Val, Result) ;
			(Val1 = Val,
                Result = present
			)
		 ),
		 !
          ) ;
          Result = conflict.
--------------------
STEP 5: Assume that association/4 is deterministic.  This allows us to
bubble the cut backwards, eventually yielding

 	association(Stack, Key, Val, Result) :-
          (pop_stack(Key1, Val1, Stack, Stack1),
		 !,
 		 (Key1 \== Key ->
 		     association(Stack1, Key, Val, Result) ;
			(Val1 = Val,
                Result = present
			)
		 )
          ) ;
          Result = conflict.

It's now a simple matter to reformat this to

 	association(Stack, Key, Val, Result) :-
        pop_stack(Key1, Val1, Stack, Stack1) ->
		 (Key1 \== Key ->
 		     association(Stack1, Key, Val, Result) ;
			(Val1 = Val,
                Result = present
			)
		 ) ;
           Result = conflict.
--------------------
[ If we don't want to assume that association/4 is always called with
  its third argument uninstantiated, then different (and, in my opinion,
  less reasonable) assumptions are necessary to take the transformation
  beyond step 3 above.  This article's getting too long for that. ]

The resulting code is seen to be quite different from the proposed

> 	association(Stack, Key, Val, Result) :-
> 		pop_stack(Key1, Val1, Stack, Stack1),
> 		(   Key1 \== Key ->
> 		    association(Stack1, Key, Val, Result)
> 		;   Val1 = Val ->
> 		    Result = present
> 		;   Result = conflict
> 		).

To sum up: the problems mentioned by Richard are not with clause fusion
per se, but rather with some subsequent transformations involving 
moving cuts around a clause.  Not surprisingly, this is nontrivial
in general.
-- 
Saumya Debray		CS Department, University of Arizona, Tucson

     internet:   debray@arizona.edu
     uucp:       {allegra, cmcl2, ihnp4} !arizona!debray
Technical Details
Message-ID: <5501@megaron.arizona.edu>
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!husc6!bloom-beacon!tut.cis.ohio-state.edu!mailrus!ames!ncar!noao!arizona!debray
References: <983@cresswell.quintus.UUCP>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!husc6!bloom-beacon!tut.cis.ohio-state.edu!mailrus!ames!ncar!noao!arizona!debray
From: debray@arizona.edu (Saumya Debray)
Newsgroups: comp.lang.prolog
Subject: Re: Clause fusion
Summary: optimization not all that obvious, in general
Message-ID: <5501@megaron.arizona.edu>
Date: 16 May 88 19:23:05 GMT
References: <983@cresswell.quintus.UUCP>
Organization: U of Arizona CS Dept, Tucson
Lines: 165