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

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

PROLOG Digest            Monday, 12 Oct 1987       Volume 5 : Issue 72

Today's Topics:
         Query - Algebraic simplification & Prolog Machines,
                Implementation - Asthetics & findall/3
----------------------------------------------------------------------

Date: 2 Oct 87 10:07:36 GMT
From: Eddy Szulcm <cvax!dlt1!szulc@uunet.uu.net>
Subject: Algebraic simplification

I'm looking for an algorythm, preferably in Prolog, for simplifying
an algebraic expression. My first attempts can handle expressions of
the form 2+a+3 => 5+a, but gets lost with 2+a+3+b+4. I know that I
could flatten the trees, sort them and then re-construct them, but I
need something that works for all sorts of operators and that can be
defined by simple rules.

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

Date: 6 Oct 87 14:48:00 GMT
From: iuvax!silver!bondc@rutgers.edu
Subject: Re: Algebraic simplification algorythm


A-L-G-O-R-I-T-H-M

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

Date: 6 Oct 87 02:53:14 GMT
From:Mark Thomsen <trwrb!trwspf!thomsen@ucbvax.Berkeley.EDU>
Subject: Looking For Prolog Machines

Help me on a search if you can.

In our laboratory we have considerable interest in running things
quickly, and have developed and bought a number of fast non-general
processors for implementing certain functions or languages quickly.
We are preparing to work with Prolog for some embedded expert and
deductive processing functions, and I am not familiar with what can be
purchased to run Prolog programs fast.  If left in our current state
of ignorance, we will probably buy Modula-Prolog, put it on MC68020's
(where we have an excellent Modula-2 compiler), and do our own speed
enhancements.

What is out there that runs Prolog fast?  I have heard a little about
the ICOT effort on PSI, but have not technical article or description.
I have also seen articles in the past on extracting concurrency,
Concurrent Prolog, and the like -- are there concurrent Prolog
machines?

I would like any references or descriptions on Prolog machines that
you guys know of.  I will submit a summary of received data in a few
weeks.  Thanks!

-- Mark R. Thomsen

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

Date: 9 Oct 87 13:35:11 
Gcvax!dartvax!tedc@ucbvax.Berkeley.EDU  (Ted Cooley)
Subject: Re: Looking For Prolog Machines

There may also be the Xenologic Processor.  This machine is to be
an add-on to the SUN workstations.  Claims of up to 300Klips are claimed.

Xenologic is in Berkeley, CA.


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

Date: 9 Oct 87 13:38:05 GMT
From: mit-vax!jouvelot@eddie.mit.edu  (Pierre Jouvelot)
Subject: Re: Looking For Prolog Machines

This followup is from a friend (Akihiko Konagaya)

-- Pierre

It is true that PSI is rather slower. But it's not unfair to compare a
toy hardware and a workstation with a full programming environment.
Processor performance is considerablly diminished by various traps,
which are required for the environment.

As for performance, new PSI achieves 200-250 KLIPS. This is fairly
good because the performance of most useful application programs are
determined by the performance of built-in predicates but "append
program".  Futhermore, we know that 2 Mega LIPS is not so difficult,
if we develop a custom Prolog chip with 20 MHz.

The fastest Prolog machine currently available would be CHI-II
developed at NEC. It achieved 500KLIPS (See Habata etal, "Co-operative
High Performance Sequential Inference Machine" in ICCD '87 New York
for the details).  The full programming environment that includes
multi-window interface, mutiple process environment as well as
ordinary programming tools such as an optimizing compiler, an
interpreter with incremental compiler, would be available at the end
of 1987.

CHI-II provides an extended prolog, named SUPLOG, which is powerful
enough to implement whole system programs on CHI-II. It supports not
only whole prolog language features but also supports multiple name
space, interprocess communication facilities and various data types
(characters, arrays, streams etc). The subset of SUPLOG will be
available on a UNIX workstation soon.

The prototype CHI, oridinally named HPM, achieved 280 KLIPS. Its
architecture and an programming environment are reported in:

   Nakazaki etal, "Design of a high-speed prolog machine HPM",
   in Proc. of Computer Architecture, 1985.

   Konagaya etal, "A Co-Operative Programming Environment for a Back
   -End Type Sequential Inference Machine CHI", in Proc. of
   Parallel Algorithms and Architecture, 1987, Akademie-Verlag Berlin.

Copies are available until Jan. 1988 in the following address:

   Akihiko Konagaya
   MIT Laboratory for Computer Science,
   545 Technology Square, room 252,
   Cambridge, MA 02139

