[comp.lang.prolog] :- deterministic

ok@quintus.UUCP (Richard A. O'Keefe) (04/26/88)

HP Prolog has a declaration ":- deterministic".  The entire description
in the manual reads thus:

    "	The deterministic predicate specifies predicates having at most
	one solutions.

	@b<deterministic> @i<Pred1>, @i<Pred2>, ...

	Predn	: a predicate specification.

	The specified predicates are deterministic, i.e. they can never
	have more than one solution.  This information is used by the
	compiler and interpreter to increase efficiency.  A determinstic
	declaration must stand before all clauses of the predicate in the
	source file.	"

Now plagiarism is the sincerest form of flattery, and I don't see why
Quintus shouldn't flatter any good ideas that are going around, but it
is hard to steal an idea you don't understand.  My problem is that I
can't figure out what :- deterministic @i<means>.  (Somewhere at the
bottom of about 20 feet of reports and papers I have a copy of something
by Kamran Parsaye, I think in 1982 or 1983, where he proposed a
:-determinate declaration, or was it :-function pred(<mode>), but I
don't remember grasping that either.)

There is an obvious kludge, namely interpreting this as "add a cut at
the end of every clause of the indicated predicate", but that is clearly
not what is intended, because it would be a sure-fire recipe for making
non-steadfast predicates.  What I'm getting at is that if you do e.g.

	:- deterministic fac/2.

	fac(0, 1).
	fac(1, 1).
	fac(N, F) :- M is N-1, fac(M, G), F is G*N.

then it should be translated as

	fac(0, X) :- !, X = 1.
	fac(1, X) :- !, X = 1.
	fac(N, F) :- M is N-1, fac(M, G), F is G*N.

not as

	fac(0, 1) :- !.

and so on.  So presumably the :-deterministic directive must take the
declared :-mode into account, but how exactly does it do this?  Anyone know?

ok@quintus.UUCP (Richard A. O'Keefe) (04/26/88)

The HP Prolog manual says a little bit more about :-deterministic:
    "	HP Prolog features a mechanism called declarative determinism.
	By declaring a predicate which is known to only succeed once
	determinstic, efficiency can be gained in interpreted and
	compiled code.  This is preferable to cluttering a predicate
	definition with cuts.  "
which is tantalising, but not explicit.

fritz@hpfclp.SDE.HP.COM (Gary Fritz) (04/26/88)

Caveat:  I am not one of the designers/implementors of HP Prolog, and 
therefore do not pretend to understand all the subtleties of the design.  
Until I get one of the designers to respond, though, allow me to make
a few remarks:

The deterministic declaration does result in behavior rather like that
achieved by inserting a cut at the end of the predicate.  There is,
however, a more useful effect.  Since the compiler knows that predicate X
is deterministic, and cannot possibly backtrack, it can emit more efficient
efficient code to call it.

Since HP Prolog is implemented in Common Lisp, nested closures were used 
to implement success continuations.  This happens to be very efficient in
HP's Common Lisp, but obviously it's more efficient if you don't have to
do this.  The deterministic declarations allow the compiler to just call
the predicate without pushing a level of closures.

You could garner the same information by examining the definition of
predicate X, but the declaration has a more global effect (just like
compiler declarations in other languages).  It can be made available 
in other source files, etc.

Is this what you were looking for, or am I just stating the obvious?
I'm afraid I don't understand what you mean by a "non-steadfast" predicate,
so I can't address that.  Care to enlighten me?

Gary

voda@ubc-cs (Paul Voda) (04/27/88)

I do not know how the deterministic predicates are implemented in HP-Prolog,
but I do know what they are in Trilogy. Maybe this will help.

When you declare a Trilogy predicate as a 'proc' rather than a 'pred'
then the Trilogy system uses the mode information about the arguments
extensively in order to check that the predicate can never backtrack.

Consequently, a call to a deterministic predicate, i.e. to a procedure,
can return at most one set of results. The compiler generates a code
of Pascal-like quality in such a case.

Generally, the system checks that all calls to 'preds', i.e. to the
true backtracking predicates are enclosed in an all (min, one) solution
construct, that there are no values returned through an "or" and
that there are no symbolic (logic) variables with constraints used
(except enclosed in all,min one constructs).

For instance the declaration

    proc P(x:>I) iff x = 1 | x = 2

with the output argument "x" is rejected  because in order 
to satisfy the goal P(2) which is compiled as P(z) & z = 2 we need
backtracking.
Incidentally, the version 1.3 of Trilogy now contains a one-solution
construct of the form

          one x,y..,z  A(x,y,z)

where one (the first solution found) satisfying the formula A is
communicated to the outside through the variables x,y,z. The one
solution construct can also contain non-local parameters.

The one-solution construct in Trilogy is a tame version of the
cut of Prolog. I have included it in the version 1.3 and not in
the previous versions because until now I could not explain it
logically, and obviously I do not want any hacks in Trilogy.

I have written a paper on the one solution construct which I will
mail to all interested parties on request. The title is:

     The Logical Reconstruction of Cuts as One Solution Operators.

ok@quintus.UUCP (Richard A. O'Keefe) (04/28/88)

In article <2217@ubc-cs.UUCP>, voda@ubc-cs (Paul Voda) writes:
> I do not know how the deterministic predicates are implemented in HP-Prolog,
> but I do know what they are in Trilogy.  Maybe this will help.
Not really.  Trilogy is a logic programming language which makes no
pretence of being Prolog.  (In some ways it resembles ALEPH, except
that it is a lot purer.)  Trilogy is allowed to reject programs as
invalid based on a data flow analysis; Prolog systems are not.
The HP-Prolog manual I have a (partial) copy of does not mention any
restriction on :-deterministic predicates at all, and as it is an
incremental system, I doubt that it is doing much data-flow analysis.

> I have written a paper on the one solution construct which I will
> mail to all interested parties on request. The title is:
> 
>      The Logical Reconstruction of Cuts as One Solution Operators.

I have received a copy of this.  It's a nice paper, but it makes me even
more unhappy with cuts than ever.  There is a question: how often do we
really need the _control_ component to say "commit to the first solution"
rather than "commit to any solution"?  Consider the contrast between

	member(X, [H|T]) <-> X = H | member(X, T)

	member1(X, L) <-> first member(X, L)
-vs-

	member1(X, [H|T]) <-> X = H | X ~= H & member1(X, T)
-or-	member1(X, [H|T]) <-> if X = H then true else member1(X, T)
[if-then-else being sound as in NU Prolog].

It would be really interesting to see some sketches of how people are
using once(Goal), [! Goal !], or (Goal->true) {all the same thing}.

fiorentino@cup.portal.com (05/01/88)

Paul I purchased Trilogy. I would like to be updated and nee
to know when you have the linker ready to make stand-alone
applications

bd@zyx.SE (Bjorn Danielsson) (05/12/88)

Gary Fritz is right: The deterministic declaration has the same effect as
inserting a cut at the end of each clause, or the same effect as wrapping
'PROVE-ONCE' around every call to such a predicate. The main reason for
this is to help the compiler produce better LISP code when block-compiling
a Prolog program.

In particular, a conjunction of goals that have been declared deterministic
can be compiled to something like:

	(and (pred1)
	     (pred2)
	     (pred3)
	     (funcall cont))

instead of the general case, where an explicit success continuation is needed
as an extra argument to all three predicates:

	(pred1 #'(lambda ()
		   (pred2 #'(lambda ()
			      (pred3 cont)))))

Also, deterministic-declarations can be (mis-)used in the same way as cut.
In some situations it is easier just to say "only the first solution to this
predicate is ever needed" rather than inserting cuts at the right places.

Today I consider the deterministic-declaration to be useful only as a "pragma"
for the compiler. As a programming device it is less precise than cut, and it
hardly gives any advantage in a WAM implementation. I think a convenient
"cases" or "cond" macro based on a backtrackable if-then-else is a better
way to make programs deterministic, since that's often what people *mean*
when they misuse cut.

-- 
Bjorn Danielsson, ZYX Sweden AB, +46 (8) 665 32 05, bd@zyx.SE

ok@quintus.UUCP (Richard A. O'Keefe) (05/15/88)

In article <2525@zyx.SE>, bd@zyx.SE (Bjorn Danielsson) explains the
:-deterministic declaration.

HP Prolog sounds a *lot* like PopLog.

> I think a convenient
> "cases" or "cond" macro based on a backtrackable if-then-else is a better
> way to make programs deterministic, since that's often what people *mean*
> when they misuse cut.

I put a "cases" construct into the *.edai versions of C Prolog, but could
never think of anything to do with it.  (I got the idea from LM-Prolog.)
I would very much like to learn how to use this construct.

[I very much doubt that most people *do* mean anything when they use cuts,
 but judging from comp.lang.c there are a lot of C programmers who aren't
 too clear on assignment statements or procedure calls.]