A survey of PARLOG and Concurrent Prolog: The integration of logic and parallelism

A survey of PARLOG and Concurrent Prolog: The integration of logic and parallelism

Comput. Lang. Vol. 18, No. 3, pp. 185-196, 1993 0096-0551/93 $5.00+0.00 Copyright © 1992 Pergamon Press Ltd Printed in Great Britain. All rights res...

913KB Sizes 6 Downloads 94 Views

Comput. Lang. Vol. 18, No. 3, pp. 185-196, 1993

0096-0551/93 $5.00+0.00 Copyright © 1992 Pergamon Press Ltd

Printed in Great Britain. All rights reserved

A SURVEY OF PARLOG AND CONCURRENT PROLOG: THE INTEGRATION OF LOGIC AND PARALLELISM DOMENICOTALIA CRAI, Localit~iS. Stefano, 87036 Rende (CS), Italy E-mail: [email protected] (Received 2 April 1991; revision received 14 February 1992) Abstraet--A new class of programminglanguagesis born from the integrationof logic programmingand concurrent programming: concurrent logic programminglanguages. The main goal of concurrent logic languages is the exploitation of parallelism which is inside logic programs. This paper surveys and compares the two most significantconcurrent logicprogramminglanguages,PARLOG and Concurrent Prolog with respect to the original mechanismsdefined in each one to embody parallelism in a logic programming framework.For both languages,someimplementationtechniquesin parallel environments are discussed. Logic programming

Concurrentlanguages

Non-determinism

Parallelprocessing

1. I N T R O D U C T I O N Logic-based programming languages are today the center of many research efforts. An attractive feature of logic languages derives from their declarative interpretation. Thus, the programmer needs only to specify what computations must be performed rather than how they should be performed. On the other hand, the traditional implementations of logic programming systems on sequential von Neumann architectures, have resulted in inefficient systems. The main reason for this is that the high level of the logic programming paradigm requires more than a translation to generate the machine code of a logic program. Thus, some overhead is added to bridge the gap between the logic programming level and the machine level. On the other hand, imperative languages are closer than logic languages to the machine level because they embody many concepts of von Neumann architecture, such as the sequential step-by-step execution of instructions and the assignment statement. This results in a more efficient execution with respect to logic programs. Parallel computers may constitute an alternative to traditional systems to support the execution of logic programming languages. On these systems, a program is divided into many tasks (processes) that are mapped onto the processing nodes. For the implementation of logic programming systems on parallel computers, many abstract models that exploit the parallelism of logic programs have been proposed. These models can be divided according to the kind of parallelism they exploit and how it is exploited. That is, if the parallelism is specified in a logic program by the programmer (explicit parallelism) or it is extracted by the language support both during static analysis and at run-time (implicit parallelism). Concurrent logic programming languages are designed to execute on parallel machines using explicit parallelism. Thus, a user must specify, by means of annotations, which clauses can be solved in parallel. To offer this computational model, they embody some relevant concepts coming from concurrent programming models, such as CSP. This paper surveys and compares the two most significant concurrent logic programming languages, P A R L O G and Concurrent Prolog with respect to the original mechanisms defined in each one to embody parallelism in a logic programming framework. The paper is organized as follows. The next section overviews the computational model and the parallelism of logic programs. The third section describes the basic concepts of Concurrent Prolog and P A R L O G and the concurrent mechanisms they incorporate. In the fourth section the main features of PARLOG, its differences from Concurrent Prolog, and its implementations, are presented. The same is done for This work has been partially supported by "Progetto FinalizzatoSistemi Informaticie Calcolo Parallelo",of CNR under Grant No. 90.0076.69. 185

186

DOMENICOTALIA

Concurrent Prolog in the fifth section. Finally, the last section concludes the paper by summarizing their common features and their differences. 2. LOGIC PROGRAMMING AND PARALLELISM Logic programming is based on the first order logic and the resolution rule of Robinson. Logic programming languages, such as Prolog [I], are founded on the procedural interpretation of Horn clauses proposed by Kowalski [2]. According to this interpretation, a Horn clause: H<--BI,...,B,.

n>~0

is regarded as a procedure definition, where H is the procedure name and B ) , . , . , B, is the set of procedure calls which constitute the procedure body. Depending on this interpretation, the proof procedure for sequential logic programs solves subgoals in a sequential way. In particular, Prolog solves literals in a conjunction from left to right, and clauses in a program in the textual order, using the backtracking mechanism for handling non-determinism. Non=determinism is due to the fact that a given program and activating goal statement may admit more than a single legitimate computation. In fact, if the unification of a goal with a clause fails, Prolog chooses the next clause with the same head and tries to unify the goal with it. This is not the only possible proof strategy for Horn clauses. In particular parallel strategy may be a valid alternative to this one. The aim of concurrent logic languages [3, 4] is the exploitation of parallelism which is inside logic programs, by means of a parallel proof strategy. There are two major types of parallelism: AND parallelism and OR parallelism. OR parallelism means the parallel evaluation of several clauses whose head unifies with the goal. For instance, if we have the subgoal ?-p(X) and the clauses: p(X):- q(X).

