A Low-Level Index for Distributed Logic Programming
FF. Ricca, A. Russo et al. (Eds.): Proc. 36th International Conferenceon Logic Programming (Technical Communications) 2020 (ICLP 2020)EPTCS 325, 2020, pp. 303–312, doi:10.4204/EPTCS.325.40 c (cid:13)
Thomas ProkoschThis work is licensed under theCreative Commons Attribution License.
A Low-Level Index for Distributed Logic Programming
Thomas Prokosch
Institute for Informatics, Ludwig-Maximilian University of Munich, Germany [email protected]
A distributed logic programming language with support for meta-programming and stream process-ing offers a variety of interesting research problems, such as: How can a versatile and stable datastructure for the indexing of a large number of expressions be implemented with simple low-leveldata structures? Can low-level programming help to reduce the number of occur checks in Robin-son’s unification algorithm? This article gives the answers.
Logic programming originated in the 1970s as a result on work in artificial intelligence and automatedtheorem proving [15, 21]. One important concept of logic programming always stood out: The clearseparation between the logic component and the control component of a program [22]. In today’s com-puting landscape, where large amounts of (possibly streamed) data and distributed systems with parallelprocessors are the norm, it becomes increasingly hard to program in an imperative style where logic andcontrol are intermingled.Therefore, it is worthwhile to investigate how a logic programming language could deal with largeamounts of streamed and non-streamed data in a way such that it can adapt itself to changing circum-stances such as network outages (“meta-programming”). Creating such a programming language is theprimary drive behind the author’s line of research.The main components of a distributed logic programming language are • a stable indexing data structure to store large amounts of expressions, • a low-level unification algorithm with almost linear performance, and • a distributed forward-chaining resolution-based inference engine.Some of these components have already been investigated; the current status of the research is sum-marized in this article. The missing parts are outlined in section 6. This section introduces standard algebraic terminology and is based on [31, 30].Let v , v , v , . . . denote infinitely many variables, letters a , b , c , . . . (except v ) denote finitely manynon-variable symbols. v i (with a superscript) denotes an arbitrary variable.An expression is either a first-order term or a first-order formula. Expressions are defined as follows:A variable is an expression. A nullary expression constructor c consisting of the single non-variablesymbol c is an expression. If e , . . . , e n are expressions then c ( e , . . . , e n ) is an expression with expressionconstructor c and arity n .The fusion of the two distinct entities term and formula may seem unusual at first glance. Thisperspective, however, is convenient for meta-programming: Meta-programming is concerned with the04 A Low-Level Index for Distributed Logic Programming generation and/or modification of program code through program code. Thus, meta-programming ap-plied to logic programming may require the modification of formulas through functions which may bedifficult to achieve when there is a strict distinction between terms and formulas. Commonly, a so-called quotation is used to maintain such a distinction when it is necessary to allow formulas to occur inside ofterms. However, it was shown [4, 18, 20] that it is not necessary to preserve such a distinction and thatby removing it, the resulting language is a conservative extension of first-order logic [2].Let E denote the set of expressions and V the set of variables. A substitution σ is a total function V → E of the form σ = { v (cid:55)→ e , . . . , v n (cid:55)→ e n } , n ≥ v , . . . , v n are pairwise distinct variables,and ∀ i ∈ { , . . . , n } σ ( v i ) = e i , and σ ( v ) = v if v (cid:54) = v i . A substitution σ is a renaming substitution iff σ isa permutation of variables, that is { v i | ≤ i ≤ n } = { e i | ≤ i ≤ n } . σ is a renaming substitution for anexpression e iff { e i | ≤ i ≤ n } ⊆ V and for all distinct variables v j , v k in e the inequality σ ( v j ) (cid:54) = σ ( v k ) holds.The application of a substitution σ to an expression e , written σ ( e ) , is defined as the usual functionapplication, i.e. all variables v i in e are simultaneously substituted with expressions σ ( v i ) . The applica-tion of a substitution σ to a substitution τ , written σ τ , is defined as ( σ τ ) x = σ ( τ ( x )) . One of the most important aspects in designing efficient algorithms is finding a good in-memory repre-sentation of the key data structures. The in-memory representation of variables, expressions, and substi-tutions described in this section has already been published in [31, 30] and is based on the prefix notationof expressions. The prefix notation is a flat representation without parentheses; the lack of parenthesesmakes this representation especially suited for the flat memory address space of common hardware. Forexample, the prefix notation of the expression f ( a , v , g ( b ) , v , v ) is f / a / v g / b / v v .A similar but distinct expression representation is the flatterm representation [5, 6]. An expression representation that is particularly suitable for a run-time system of a logic programminglanguage is as follows: Each expression constructor is stored as a compound of its symbol s and its arity n . Each variable either stores the special value nil if the variable is unbound or a pointer if the variable isbound. It is worth stressing that the name of a variable is irrelevant since its memory address is sufficientto uniquely identify a variable. Two distinct expression representations do not share variables.In order to be able to represent non-linear expressions, i.e. expressions in which a variable occursmore than once, two kinds of variables need to be distinguished: Non-offset variables and offset vari-ables. The first occurrence of a variable is always a non-offset variable, represented as described above.All following occurrences of this variable are offset variables and are represented by a pointer to thememory address of the variable’s first occurrence. Care must be taken when setting the value of an offsetvariable: Not the memory cell of the offset variable is modified but the memory cell of the base variableit refers to.The type of the memory cell (i.e. expression constructor cons , non-offset variable novar , or offsetvariable ofvar ) is stored as a three-valued flag at the beginning of the memory cell. Assuming that amemory cell has a size of 4 bytes, a faithful representation of the expression f ( a , v , g ( b ) , v , v ) startingat memory address 0 is: homas Prokosch c on s f / c on s a / nov a r nil c on s g / c on s b / nov a r nil o f v a r An elementary substitution { v i (cid:55)→ e } is represented as a tuple of two memory addresses, the addressof the variable v i and the address of the first memory cell of the expression representation of e . Asubstitution is represented as a list of tuples of addresses. Assume the representation of the expression f ( a , v , g ( b ) , v , v ) starts at address 0 and the representation of the expression h ( a , v ) at address 36,then the substitution { v (cid:55)→ h ( a , v ) } is represented as the tuple ( , ) : f / a / g / b / . . . h / a / nil Substitution application simply consists of setting the contents of the memory cell of the variableto the address of the expression representation to be substituted. After the substitution application f ( a , v , g ( b ) , v , v ) { v (cid:55)→ h ( a , v ) } memory contents is: f / a / g / b / . . . h / a / nil Observe that the contents of the offset variable at address 24 keeps its offset 4 unchanged, and thatthe contents of the non-offset variable at address 20 contains an absolute address.
Automated reasoning [34] relies upon the efficient storage and retrieval of expressions. Standard datastructures such as lists or hash tables can be used for this task but more specialized data structures,known as term indexing [14, 36] data structures, can significantly improve the retrieval speed of expres-sions [6, 36, 35]. Depending on the application, certain characteristics of a term indexing data structureare beneficial. For meta-programming [2] the retrieval of expressions unifiable with a query as well asretrieval of instances, generalizations, and variants of a query are desirable. For tabling [39, 32, 19],a form of memoing used in logic programming, the retrieval of variants and generalizations of queriesneeds to be well supported. In this section, which is based upon already published work [30], instancetries are proposed. Instance tries are trees which offer a few conspicuous properties such as: • Stability. Instance tries are stable in the sense that the order of insertions into and removals fromthe data structure does not determine its shape. • Versatility. Instance tries support the retrieval of generalizations, variants, and instances of a queryas well as those expressions unifiable with a query.06
A Low-Level Index for Distributed Logic Programming • Incrementality. Instance tries are based upon the instance relationship which allows for incremen-tal unification during expression retrieval. • Perfect filtering. Some term indexing data structures require that the results of a query are post-processed [14]. Instance tries do not require such post-processing because querying an instancetrie always returns perfect results.
Term indexing data structures are surveyed in the book [14] and in the book chapter [36] the lattercontaining some additional data structures which did not exist when the former was written. The latterdoes not describe dated term indexing data structures.Tries were invented in 1959 for information re trie val [1] while the name itself was coined one yearlater [9]. Tries exhibit a more conservative memory usage than lists or hash tables due to the fact thatcommon word prefixes are shared and thus stored only once.Coordinate indexing [16] and path indexing [38] consider positions (or sequences of positions, re-spectively, so-called paths ) of symbols in a term with the goal of subdividing the set of terms into subsets.Both coordinate indexing and path indexing disregard variables in order to further lower memory con-sumption making them non-perfect filters: Subject to these limitations, terms f ( v , v ) and f ( v , v ) areconsidered to be equal which means that results returned from a query need to be post-processed to iden-tify the false positives. Several variations of path indexing, such as Dynamic Path Indexing [23] andExtended Path Indexing [12] have been proposed, none of which are stable or perfect filters.Discrimination trees [25, 26] (with their variants Deterministic Discrimination Trees [11] and Adap-tive Discrimination Trees [37]) were proposed as the first tree data structures particularly designed forterm storage. However, all of them are non-perfect filters, a shortcoming that Abstraction Trees [27] wereable to remedy. Substitution Trees [13] and Downward Substitution Trees [17] further refine the idea ofabstraction trees and have been recently extended to also support indexing of higher order terms [29].While Code Trees [40] and Coded Context Trees [10] are also frequently used in automated theoremprovers both data structures are not versatile according to the characterization above. A total order on expressions ≤ e is lexicographically derived from the total order ≤ c : • v < c v for variables v , v with an order ≤ v such that v < v v , • v < c s / a for variable v and non-variable symbol s with arity a , • s / a < c s / a for non-variable symbol s and a < a , • s / a < c s / a for non-variable symbols s , s with an order ≤ nv such that s < nv s . A versatile term indexing data structure needs to support the retrieval of expressions that are more generalthan, a variant of, an instance of, or unifiable with a query. For the construction and querying of instancetries, however, mutually exclusive definitions are required. Expression e is a variant (VR) of expression e iff there exists a renaming substitution ρ for e such that e ρ = e . e is strictly more general (SG) than e and e is a strict instance (SI) of e iff σ is a non-renaming substitution for e such that e σ = e . e and e are only unifiable (OU) iff ∃ σ ( e σ = e σ ) , σ is most general, and σ is not a renaming substitution for homas Prokosch e and e . e and e are non-unifiable (NU) iff ∀ σ ( e σ (cid:54) = e σ ) . A matching-unification algorithmthat is able to determine the mode for two expressions is given in Section 5. An instance trie is a tree T such that • Every node of T except the root stores an expression. • Every child N c of a node N in T is a strict instance of N . • Siblings N s in T are ordered by < e as described above.Figure 1 shows an example of an instance trie storing six expressions. Figure 2 shows the same trieusing substitutions; this alternate representation is possible due to the strict instance relation between anode and its children. In this representation, repeated application of substitutions to the root variable v along a path yields the corresponding expression. The use of substitutions gives two advantages: First,common symbols are shared further reducing memory consumption. Second, querying an instance triecan make use of incremental unification resulting in faster retrieval. Retrieving expressions from an instance trie T requires the following steps:1. Top-down left-to-right traversal of the tree.2. Expression e of each node N is unified with the query q to determine the matching and unificationmode of e and q as outlined above.All query modes except unification affect the traversal: • Variant: The traversal can be interrupted as soon as an answer is found. • Instance: If e is an instance of q then the sub-tree rooted at N does not need to be traversed: Allchildren of N necessarily store instances of q . • Generalization: The traversal can ignore all child nodes of a node which is not strictly more generalthan q . Insertion of an expression e into an instance trie T involves first searching for a node N such that theexpression of N is more general than e and the expressions of all children of N are not more general than e . If e is a variant of the expression in N then nothing is done. Otherwise, a new node N (cid:48) containingexpression e is inserted as a child of N (at the correct position among its siblings according to the order < e defined above) and instances of e (found to the right of N (cid:48) ) are inserted below N (cid:48) . Deletion of an expression e from an instance trie T requires retrieving the node N containing expres-sion e . If such a node is found then this node N is deleted and, after this deletion, each child node of N is inserted into the node which, before the deletion, was the parent node of N .08 A Low-Level Index for Distributed Logic Programming
Unification, that is determining whether a pair of expressions has a most general unifier (MGU), is anintegral part of every automated reasoning system and every logic programming language. Nevertheless,only little attention has been given to potential improvements which develop their full effect at machinelevel or in an interpreter run-time. This section, based on previously published work [31], outlines aunification algorithm which has been specifically developed for such an environment.
Since Robinson introduced unification [33], a wealth of research has been carried out on this subject [28,3, 24, 8]. Nevertheless, only few algorithms are used in practice not only because more sophisticatedalgorithms are harder to implement but also, unexpectedly, Robinson’s unification algorithm is still themost efficient [17]! Consequently, Robinson’s unification algorithm has been chosen as a starting pointfor the following unification algorithm.
The algorithm unif(e1, e2) performs a left-to-right traversal of the representation of expressions e and e whose first addresses are e1 and e2 , respectively. Let c , c1 , c2 be addresses of memory cells inthe representation of e or e . In each step of the algorithm two memory cells are compared based ontype and content using the following functions: • type(c) : Returns the type of the value stored at c , resulting in cons , novar , or ofvar . • value(c) : Value stored in memory cell c . • arity(c) : Arity of the constructor stored in memory cell c , or 0. • deref(c, S) : Creates a new expression from the expression representation at c and applies sub-stitution S to it. • occurs-in(c1, c2) : Checks whether variable at c1 occurs in expression at c2 .The algorithm sets and uses the following variables: A (short for “answer”) is initialized with VR andcontains VR , SG , SI , OU , or NU . Variables R1 and R2 , both initialized with 1, contain the number ofremaining memory cells to read. The algorithm terminates if R1 = 0 and
R2 = 0 . S1 and S2 containsubstitutions for variables in the expression representations of e , e and are initialized with the emptylists S1 := [] , S2 := [] .In each step of the algorithm two memory cells c1 and c2 are compared (starting with e1 and e2 ,respectively), with the following four possibilities for each cell, resulting in a total of 16 cases: type(ei)= cons , type(ei) = novar , type(ei) = ofvar && deref(ei, Si) != nil , and type(ei) =ofvar && deref(ei, Si) = nil .Table 1 shows the core of the matching-unification algorithm. For clarity and because the table issymmetric along its principal diagonal only the top-right half of the table contains entries. For spacereasons, the table is abbreviated; for the full table refer to [31]. An example should illustrate how the matching-unification algorithm works: Expression f ( v , v ) ataddress e1=0 shall be unified with expression f ( a , a ) at address e2=20 : homas Prokosch cons novar ofvar && deref!=nil ofvar && deref=nilcons continue or NU bind, dereference occurs check change mode recursive call (bind and OU) or NU novar bind to left bind, change mode bind, change mode ofvar && dereference occurs check deref!=nil recursive call (bind and OU) or NU ofvar && bind deref =nil Table 1: Core of the matching-unification algorithm, abbreviated f / . . . f / a / a /
01. Initialization:
A := VR; R1 := 1; R2 := 1; S1 := []; S2 := [] type(e1) = cons, type(e2) = cons, value(e1) = value(e2) Constructors f / f / R1 := R1+2 = 3; R2 := R2+2 = 3
3. Continue to next cell: Each cell consists of 4 bytes. e1 := e1+4 = 4; e2 := e2+4 = 24; R1 := R1-1 = 2; R2 := R2-1 = 2 type(e1) = novar, type(e2) = cons First, the non-offset variable at address e1=4 needs to be bound to the sub-expression startingwith the constructor a / e2=24 by adding the tuple ( , ) to the substitution S1 :=[(4, 24)] . Note that no occurs check is required when introducing this binding; this is a speedimprovement with respect to some other unification algorithms such as Robinson’s algorithm [33]or the algorithm from Martelli-Montanari [24].Then, change the mode by setting
A:=SG since the non-offset variable at e1=4 is strictly moregeneral than the expression constructor a / e2=24 .5. Continue to next cell: Each cell consists of 4 bytes. e1 := e1+4 = 8 ; e2 := e2+4 = 28; R1 := R1-1 = 1; R2 := R2-1 = 1 type(e1) = ofvar, type(e2) = cons First, dereference e1 with S1 yielding address 24. Dereferencing address e2=28 yields 28. (Thememory cell at address 28 contains a constructor.) Then, call the algorithm recursively with ad-dresses 24 and 28.7. The recursive call confirms the equality of the expression constructors a / a / A , R1 , R2 .8. Continue to next cell: Each cell consists of 4 bytes. e1 := e1+4 = 12 ; e2 := e2+4 = 32; R1 := R1-1 = 0; R2 := R2-1 = 0 The algorithm terminates because
R1 = 0 and
R2 = 0 . The result
A = SG and
S1 = [(4, 24)] , S2 = [] is returned to the caller. The result is correct since f ( v , v ) is strictly more general than f ( a , a ) .10 A Low-Level Index for Distributed Logic Programming
While some progress towards a distributed logic programming language has already been made, thereare still further challenges: • Instance tries have been fully specified and their implementation is currently ongoing. Upon com-pletion an empirical evaluation of instance tries together with a variety of common term indexingdata structures need to verify the expected speed-up of instance tries. • It is currently being investigated how stream processing can be integrated with logic programming. • The forward-chaining resolution engine to derive the immediate consequences from a set of ex-pressions in order to perform program evaluation has not been investigated so far.Follow-up articles will report on each of those aspects of research.
References [1] Ren´e de la Briandais (1959):
File searching using variable length keys . In:
Proceedings of the Western JointComputer Conference , pp. 295–298, doi:10.1145/1457838.1457895.[2] Fran¸cois Bry (2020):
In Praise of Impredicativity: A Contribution to the Formalizationof Meta-Programming . Theory and Practice of Logic Programming https://pms.ifi.lmu.de/publications/PMS-FB/PMS-FB-2018-2/PMS-FB-2018-2-paper-second-revision.pdf .[3] Dennis de Champeaux (1986):
About the Paterson-Wegman Linear Unification Algorithm . Journal of Com-puter and System Sciences
HILOG: A Foundation for Higher-Order LogicProgramming . Journal of Logic Programming
Fast Knuth-Bendix Completion: Summary . In Nachum Dershowitz, editor:
RewritingTechniques and Applications, 3rd International Conference (RTA’89) , LNCS
Flatterms, Discrimination Nets, and Fast Term Rewriting . Journal of AutomatedReasoning
The Semantics of Predicate Logic as a ProgrammingLanguage . Journal of the ACM
A Practically Efficient and Almost Linear UnificationAlgorithm . Artificial Intelligence
Trie Memory . Communications of the ACM
Fast Term Indexing with Coded Context Trees . Journal of Automated Reasoning
Left-to-Right Tree Pattern Matching . In Ronald V. Book, editor:
Rewriting Techniquesand Applications, 4th International Conference (RTA’91) , LNCS
Extended Path-Indexing . In Alan Bundy, editor: , LNCS
Substitution Tree Indexing . In Jieh Hsiang, editor: , LNCS homas Prokosch [14] Peter Graf (1995):
Term Indexing . LNCS
Application of Theorem Proving to Problem Solving . In Donald E. Walker &Lewis M. Norton, editors:
Proceedings of the 1st International Joint Conference on Artificial Intelligence ,William Kaufmann, pp. 219–240. Available at http://ijcai.org/Proceedings/69/Papers/023.pdf .[16] Carl Hewitt (1972):
Description and Theoretical Analysis (Using Schemata) of Planner . Ph.D. thesis, Ar-tificial Intelligence Laboratory, Massachusetts Institute of Technology. Available at http://hdl.handle.net/1721.1/6916 .[17] Kryˇstof Hoder & Andrei Voronkov (2009):
Comparing Unification Algorithms in First-Order Theorem Prov-ing . In B¨arbel Mertsching, Marcus Hund & Muhammad Zaheer Aziz, editors:
KI 2009: Advances in Arti-ficial Intelligence (KI’09) , LNCS
Ambivalent Logic as the Semantic Basis of Metalogic Programming . In Pascal VanHentenryck, editor:
Logic Programming, Proceedings of the Eleventh International Conference , MIT Press,Santa Marherita Ligure, Italy, pp. 387–401.[19] Ernie Johnson, C. R. Ramakrishnan, I. V. Ramakrishnan & Prasad Rao (1999):
A Space Efficient Enginefor Subsumption-Based Tabled Evaluation of Logic Programs . In Aart Middeldorp & Taisuke Sato, editors:
Functional and Logic Programming, 4th Fuji International Symposium (FLOPS’99) , LNCS
Meta-Logics and Logic Programming , chapter A Vademecumof Ambivalent Logic, pp. 27–56. Computation and Complexity Theory, MIT Press. Available at .[21] Robert A. Kowalski (1974):
Predicate Logic as Programming Language . In Jack L. Rosenfeld, editor:
Information Processing, Proceedings of the 6th IFIP Congress , North-Holland, pp. 569–574. Available at .[22] Robert A. Kowalski (1979):
Algorithm = Logic + Control . Communication of the ACM
SETHEO: A high-performancetheorem prover . Journal of Automated Reasoning
An Efficient Unification Algorithm . ACM Transaction on Pro-gramming Language Systems (TOPLAS’82)
An indexing mechanism for finding more general formulas . Association for Auto-mated Reasoning Newsletter
Experiments with Discrimination-Tree Indexing and Path Indexing for Term Re-trieval . Journal of Automated Reasoning
9, pp. 147–167, doi:10.1007/BF00245458.[27] Hans J¨urgen Ohlbach (1990):
Abstraction Tree Indexing for Terms . In: , pp. 479–484.[28] Michael Stewart Paterson & M.N. Wegman (1978):
Linear Unification . Journal of Computer and SystemSciences
Higher-Order Term Indexing Using Substitution Trees . ACM Transactions on Com-putational Logic (TOCL)
Give Reasoning a Trie . In: , CEUR Workshop Proceedings, Aachen. To appear.[31] Thomas Prokosch & Fran¸cois Bry (2020):
Unification on the Run . In Temur Kutsia & Andrew M. Marshall,editors:
The 34th International Workshop on Unification (UNIF’20) , RISC Report Series . A Low-Level Index for Distributed Logic Programming [32] I. V. Ramakrishnan, Prasad Rao, Konstantinos Sagonas, Terrance Swift & David Scott Warren (1999):
Ef-ficient Access Mechanisms for Tabled Logic Programs . Journal of Logic Programming
A Machine-Oriented Logic Based on the Resolution Principle . Journal of theACM
Handbook of Automated Reasoning . ElsevierScience Publishers.[35] Stephan Schulz & Adam Pease (2020):
Teaching Automated Theorem Proving by Example: PyRes 1.2 . InNicolas Peltier & Viorica Sofronie-Stokkermans, editors:
Automated Reasoning - 10th International JointConference (IJCAR’20), Proceedings, Part II , LNCS
Handbook of Automated Reasoning , chapter TermIndexing, pp. 1853–1964. 2, Elsevier Science Publishers, doi:10.1016/B978-044450813-3/50028-X. Avail-able at .[37] R. C. Sekar, R. Ramesh & I. V. Ramakrishnan (1992):
Adaptive Pattern Matching . In Werner Kuich, editor:
Automata, Languages and Programming, 19th International Colloquium (ICALP’92) , LNCS
The Path-Indexing Method For Indexing Terms . Technical Note 473, SRI Interna-tional, Menlo Park, California, USA. Available at .[39] Hisao Tamaki & Taisuke Sato (1986):
OLD Resolution with Tabulation . In Ehud Shapiro, editor:
ThirdInternational Conference on Logic Programming (LP’86) , LNCS