In case of after Jan. 1988, try to access:

   Akihiko Konagaya
   Computer System Laboratory,
   C&C Systems Laboratories,
   NEC Coporation, 1-1 Miyazaki, 4-chome, Miyamae,
   Kawasaki, Kanagawa, 213 Japan

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

Date: Thu, 1 Oct 87 19:46:40 PDT
From: quintus!cresswell!ok@Sun.COM (Richard A. O'Keefe)
Subject: findall/3


    This is really very embarrassing.  Lagache's library is such a
wonderful source of examples that I keep writing about it.  I am so
very sorry about this.  Here is a public-spirited sort of fellow, no
doubt expecting nothing but thanks, and here I am saying all sorts
of critical things about his code.  But what a wonderful source of
examples!  There are so many things to criticise, and everyone who
gets the Prolog Digest will know what I am talking about.

    This time, it isn't really his fault.  Lagache's version of
findall/3 has a serious bug, but this bug was present in the code
in Clocksin & Mellish which he copied, and in his copy he has at
least tried to reduce the chance of the bug biting.

    Here is the code from Clocksin & Mellish:

        findall(Template, Generator, List) :-
                asserta(found(mark)),
                call(Generator),
                asserta(found(Template)),
                fail.
        findall(Template, Generator, List) :-
                collect_found([], Matches),
                !,
                List = Matches.

        collect_found(Matches0, Matches) :-
                getnext(Match),
                !,
                collect_found([Match|Matches0], Matches).
        collect_found(Matches, Matches).

        getnext(Match) :-
                retract(found(Match)),
                !,
                Match \== mark.

    The bug, of course, is that Template musn't ever be bound to 'mark'.
The query
        ?- findall(X, member(X,[tom,dick,harry]), L).
works, but the query
        ?- findall(X, member(X,[matthew,mark,luke,john]), L).
doesn't work at all.

    Lagache has improved this code by using a much less plausible atom.
But what a pity that he didn't take the trouble to eliminate the bug
entirely.

    As a matter of fact, it isn't Clocksin & Mellish's fault either,
really.  The bug is present in DEC-10 Prolog.  Which is why I care
about the bug.  You see, I was bitten by it.  In all innocence, I
wrote a DEC-10 Prolog program which did something perfectly
sensible, but it broke mysteriously.  I just could not figure out
what was going wrong.  Fernando Pereira took one look at it, and saw
the problem at once.  The moral of the story is that there is no atom
so bizarre that a programmer may not encounter it quite innocently.

    How can we avoid the bug?  By systematic design in the first place.
There are two conditions we want to represent on the stack:
        - there is a marker here
        - there is a solution X here
How do you do that?  Always, Always, Always, the right way to represent
cases in Prolog is to have a different constructor function for each.
(You may have to deal with "anything else" cases in the input of your
programs, but you shouldn't design your own data structures that way.)
These two cases map DIRECTLY onto

        - marker
        - solution(X)

    Another point, which isn't exactly a bug, is that it is rather
regrettable to take such an obviously useful predicate name as
found/1 away from the user.  We don't need to do that.  (It is
far easier for a user to avoid predicate names with spaces in them
than to avoid an atom he isn't supposed to know about.)

    Accordingly, we can code findall/3 like this:

        findall(Template, Generator, List) :-
                asserta('findall stack'(marker)),
                call(Generator),
                asserta('findall stack'(solution(Template))),
                fail.
        findall(Template, Generator, List) :-
                'findall collect'([], List).

        'findall collect'(List0, List) :-
                retract('findall stack'(Item)),
                !,
                'findall collect'(Item, List0, List).

        'findall collect'(marker, List, List).
        'findall collect'(solution(X), List0, List) :-
                'findall collect'([X|List0], List).

This has one cut, not three, and no instances of \==, and not only
is it less ugly, it doesn't suffer from the bug.  In fact, if you
define another library predicate:

        retract_first(Clause) :-
                retract(Clause),
                !.

which is generally useful, we can see that the one surviving cut
hasn't really got anything to do with findall/3 as such.

    Much may be forgiven the pioneers of a field.  I do not fault
the designers of DEC-10 Prolog for this bug.  (They went on to
produce Quintus Prolog, which naturally has NOT got this bug.)
However, it has always been a bug, not a feature.

                +-------------------+
                |READ THIS PARAGRAPH|
                |EVEN IF NAUGHT ELSE|
                +-------------------+
    What is really important here is this systematic approach to
designing data structures.  If you want to represent situations
        s1 with data d11,...,d1k1
        ...
        sn with data dn1,...,dnkn
use terms
        s1(D11,...,D1k1)
        ...
        sn(Dn1,...,Dnkn)
to do it, so that you can tell later on what you meant by looking
at the principal functor.  If there is a most common case, it is
very tempting to ex-Lisp programmers to make that the "else" case
of an if-then-else (or a 'caseq') and avoid the "inefficiency" of
putting a wrapper around that case.  DON'T DO IT.  It will actually
make your programs LESS efficient (because there will be a 'variable'
case in your predicates, which will need cuts in all the preceding
clauses, which take time to execute, and you'll probably get
temporary choice-points you can do without).


    Lagache's code for findall/3 is a straightforward improvement
of Clocksin & Mellish's.  But his documentation is his own.  And
that is very strange.  Here is that documentation, retyped rather
sloppily so that you won't have to look it up:

        findall(Variable_atom, Predicate, Binding_list)

        generates a list of all values of Variable_atom
        that satisfy Predicate.

        findall is the classic predicate for collecting all
        the values of Variable_atom that are true of Predicate.
        The values are returned on list Binding_list.
        Both Binding_list and Variable_atom should be unbounded.

        Prolog code is based on Clocksin and Mellish p 162.
        The variable Variable_atom must be some atom in the
        Predicate call.  For example, to build up the list of
        integers between 1 and 10 the following call will
        do the trick:
                findall(X, between(1,10,X), Numberlist)


(a) What is a 'variable atom'?
    The first argument is usually a variable.  But it may be
    ANY term, and it is very useful to pass a skeletal term
    (a compound term with unbound arguments).  It even makes
    sense to pass a constant, sometimes.

    The argument need not be an atom "in the Predicate call".
    Presumably he means "the Generator must bind the Template
    to an atom".  In fact, in his own example, the Template
    gets bound to integers, not atoms.

    It is true that findall/3 doesn't make much sense if the
    Generator doesn't bind the Template to a ground term.
    But if you want to call
        findall(Name, (member(X,[fred,jim]),name(X,Name)), Names)
    go right ahead.  It will work.

(b) The second argument is a Goal, not a Predicate.
    Anything that is acceptable to call/1 is acceptable here,
    which is to say that anything which is acceptable as the
    body of a clause is acceptable.

(c) Is 1 really "true of" between(1,10,X) ?

(d) Presumably he means "unbound", not "unbounded".  In fact,
    there isn't the slightest need for the third argument to
    be unbound.  This is one of the few predicates where
    Lagache warns you about the need for output arguments to
    be unbound, and the irony is that findall/3 is steadfast!
    Suppose you wonder whether there are at least two proofs
    for a goal G.  It makes perfect sense to call
        findall(*, G, [*,*|_]).
    Go ahead and do it.  findall/3 won't mind.  It'll even work!


Lagache goes on to say in his documentation:
    LIMITATIONS: None.
        There are certainly fewer limitations than he thought.
    BUGS: No Known Bugs.
        Well, now it's known.


    To summarise this section, there is a serious problem with the
code that Lagache copied, but that isn't his fault.

    Lagache also offers a predicate countmatch/2.  There are
two things that
        countmatch(Goal, Count)
might have meant:
        how many SOLUTIONS are there?
        how many PROOFS could Prolog find?
His documentation makes it perfectly clear that he means the
latter.  There is also a sum_match/3.

    Let's look at his code.  (By the way, I can't thole run-on
words.  The underscore is there.  Let's use it.  The Melbourne
Mob prefer capitalisation, as in countMatch.  See Arthur Sale's
article in SP&E, or any good book on general programming style.
"ADA in Practice" has some good tips.)

        count_match(Generator, _) :-
                asserta(current_count(0)),
                call(Generator),
                increment_current_count,
                fail.
        count_match(_, Count) :-
                current_count(Count),
                retract(current_count(Count)).

        increment_current_count :-
                current_count(M),
                N is M+1,
                retract(current_count(M)),
                asserta(current_count(N)),
                !.

    Is there anything wrong with this?  Yes, it is neither
steadfast nor backwards correct.  If I ask whether a goal G
has exactly 2 proofs by calling
        count_match(G, 2)
and it turns out to have 3, what happens?  The call
current_count(2) in the second clause of count_match/2 fails,
and the counter is never retracted.  Is that so bad?  Yes,
because if this call to count_match/2 is dynamically nested
inside another call to count_match/2 (and why should it not
be?) the counter call will start using the inner call's
counter.  OH DEAR.

    Using the predicate retract_first/1 defined earlier, we
can code count_match/2 thus:

        count_match(Generator, _) :-
                asserta('count match'(0)),
                call(Generator),
                retract_first('count match'(Sum0)),
                Sum1 is Sum0+1,
                asserta('count match'(Sum1)),
                fail.
        count_match(_, Count) :-
                retract_first('count match'(Total)),
                Count = Total.

    To the best of my belief, this is a correct implementation of
the operation that Lagache documented.

    His sum_match/3 has a very serious bug:  he uses assertz/1
rather than asserta/1, which means that calls to sum_match/3
cannot be nested.  Since he got count_match/2 very nearly right,
this is probably a slip rather than a systematic error.  There
is a detail which is good: he checks that the number found by
the generator IS a number before trying to add it to the Sum.
But sum_match/3 has the same problem as count_match/2:

        zero_sum(Template, Generator) :-
                sum_match(Template, Generator, 0)

will do terrible things if the sum is NOT 0.  Again, recoding as

        sum_match(Template, Generator, _) :-
                asserta('sum match'(0)),
                call(Generator),
                integer(Template),
                retract_first('sum match'(Sum0)),
                Sum1 is Sum0+Template,
                asserta('sum match'(Sum1)),
                fail.
        sum_match(_, _, Sum) :-
                retract_first('sum match'(Total)),
                Sum = Total.

fixes the bug, and we can see by inspection that count_match(G, C)
is equivalent to sum_match(1, G, C).

    However, neither count_match/2 nor sum_match/3 is what we
usually want.

    The basic problem is the distinction between SOLUTIONS and PROOFS.
We typically want solutions, and if we want to build logical relations
solutions are what we need, but what Prolog finds is proofs.  Normally,
we couldn't care less how many proofs there are.

    Let's look at a very simple case.  We have a relation
        phone(Office, PhoneNumber)
and we'd like to be able to count the number of phones in an office.
Modulo the justified criticisms of setof/3, the right way to do this
is
        phone_count(Office, Count) :-
                setof(Phone, phone(Office,Phone), Phones),
                length(Phones, Count).

Given a particular phone, we can ask about how many phones it has.
Given a count, we can ask which offices have that many phones.
Or, if phone/2 can be enumerated, Prolog can enumerate pairs of
Offices and Counts.

    Now suppose phone/2 is defined like this:

        phone(Office, Number) :-
                occupant(Office, Person),
                extension(Person, Number).

Fine.  But if an office has two occupants,
        count_match(phone(Office,_), Count)
will return 2 when it should have returned 1.

    This is not a criticism of Lagache or his library.  He never
claimed that count_match/2 would be useful in a case like this.

    setof/3 and bagof/3 have a property which some people find
strange (who have never fully realised the difficulties implied
by the connection between setof(X, G, []) and ~G), namely that
they will never return an empty list.  There are useful hybrids
between {setof,bagof}/3 and findall/3:

        set_of_all(Template, Generator, Set) :-
                'sound query'(Generator, Template, Goal),
                findall(Template, Goal, List),
                sort(List, Set).

        bag_of_all(Template, Generator, Bag) :-
                'sound query'(Generator, Template, Goal),
                findall(Template, Goal, Bag).

        'sound query'(Exvar^Generator, Template, Goal) :- !,
                'sound query'(Generator, Exvar^Template, Goal).
        'sound query'(Goal, Template, Goal) :-
                numbervars(Template, 0, N1),
                numbervars(Goal, N1, N2),
                N2 > N1,
                !,
                /* report the error and only then */
                fail.
        'sound query'(Goal, _, Goal).

This checks that there are no variables in the Generator
which are not captured by the Template or existential quantifiers.
In that case, and ONLY in that case, it would be sound for
the routines to return an empty list of solutions.

        phone_count(Office, Count) :-
                set_of_all(Phone, phone(Office,Phone), Phones),
                length(Phones, Count).

will give the correct results, provided that Office is instantiated,
and if Office is not ground at the time of all, it will report an
error.  If you want to know how many phones are associated with
offices,
        set_of_all(P, Office^phone(Office,P), Phones)
will give you the set (list in standard order with no duplicates)
of such phones.

    It is very hard to find examples where count_match/3 or
sum_match/3 would be useful.  I have generally found it to be the
case that I wanted some other information about the results as
well.  I would be interested to see examples where some more
general operation would not be more useful.

    By the way, the Quintus library contains a predicate
aggregate/3, so that to find the mean area of the offices in
each department one could write

        mean_office_area(Dept, MeanArea) :-
            aggregate(sum(A)/sum(1),
                Office^(dept_office(Dept,Office), area(Office,A)),
                TotalArea/Count),
             MeanArea is TotalArea/Count.

This will of course enumerate departments with their mean areas
as well as finding the mean area of a given department.  It is
very easy to build such a predicate on top of setof/3, and for
operations like finding the average, minimum, or maximum,
refusing to yield an empty list is just what the doctor ordered.
It would be interesting to discuss what the aggregate/3 operation
should look like:  the present version is satisfactory but there
is more that it could do.

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

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