p(X):- r(X). OR parallelism is exploited unifying the subgoal with the head of the two clauses in parallel. AND parallelism consists of the parallel evaluation of each subgoal which compose the current goal. For instance, if the goal to be solved is: ?-p(X), q(Y). according to AND-parallelism, subgoals p(X) and q(Y) are solved in parallel. Notice that Prolog language solves sequentially both cases above described. Concurrent logic languages can be viewed as a new interpretation of Horn clauses, the process interpretation. According to this interpretation, an atomic goal ~-C can be viewed as a process, a conjunctive goal ~ Ct . . . . . C, as a process network, and a logic variable shared between two subgoals can be viewed as a communication channel between two processes. The exploitation of parallelism is achieved through the enrichment of a logic language like Prolog, with a set of mechanisms for the annotation of programs. One of these mechanisms, for instance, is the annotation of shared logical variables, to ensure that they are instantiated by only one subgoal. The goal of the logic variable annotation is to avoid conflicts between subgoals which are solved in parallel (AND parallelism), since they could bind different values to the same variable. Many concurrent logic languages have been proposed, among the most significant there are: Concurrent Prolog [5], PARLOG [6], Guarded Horn Clauses (GHC) [7], and Delta-Prolog [8]. In particular, Concurrent Prolog and PARLOG are the most known in the scientific community. Concurrent logic languages extend the application areas of logic programming from expert systems, natural languages and databases to system-level applications. In fact, Concurrent Prolog and PARLOG have been successfully used to implement applications in the field of operating systems, object=oriented programming, simulation and specification of communicating systems, such as devices composing a VLSI architecture, or a network of independent processors [9]. 3. BASIC CONCEPTS As mentioned before, Concurrent Prolog and PARLOG are two programming languages based on the Horn clauses and designed for parallel execution. They are process-oriented languages with control mechanisms such as guards and dataflow synchronization.

A survey of PARLOG and Concurrent Prolog

187

The model of concurrency utilized by PARLOG and Concurrent Prolog languages is based on the CSP (Communicating Sequential Processes) model [10]. According to this concurrent programming model, a program is regarded as a network of proczsses, each one with its local environment, and cooperating by means of message passing. The main features of the CSP model are: (i) communication management by means of input and output commands and the use of channels, (ii) the exploitation of parallelism by means of the parallel command, and (iii) non-determinism management by guarded commands. These three main features make the CSP model suitable to support the parallel evaluation of the Horn clauses which concurrent logic languages are based on. In particular, communication channels are implemented in PARLOG and Concurrent Prolog by means of logical variables shared between two subgoals. To implement the CSP input/output commands which correspond to read/write on a variable, the two languages define different mechanisms described in the next sections. Both languages use the guard concept to handle non-determinism in the same way as it is used in CSP to delay communication between parallel processes until a commitment is reached. These features are typical examples of the integration between concurrent programming and logic programming in PARLOG and Concurrent Prolog. A program in these two languages is a finite set of guarded clauses: H*-Gj

, G2 . . . . .

G n l B l , B2 . . . . .

Bin.

(n,m ~>0)

where H is the clause head, {G~} is the guard, and {B~} is the body of the clause. Operationally the guard is a test that must be successfully evaluated with the head unification so that the clause could be selected. " l " is called commit operator, and it is used as a conjunction between the guard and the clause body. If the guard is empty (n = 0), the commit operator is omitted. The declarative reading of a guarded clause is: H is true if both {G~} and {B~}are true. According to the process interpretation, to solve H it is necessary to solve the guard {G~}, and if its resolution is sucessfully, B~, B 2 . . . . . Bm are solved in parallel. During the search for a candidate clause by which to solve a goal, a guard evaluation must not be allowed to bind any variables in the goal. If this were allowed, a guard might bind a variable in the goal even if the guard subsequently fails. Partial results that are obtained from head unification and solving the guard, will become available to other subgoals only after the clause commitment. The clause commitment cuts off the alternative clauses. The control mechanisms of Concurrent Prolog and PARLOG are different from those of Prolog. Prolog uses the textual order of clauses in the program and the order of literals within a clause, to perform sequentially the resolution proof. Concurrent Prolog and PARLOG perform in parallel the resolution proof, associating a process to a unit goal and a process network to a conjunctive goal. Furthermore, subgoals in a conjunction can share variables; when this occurs, as mentioned before, variables are used as communication channels between the processes. Non-determinism incoporated into the computation model of these languages is called committed-choice non-determinism or don't care non-determinism, to distinguish it from don't know non-determinism, typical of Prolog. According to don't know non-determinism, Prolog implements backtracking when an alternative fails. That is if a resolution step fails, Prolog backtracks to the last choice point, selects the next clause in the procedure and proceeds with this new clause. When PARLOG and Concurrent Prolog make a choice, according to don't care non-determinism the evaluation commits to that choice, and no backtracking is performed. Thus variable bindings are never retracted. To ensure a correct choice, guards and input matching (i.e. the unification of the input arguments) are utilized. Both languages implement the stream-AND parallelism. According to this type of parallelism, more subgoals can be executed concurrently. They communicate incrementally through the values bounded to their shared variables. Figure 1 shows a simple example that exploits stream-AND parallelism. The sumtree predicate adds to the N variable the node values of the tree

188

DOMENICOTALIA

sumtree(T,N) :-

flatten(T,L), sum(L,N).

Fig. 1. The sumtree clause.

T. For this purpose it uses the fatten predicate that flattens the tree in the list L, and the sum predicate to add the list elements. The declarative reading is: N is the sum of all values on the tree T. Theflatten(T, L) and sum(L, N) literals can be executed in parallel. Thus having that flatten sends incrementally to sum the elements of the list L through bindings to the shared variable (L) and sum adds their values in N. The communication is incremental because the list elements can be used by sum without waiting that the list L is completed. To achieve an efficient implementation of this kind of parallelism it is necessary that each call does not produce more than one solution. In this way, shared variables have the single-assignment property, namely their bindings are never retracted. To implement the single-assignment property it is necessary to establish the direction of communications between two subgoals having shared variables, For this purpose, PARLOG and Concurrent Prolog use two different mechanisms, modes and read-only variables, described in sections 4.2 and 5.2. In addition to AND-stream parallelism, both languages implement another kind of parallelism, the committed-choice OR parallelism. This kind of parallelism implements the parallel evaluation of guards of alternative clauses (i.e. clauses with the same head). Committed-choice OR parallelism is a restricted form of OR parallelism which searches in parallel for a solution. When this is found, the evaluation commits to the selected clause, and the evaluation of other clauses is aborted. Differently from complete OR parallelism where all solutions are searched in parallel and no commit to a selected clause is operated. In this case all or only some clauses may provide a subset of solutions, but the whole search space must be traversed to achieve the full set of solutions. According to the committed-choice OR parallelism, the system tries to unify concurrently the goal with all the clauses with the same head, but among these only one is selected in a non-determinate way. To avoid multiple binding management, caused by parallelism, PARLOG clauses must be safe. A clause in a program is defined to be safe if and only if for any goal, the evaluation of head unification and guard never bind a variable appearing in the goal to a non-variable term. In a PARLOG, program safety is checked at compile time. This simplifies the implementation and improves the program performance. Nevertheless it has been demonstrated that the safety of a program can't be completely verified at compile time. This means that multiple binding management in PARLOG cannot be completely eliminated at compile time. On the other hand, Concurrent Prolog programs can be unsafe. Hence the language run-time support must manage multiple binding environments, thereby having some overhead. From the semantics point of view, PARLOG and Concurrent Prolog are not complete, because they are single-assignment languages. In fact, because of don't care non-determinism, these languages compute only one solution among those that program logically defines. This kind of incompleteness of PARLOG and Concurrent Prolog requires a different style of programming as regards Prolog, but it is not a real problem in the implementation of system-level applications [11, 12]. 4.

PARLOG

The PARLOG language (a PARallel LOGic programming language) [13, 14], can be considered as the immediate descendant of the Relational Language [15]. The main goal of PARLOG is the implementation of the AND-stream parallelism.

A survey of PARLOG and Concurrent Prolog

189

mode search (?,?,3.

seareh(u,[ul xl,x). seareh(u,[vl x],y) <--- u =/= v : seareh(u,x,y).

Fig. 2. The PARLOG search program.

4. I. Syntax A P A R L O G program consists of a finite set of guarded clauses: (head) ,,-- (guard) : (body). where the symbol ":" is the commit operator. In PARLOG, variable names begin with a lower case letter, and the constant names begin with an upper case letter, contrary to what occurs in Prolog. Figure 2 shows a simple P A R L O G program that performs the search for an element in a list, and when the element is reached, the program returns the rest of the list. The second clause contains a guard (u = / = v) used to check if the element is not the first element of the list, because this case is solved by the first clause. P A R L O G defines a sequential operator (&), that is a conjunction operator used to execute sequentially the evaluation of subgoals: Cll &

CI 2 .

Here, the clause C12 is evaluated after that evaluation of Clm is terminated. This operator is utilized both to program with side-effects and to limit the parallelism degree of programs. For clause termination, two terminators are defined, " " and " ; " . The " . " is the parallel search operator and " ; " is the sequential search operator. In Fig. 3 is shown an example of use of these operators. In this example, iff Clt is not a candidate clause, CI 2 and CI 3 a r e solved in parallel. P A R L O G language has not been designed for a particular target architecture, hence the language does not force a particular granularity of parallelism. We can have programs with a coarse-grained parallelism which use operators like "&" and " ; " on architectures using few processors (multiprocessors), and fine-grained programs on architectures with a large number of processors (massively parallel systems). Furthermore, P A R L O G provides some neutral operators, "and" and " . . ". These operators can be interpreted in many ways, namely they can be translated to sequential or parallel operators according to the particular architecture utilized.

Ch ; C12. 03.

Fig. 3. PARLOG operators.

190

Do~mr~lcoTALIA

4.2. Mode declaration P A R L O G establishes access restriction to variables by the predicate mode declaration. This imposes a direction on the communication via shared variables between two subgoals. In PARLOG a mode declaration is defined for each predicate: mode R(tl . . . . . t,).

where ti can be "?" to specify input mode, or "1"" to specify output mode. In the example shown in Fig. 2 the first and the second argument of the search predicate have input modes, while the third argument has output mode. Modes introduce a restriction in the unification of a goal with a clause head. Unification of a term with input mode means that its value can be substituted matching it with values of the call (input matching). A call is suspended if a variable with input mode has not been bounded at call time. It remains suspended until the variable is bounded by another subgoal which shares it, or until another candidate clause is selected. Using a different terminology, we can say that a goal holding a variable with input mode can only be a consumer for that variable, that is during the unification the subgoal it can only receive the value of that variable. A variable with output mode can be unified binding its value to the corresponding term in the call (output matching). Each argument with output mode must be unbound within the call. An error occurs if a variable specified with output mode is bound into the relation call at unification time. Using the terminology of concurrent programming, we can say that a goal holding a variable with output mode can only be a producer for that variable. This means that during unification a value for the variable is produced (instantiated). Program shown in Fig. 1 will be written in P A R L O G as described in Fig. 4. We suppose that flatten and sum are predefined predicates. As you can see, the t argument of predicate sumtree has input mode, i.e. sumtree must receive the value of t (from the query), and it produces (binds) a value for (to) its second argument n. Notice that, in P A R L O G it is not possible to declare a term with a double mode, that is with both input and output mode. Figure 5 shows a P A R L O G program which implements a stack. It uses modes to restrict access to variables. The stack clause has two arguments. The first is an input stream and the second stores the list of stack elements. The program terminates when the end of the input stream is reached and the stack is empty. In section 5.2 a way is presented to implement the same program in Concurrent Prolog.

4.3. Implementations The status of a P A R L O G program evaluation can be represented as a set of cooperating processes. The computation proceeds by selecting a process and attempting to "reduce" it using the program clauses. The reduction can succeed, fail or suspend. The program terminates successfully when the set of processes is empty. When a reduction attempt fails, the program fails. Finally, when there is no process to reduce, but the process set is not empty, the program is said to be deadlocked.

mode flatten (?,^). mode sum (?2). mode sumtree (?2). sumtree(t,n) <-- flatten(t,l), sum(l,n).

Fig. 4. The PARLOG sumtree program.

A survey of PARLOG and Concurrent Prolog

191

mode prog_stack (?). mode stack (?,3. prog_stack(s) ~ stack(s,[]). stack([POP(x) I s], [x l i d ~ stack([PUSH(x) I s], 1) ~

stack(s, 1).

stack(s, [x I1]).

stack([l,[]).

Fig. 5. A stack in PARLOG.

The first implementation of PARLOG has been developed using the Prolog language. The system was composed of a compiler from PARLOG to Prolog, and an interpreter written in Prolog for the simulation of the parallel execution. The performance of this system was not acceptable. Subsequently, a PARLOG compiler using the instruction set of the Warren Abstract Machine was developed. The first real attempt to implement PARLOG on a parallel architecture has been done on the ALICE machine [16, 17], a shared memory multiprocessor system for graph reduction. The implementation of PARLOG on ALICE is based on A N D tree model [14]. This model is an abstract computational model for PARLOG which represents a PARLOG program as a tree of AND processes. In the AND tree model branches represent calls in a conjunction. This model corresponds to the 'term rewrite' mechanism embodied by functional architecture such as ALICE. On this system, a PARLOG program is translated into the KPANDtr~language, which is the machine language of the AND tree model and can be directly implemented on the ALICE architecture. When PARLOG is implemented on a shared-memory parallel architecture, each shared variable is implemented as a location of the memory shared among the processors. Vice versa, when a distributed memory parallel architecture is used, each processor has its own local memory, and no global storage is available. Thus, due to the lack of a common memory space, the binding of variables shared between two subgoals, must be implemented through the communication channels connecting the processors of the parallel architecture. The main problem to be solved in a parallel implementation of the language on a shared-memory multiprocessor is due to the concurrent access of more processors to the global memory space. If appropriate policies are not correctly implemented, the memory bottleneck may abate the system performance. On the other hand, this bottleneck is not present in distributed memory systems. On this kind of system the major problem to be solved is the processor-to-processor communication overhead. If the communication time is excessive with respect to the computation time, no benefits can be achieved from the use of distributed memory parallel systems. Foster in [18] describes a model for the implementation of PARLOG on a distributed memory architecture. It defines an algorithm for the distributed unification that allows the processes located on different processors to communicate using "virtual" shared variables. These shared variables are implemented by a single occurrence stored on one processor, and a set of remote references to this occurrence from the remote processes that use this variable. The implementation model proposed by Foster addresses some typical issues of distributed memory systems, such as distributed computation control, deadlock, and termination detection of processes. A parallel implementation of the language that is based on this model has been developed. CL 1813~D

192

DOMENICOTALIA

At present, a sequential implementation of the language on a Unix environment is available. The system is based on an instruction set named SPM (Sequential PARLOG Machine) designed for the sequential execution of PARLOG. This system is composed of a SPM emulator, a PARLOG compiler, and a query interpreter. These last two components are written in PARLOG. Furthermore, several sequential implementations of PARLOG for personal computers (IBM PC and Macintosh) are commercially available. These implementations have a scheduling strategy that simulates the parallel evaluation of subgoals assigning a time slot to the processes (simulated) that implement the system. Notice that in the parallel implementations of PARLOG the scheduler provides for the mapping of the processes implementing the program, on the processors, in a transparent way for the user. To simplify the implementation of the language, a subset called Flat PARLOG [19] has been defined. In it, the operations performed in a clause guard are reduced to a set of predefined predicates. As a result of this choice, a simple operational model that does not need to maintain a process hierarchy is obtained. For the implementation of Flat PARLOG an abstract machine has been defined. 5. CONCURRENT PROLOG Concurrent Prolog is a process-oriented programming language. It provides mechanisms for the control of parallel search and for the clause synchronization. The principal mechanisms are: • the commit operator (I) to handle non-determinism, • the read-only variables (X?) for subgoal synchronization.

5.1. Syntax A Concurrent Prolog program is defined as a set of guarded clauses p(t,,...,t,)~GIB. where p(t, . . . . . t,) represents the clause head, G represents the guard, and B the clause body. The symbol "1" represents the commit operator. If the guard is empty, the commit operator is omitted. In Concurrent Prolog, constant names begin with a lower case letter, and variable names begin with an upper case letter, like in Prolog.

5.2. Read-only variables The mechanism introduced in Concurrent Prolog to solve conflicts on shared variables arising from the concurrent execution of more subgoals, is the read-only annotation. It can be associated to any variable. A variable with this annotation is called read-only variable. In Concurrent Prolog, a read-only variable is declared adding a question mark to the variable name, such as X?. Unification of a read-only term X? with a term Y is defined as follows. If Y is not a variable, then the unification succeeds only if X is not a variable, and X and Y are recursively unifiable (i.e. their arguments are unifiable). If Y is a variable then the unification of X? and Y succeeds, and the result is X?. Read-only variables are used to implement dataflow synchronization among processes that solve the subgoals. A process that solves a goal containing a read-only variable is called consumer of that variable. A process that solves a goal containing the same variable without annotations is called producer of the variable. If during the unification a consumer process tries "to write" a read-only variable, it suspends until the variable will be bound by the producer process. In other words, the unification of a subgoal with the head of a clause that tries to bound a read-only variable remains suspended until the variable will be bound. For instance, the unification of the term f(X?) with the term f(b) suspends because X is annotated read-only, whereas the unification ofg(X, Y?)with g(c, Z)succeeds obtaining the bindings {X = c, Z = Y?}. Notice that, a formal semantics for read-only variables has been defined in [20]. Figure 6 shows a Concurrent Prolog program that implements a stack. It uses read-only variables to operate its suspension if no message is available.

A survey of PARLOG and Concurrent Prolog

193

(1) stack(S) ~--stack(S?,[]). (2) stack([pop(X) I S], [Xl Xs]) ~-- stack(S?, Xs). (3) stack([push(X) I S], Xs) ~-- stack(S?, [ X l Xs]).

(4) stack([],[]).

Fig. 6. A stack in Concurrent Prolog.

This program implements the pop and push operations on the stack (clauses 2 and 3). Read-only variables are utilized to suspend the program if no operation is required on the stack. For instance, if variable S is unbound, the unifications of stack(S~[ ]) with stack(~op(X) lS], [X IXs]) and stack([push(X) l S], Xs) suspends until S will be bound to pop(X) or push(X). Clause 4 indicates the program termination when there are no operations and the stack is empty. Read-only variables used in Concurrent Prolog are different from mode declarations used in PARLOG. In P A R L O G a term must have input or output mode, not both. In Concurrent Prolog, read-only variable mechanism allows a greater freedom because if a term is not annotated by the question mark ?, it can be "read" and "written" during unification. This means that in Concurrent Prolog, a term without annotation corresponds to a PARLOG term with both input and output mode. This makes programming in Concurrent Prolog more flexible than in PARLOG. On the other hand, P A R L O G is more safe and efficient than Concurrent Prolog since controls are executed at compile time, while in Concurrent Prolog controls are executed at run time [19].

5.3. Incomplete messages An important feature of Concurrent Prolog is represented by incomplete messages, called at first partially determinated messages. An incomplete message is a message that contains one or more unbound variables [21]. It can be viewed and used as: • a message being sent incrementally, • a data structure being constructed cooperatively, and • a message containing a channel as an argument. Figure 7 shows a simple program which implements a monitor which serves read and write operations on a data structure. The monitor uses the mechanism of incomplete messages to serve a read operation. In fact, the monitor serves a read request using the unbound variable Args which it receives in the message read(Args). It is not necessary that the sender of an incomplete message must complete it, this can be done by the receiver. Incomplete message are different from streams, because the process that instantiates the variable can be the receiver, whereas in the stream case this is not possible. This mechanism in P A R L O G is called back communication.

5.4. Implementations The first interpreter of Concurrent Prolog was implemented in 1982. It was a prototype written in Prolog with a poor performance. In an early paper on Concurrent Prolog [5], Shapiro presented the structure of this interpreter composed of concurrent processes. This interpreter is composed of three process types: and-dispatcher, or-dispatcher and unifier. According to this model, the running of a program P is composed at the beginning of a system of Concurrent Prolog processes

194

DOMENICOTALIA

(1) monitor([write(Args) IS], Data) <--- serve(write(Args), Data, NewData), monitor(S?, NewData?).

(2) monitor([read(Args) I S], Data) ¢-- serve(read(Args), Data, ._), monitor(S?, Data). O) monitor([l,__).

Fig. 7. A monitor in Concurrent Prolog.

S. Each process A that belongs to S, attempts to reduce itself using the clauses of the program P. We will describe briefly how the processes of the interpreter work: • The and-dispatcher process working on a system of Concurrent Prolog processes S, forks one or-dispatcher process for each process A in S. Then it waits for a success message from all. When this occurs, it notifies the success to its parent and terminates. • The or-dispatcher process working on a process A, operates as follows: for every clause A i ~ G IB whose head is unifiable with A, it invokes a unifier with A and the clause Ai ~ G IB. Then, the or-dispatcher waits for any of the unifiers to report success. When one success arrives, the or-dispatcher reports success to its parent and-dispatcher and terminates. • The unifier process working on a process A and a clause A~,--GI B, operates as follows. It attempts to unify A with Ai, storing bindings made to non read-only variables in A on its private memory. If the unification is successful, it invokes an and-dispatcher with G and waits for it to report success. Then, the unifier attempts to commit. If successful, it reports success, but in either case it terminates. Notice that, at most one unifier may commit. This because, as mentioned above, Concurrent Prolog implements the don't care non-determinism that computes only one solution. The interpreter is implemented as a process tree with three types of nodes: and-dispatcher, or-dispatcher and unifier. For the implementation of this model of interpreter it is necessary to solve problems like failures, deadlock, termination. In particular, when a unifier process commits, the termination of every brother process must be forced. The first compiler for the Concurrent Prolog language has been developed in Prolog at ICOT (Institute for New Generation Computer Technology). The performance of this compiler was similar to the performance of the Prolog system used to develop it. After these attempts, a subset of the language named Flat Concurrent Prolog (FCP) has been defined to achieve an efficient implementation. The main restriction of Flat Concurrent Prolog is that the clause guards can have a fixed number of simple test predicates [22]. Using FCP has been implemented a multi-tasking single-user environment for Concurrent Prolog named Logix [23]. The Logix system has been developed on a DEC VAX system with Unix 4.2, it is composed of a compiler, a shell, a file-server and a debugger. This system is based on three main concepts: the modules, the computations and the servers. A module is a unit of compilation. A computation is a unit of execution, control and debugging. Finally, a server offers the access to the facilities of the Logix support, e.g. the file-system [24]. Even if the first implementation of Logix has been developed on a sequential architecture, the system has been designed to be implemented on a parallel architecture. In the parallel implementation of Logix, modules are mapped on different processors, and for the cooperation between modules, a remote procedure call mechanism implemented with the FCP constructs is used.

A surveyof PARLOGand Concurrent Prolog

195

Concerning the implementation of FCP on parallel systems, at the Weizmann Institute has been developed an interpreter on the lntel iPSC with 16 processors connected in an hypercube topology [25]. The model defined for this implementation is an extension of the sequential implementation of FCP. It extends the unification algorithm to permit the reading and writing of terms stored on remote processors. The implementation guarantees the existence of only one occurrence of every variable, and a set of remote references to it. A remote reference includes the identifier of the processor and the address of the variable in the processor memory. In this way, a virtual shared memory space visible to all the processes is implemented. This avoids the need to maintain the consistency among the multiple copies of a variable. To prevent processes from writing simultaneously a shared variable, the interpreter implements a locking scheme. According to this scheme, a process can access a variable only after it has asked for and obtained a lock on that variable. The process will release the lock after its operations on the variable are terminated. Using this technique it is possible to have a deadlock of the system when a process maintains a lock on some variable and asks for the lock on other variables. To avoid deadlock, the interpreter provides an algorithm of deadlock prevention based on the priority of the processors of the parallel architecture. This interpreter on the iPSC is the first parallel implementation of the language and its main goal is to produce a first basic prototype to be used for future research. The techniques implemented in the interpreter have been incorporated into a compiler based implementation of FCP emulated on an iPSC/2 [26]. 6. SUMMARY AND CONCLUSIONS This paper discussed two languages, PARLOG and Concurrent Prolog, which represent a valid example of the integration of the logic programming paradigm and parallelism concepts. Even if Concurrent Prolog and PARLOG originate from a common model, and in many aspects are similar, they have some different features. For example, the languages use two different techniques to annotate variables. PARLOG uses mode declaration of predicate arguments, while Concurrent Prolog uses read-only variables. As above mentioned, read-only variables are more flexible than modes. In fact, using modes all data direction flow must be specified. In Concurrent Prolog, instead, not all variables must be annotated, so not all data directions are specified. This makes easier the programmer's task, but requires a more complex run-time support for the language. Furthermore, PARLOG performs checks at compile time, so we can say that this language is compilation oriented. Vice versa, Concurrent Prolog performs most checks at run time. This makes the language suitable for meta-programming, but on the other hand, decreases the performance of its programs. Another difference between the languages concerns the binding environments management. In PARLOG clauses are safe, that is the evaluation of the head and guard of a clause does not instantiate a variable appearing into the goal to a non-variable term. If a PARLOG program is unsafe (i.e. contains at least an unsafe clause) an error is produced at compile time. Thus, having only safe programs only one binding environment is sufficient to store the bindings of variables in a goal. Concurrent Prolog programs can be unsafe, hence the language requires multiple binding environments. For each guard evaluation it is provided a local binding environment, because a variable can be instantiated to different values. During the guard evaluation, variables in the goal are copied into the local environment, and only after the clause commitment they are copied into the global environment. The multiple binding environments require a more complex implementation, and result in lower performance of Concurrent Prolog programs compared to PARLOG programs. Apart from these differences, the languages are based on the same theoretical model. From the concurrent programming point of view they utilize the CSP model, and they contain the main features of it, non-determinism, process activation, and message-passing communications. From the logic programming point of view, they represent a subset of Prolog language, since they are single-solution logic languages.

196

DOUENICO TALIA

Finally, Concurrent Prolog and P A R L O G extend the traditional application areas of logic programming from databases, and artificialintclligcnccapplications to operating systcms, simulation, and system specification [27]. REFERENCES I. Sterling L. S. and Shapiro E. The Art of Prolog, Cambridge, MA: MIT Press; 1986. 2. Kowalski R. Predicate logic as programming language. IFIP Informat. Proc., North-Holland, 569-574; 1974. 3. Takeuchi A. and Furakawa K. Parallel Logic Programming Languages, Proceedings of 3rd International Logic Programming Conference, Springer Verlag, LNCS, 225: 242-254; 1986. 4. Shapiro E. (Ed.) Concurrent Prolog: Collected Papers, Vols 1 and 2. Cambridge, MA: MIT Press; 1988. 5. Shapiro, E. A Subset of Concurrent Prolog and its Interpreter. Technical Report ICOT, Tokyo, Vol TR-003; 1983. 6. Clark K. L. and Gregory S. PARLOG: Parallel Programming in Logic. In Trans. Program. Lang. Systems 8: 1-49; 1986. 7. Ueda K. Guarded Horn Clauses. Technical Report ICOT, Tokyo, Vol TR-103; 1985. 8. Pereira L. M. and Nasr R. Delta-Prolog: a Distributed Logic Programming Language, Proceedings of the International Conference on Fifth Generation Computer Systems, Tokyo, 283-291; 1984. 9. Steer K. G. Specification and Simulation of Parallel System Architectures in PARLOG, MSc Thesis, Dept. of Computing, Imperial College, 87/15; 1987. I0. Hoare C. A. R. Communicating Sequential Processes. Commun. ACM 21: 666-677; 1978. 11. Shapiro E. Systems Programming in Concurrent Prolog. Technical Report ICOT, Tokyo, Vol TR-034; 1984. 12. Ringwood G. A. PARLOG86 and the Dining Logicians. Commun. ACM 31: 10-25; 1988. 13. Clark K. L. PARLOG: the language and its applications. Proceedings of TAPSOFT; 31-53; 1987. 14. Gregory S. Parallel Logic Programming in PARLOG. Addison-Wesley; 1987. 15. Clark K. L.and Gregory S. A Relational Language for Parallel Programming. Conference on Functional Programming Languages and Computer Architecture, 171-178; 198 I. 16. Darlington J. and Reeve M. J. 'ALICE: a Multi-Processor Reduction Machine for the Parallel Evaluation of Applicative Languages', Proceedings of the ACM/MIT Conference on Functional Language and Computer Architecture: 65-75; 1981. 17. Lam M. and Gregory S., PARLOG and ALICE a Marriage of Convenience, Proceedings of 4th International Logic Programming Conference, Melbourne: 294-310; 1987. 18. Foster I., Parallel Implementation of PARLOG, Proceedings of 1988 International Conference on Parallel Processing: Vol. II, 9-16; 1988. 19. Foster I. and Taylor S. Flat PARLOG: A Basis for Comparison, Int. J. Parallel Prog. 16: 87-125; 1987. 20. Levi G. and Palamidessi C. The Declarative Semantics of Logical Read-only Variables, Proceedings of 1985Symposium on Logic Programming, 128-137; 1985. 21. Shapiro E. Concurrent Prolog: a progress report. IEEE Comput. 19: 44-58; 1986. 22. Houri A. and Shapiro E. A sequential abstract machine for flat Concurrent Prolog. Technical Report, Weizmann Institute, Rehovot, Israel: Vol CS86-20; 1986. 23. Silverman W., Hirsch M., Houri A. and Shapiro E. The Logix System User Manual, Version 1.2, Technical Report, Weizmann Institute, Rehovot, Israel, Vol CS86-21 (1986). 24. Hirsch M., Silverman W. and Shapiro E., 'Layers of Protection and Control in the Logix System'. Technical Report, Weizmann Institute, Rehovot, Israel: Vol CS86-19; 1986. 25. Taylor S., Safra S. and Shapiro E. A Parallel Implementation of Flat Concurrent Prolog. Int. J. Parallel Progr. 15: 245-275; 1987. 26. Taylor S. Parallel Logic Programming Techniques. Prentice-Hall; 1989. 27. Shapiro E. The family of Concurrent Logic Programming languages. ACM Comput. Surv. 21: 412-510; September 1989. About the Author--DOM~-NICOTAUA studied Physics at University of Calabria. In 1982 he was awarded at CRAI a FORMEZ fellowship for 3 years. He has been working in the area of paraUel architecture and distributed systems since 1984 and is a senior researcher fellow of the CRAI parallel computing team. Currently member of the CRAI team in the CNR-PFI research project. His interests are on distributed systems, parallel architectures, and concurrent programming languages. He published more than 30 scientific papers. He is a member of the Association for Computing Machinery, the IEEE Computer Society and AICA.