=Paper=
{{Paper
|id=Vol-1195/long3
|storemode=property
|title=JTabWb: a Java Framework for Implementing Terminating Sequent and Tableau Calculi
|pdfUrl=https://ceur-ws.org/Vol-1195/long3.pdf
|volume=Vol-1195
|dblpUrl=https://dblp.org/rec/conf/cilc/FerrariFF14
}}
==JTabWb: a Java Framework for Implementing Terminating Sequent and Tableau Calculi==
JTabWb: a Java framework for implementing terminating sequent and tableau calculi Mauro Ferrari1 , Camillo Fiorentini2 , Guido Fiorino3 1 DiSTA, Univ. degli Studi dell’Insubria, Via Mazzini, 5, 21100, Varese, Italy 2 DI, Univ. degli Studi di Milano, Via Comelico, 39, 20135 Milano, Italy 3 DISCO, Univ. degli Studi di Milano-Bicocca, Viale Sarca, 336, 20126, Milano, Italy Abstract. JTabWb is a Java framework for developing provers based on terminating sequent or tableau calculi. It provides a generic engine which performs proof-search driven by a user-defined specification. The user is required to define the components of a prover by implementing suitable Java interfaces. The implemented provers can be used as stan- dalone applications or embedded in other Java applications. The frame- work also supports proof-trace generation, LATEX rendering of proofs and counter-model generation. 1 Introduction JTabWb is a Java framework for developing provers based on terminating se- quent or tableau calculi. The project originated as a tool for experimenting and comparing on the same ground di↵erent calculi and proof-search strategies for intuitionistic propositional logic. Now it is an advanced general framework which can be used to implement di↵erent logics and calculi. It can be used either to implement provers as stand-alone Java applications or as APIs to be integrated in other Java applications. Di↵erently from other frameworks like [1, 4, 5], in JTabWb the user can specify all the components of a prover including: formu- las, proof-search nodes, rules and strategies. This allows one to easily implement provers for di↵erent logics and di↵erent calculi (sequent-style or tableau-style calculi). Its main limitation is that all the components are provided as Java classes, hence the user is expected to be experienced with the language. The object oriented nature of the target language and the compositionality of the framework supports the reuse of the components of a prover. This allows one to easily develop di↵erent variants of a prover, so to compare di↵erent implemen- tations of data structures (formulas, sequents,. . . ) and di↵erent strategies. The framework also provides support for generation of proof-traces (histories of proof- search), LATEX rendering of proofs and counter-model generation. JTabWb and some provers for intuitionistic propositional logic implemented in it are available at http://www.dista.uninsubria.it/~ferram. 46 M. Ferrari, C. Fiorentini, G. Fiorino. JTabWb Concept Interface Main methods Formula AbstractFormula String format() Node-set AbstractNodeSet String format() Strategy Strategy AbstractRule nextRule( AbstractNodeSet, IterationInfo) Prover Prover Strategy getStrategy() Engine Engine Engine( Prover, AbstractNodeSet) ProofSearchResult searchProof() Rules supertype AbstractRule String name() Regular rule RegularRule int numberOfConclusions() AbstractFormula mainFormula() R 1| . . . | n Iterator< AbstractNodeSet> iterator() Clash-detection rule ClashDetectionRule ProofSearchResult checkStatus() A function associating with AbstractNodeSet premise() any node-set a value in {SUCCESS, FAILURE} Branch-exists rule BranchExistsRule int numberOfBranchExistsConclusions() AbstractFormula mainFormula() R 1... n Iterator< AbstractNodeSet> iterator() Meta-backtrack rule MetaBacktrackRule AbstractNodeSet premise() A function associating with int totalNumberOfRules() a node-set an enumeration of Iterator< AbstractRule> iterator() rule instances Fig. 1. Basic concepts and their implementation 2 Basic notions and their implementation JTabWb provides a generic engine that searches for a proof of a goal driven by a user-defined prover. In particular the engine searches for a proof of the goal visiting the proof-search space in a depth-first fashion; at any step of the search, the engine asks to the strategy component of the prover the next rule to apply. The user defines the prover by implementing the interfaces modeling the logical components of the proof-search procedure in Fig. 1. For every component we indicate the corresponding interface and its main methods. Formulas are the basic elements of the formal system at hand; one can define formulas of any kind, e.g., propositional, first-order or modal formulas, but also “formulas with a sign” or “labelled formulas”. The data structure storing formu- las during proof-search is modeled by a node-set. E.g., in the case of a sequent calculus, node-sets represent sequents [ ) ] where and are finite sets or multisets of formulas. Formulas and node-sets only require the implementation of a method format(), which is invoked by the engine to print detailed information about the proof-search process. JTabWb models four kinds of rules: regular, clash-detection, branch-exists and meta-backtrack. Regular and clash-detection rules directly correspond to 47 M. Ferrari, C. Fiorentini, G. Fiorino. JTabWb the rules of a calculus; branch-exists and meta-backtrack rules are meta-rules to encode the proof-search strategy. All the rules have AbstractRule as a common supertype. Regular rules directly correspond to the usual formalization of rules in tableau calculi. A regular rule has the form displayed in Fig. 1: R is the name of the rule, is the premise of R and 1 , . . . , n (n 1) its conclusions. We use a double line to represent rules of the framework so to distinguish them from the rules of the sequent calculus we use in the examples. A rule Rs of a sequent calculus can be mapped to a regular rule R writing Rs bottom-up (the conclusion of Rs becomes the premise of R). As an example, let us consider the rule for left disjunction of G3i [6] [A, ) H] [B, ) H] _L [A _ B, ) H] This rule is represented in our framework by the regular rule: [A _ B, ) H] _L [A, ) H] | [B, ) H] The main formula of a regular rule is the formula put in evidence in the premise of the rule (e.g., A _ B in the above example). An instance of a regular rule is an object implementing the RegularRule interface. Conclusions of a regular rule are returned as an enumeration of objects of type AbstractNodeSet. To represent an enumeration of objects we use the Java Iterator interface: it defines the method next() to get the next element in the enumeration and the method hasNext() to check whether the enumeration contains more elements or not. Clash-detection rules model rules without conclusions corresponding to the end-points of a derivation (closure rules of tableau calculi and axiom rules of sequent calculi). We represent such a rule by a function CD that, given a node- set , returns SUCCESS if is an end-point of the derivation, FAILURE otherwise. As an example, the axiom rules of G3i Ax L? [A, ) A] [?, ) H] correspond to the following clash-detection rule: SUCCESS if ? 2 or A 2 CD([ ) A]) = FAILURE otherwise An instance of a clash-detection rule is an object that implements the interface ClashDetectionRule providing the checkStatus() method encoding the correspond- ing CD function. The distinction between invertible and non-invertible 1 rules has a crucial role in the definition of a proof-search procedure, since non-invertible rules introduce backtrack points in proof-search. E.g., the rules of G3i for right disjunction 1 We adopt the formalization of invertible rule of [6]. 48 M. Ferrari, C. Fiorentini, G. Fiorino. JTabWb public class Rule_Right_Or implements _BranchExistsRule { private Sequent premise ; private Formula or_formula ; public Rule_Right_Or ( Sequent sequent , Formula or_formula ) { this . premise = sequent ; this . or_formula = or_formula ; } ... public Iterator < _AbstractNodeSet > iterator () { LinkedList < Sequent > list = new LinkedList < Sequent >(); Formula [] disjuncts = or_formula . getSubformulas (); for ( int i =0 ; i < disjuncts . length ; i ++){ Sequent conclusion = premise . clone (); conclusion . removeRight ( or_formula ); conclusion . addRight ( disjuncts [ i ]); list . add ( conclusion ); } return list . iterator (); } } Fig. 2. Implementation of the rule _Ri of G3i [ ) Ai ] _Ri i 2 {1, 2} [ ) A1 _ A2 ] are not invertible. Indeed, it could be the case that [ ) A2 ] is provable while [ ) A1 ] is not. Hence, searching for a proof of [ ) A1 _ A2 ], we have to try both the rules; if the construction of a proof for [ ) A1 ] fails, we have to reconsider the premise (backtrack ) and try the other way. The two rules above can be formalized in our framework by means of a branch-exists rule. A branch- exists rule R with premise and conclusions 1 , . . . , n (n 1) means that is provable i↵ at least one of the i is provable. An instance of a branch- exists rule is an object implementing the BranchExistsRule interface. The iterator method returns the conclusions of the rule as an enumeration of objects of type AbstracNodeSet. As an example, in Fig. 2 we describe an implementation of the rules _Ri of G3i. A calculus C is a finite set of regular rules, clash-detection rules and branch- exists rules. A C-tree ⇡ is a tree of node-sets such that, if is a node of ⇡ with 1 , . . . , n as children, then either there exists a regular-rule of C having as premise and 1 , . . . , n as conclusions, or n = 1 and there exists a branch-exists rule of C having as premise and 1 as one of its conclusions. A C-proof is a C-tree ⇡ such that, for every leaf of ⇡, there exists a clash-detection rule CD of C such that CD( ) = SUCCESS. To define proof-search strategies we need to encode another kind of back- tracking arising from the application of non-invertible rules. Let us consider the non-invertible rule ! L of G3i 49 M. Ferrari, C. Fiorentini, G. Fiorino. JTabWb [A ! B, ) A] [B, ) H] !L [A ! B, ) H] If we are searching for a proof of a sequent containing more than one impli- cation in the left-hand side, we must try all the possible instances of the rule ! L to assert the provability status of . To express this situation we use a meta-backtrack rule, which is a function MB associating with a node-set an enumeration of rule instances having as premise (the non-invertible rules ap- plicable to ). We remark that a meta-backtrack rule is not a rule of the calculus but a meta-rule to encode the proof-search strategy in presence of non-invertible rules. The strategy is a function that, taken the current goal of the proof-search (a node-set) and the current state, returns the next rule to apply in the proof-search. The method nextRule( AbstractNodeSet goal,IterationInfo info) of the Strategy inter- face is a callback method invoked by the engine when it needs to determine the next rule to apply in the proof-search. The engine invokes this method providing as arguments the current goal of the proof-search and a bunch of data describing the last move performed by the engine. E.g., the method getAppliedRule() of Iter- ationInfo returns the rule applied by the engine in the last move; in many cases this is the only data needed to choose the next rule to apply to goal. For instance, this is an high-level description of the strategy for a terminating sequent calculus for intuitionistic propositional logic (as, e.g., the calculus LSJ described in [2]). _AbstractRule nextRule ( _AbstractNodeSet sequent , IterationInfo info ) { if ( an invertible rule r is applicable to sequent ) return r ; _AbstractRule lastAppliedRule = info . getAppliedRule (); if ( lastAppliedRule is not a clash - detection rule ) return the clash - detection rule for sequent ; if ( non invertible rules r1 ... rN are applicable to sequent ) return the meta - backtrack rule collecting r1 ... rN return null } A prover is an object implementing the Prover interface which provides the getStrategy() method and some other methods that are not essential to our dis- cussion. 3 High-level description of the engine We give in Fig. 3 an high-level description of the algorithm implemented by the engine to perform proof-search. An instance of the engine is built by specifying the prover and the goal; searchProof() searches for a proof of the goal driven by 50 M. Ferrari, C. Fiorentini, G. Fiorino. JTabWb the strategy specified by the prover. To simplify the presentation we assume that the data passed to the strategy only consist of the rule applied in the last step. The search space is visited in a depth-first fashion using a stack to store the information related to branch points and backtrack points. More precisely, the stack contains elements (rule,iterator), where rule is the rule that caused the push action, iterator is the associated enumeration. If rule is a regular rule, the element of the stack represents a branch point, if rule is a branch-exists or a meta-backtrack rule, the element represents a backtrack point. The method searchProof() essentially consists of a loop; we call iteration of the engine an iteration of such a loop. At every iteration the state of the engine is characterized by the current goal of the proof-search (i.e., current goal), the rule to apply in the current iteration (i.e., current rule) and the rule selected for the next iteration (i.e., next rule). If current rule is a regular rule or a branch- exists rule, the engine replaces the goal with the first conclusion of the rule and determines the next rule to apply by invoking the strategy. If the applied rule has more than one conclusion, then an element e is pushed on the stack by the call push(current rule,iterator): if current rule is a regular rule, e is a branch point, otherwise e is a backtrack point. If current rule is a meta-backtrack rule, the associated enumeration iterator collects the rules to be applied to current goal. The first rule in the enumeration (returned by the method next()) is applied and, if iterator contains one or more rules, the backtrack point (current rule, iterator) is pushed on the stack. If current rule is a clash-detection rule, then the engine invokes checkStatus() to determine if the current goal is an end-point of the proof-search. If checkStatus() returns FAILURE, then the strategy selects the next rule to apply. If it returns SUCCESS, then restoreBranchPoint() searches the stack for a branch point. If such a point exists, it provides the new goal and the strategy selects the next rule to be applied; if the stack does not contain any branch point, the proof-search successfully terminates. The method restoreBranchPoint() searches the stack for a branch point, namely an element (rule,iterator), where rule is a regular rule. If such an element does not exist, it returns null; otherwise, it returns iterator.next(), representing the new goal to be proved. If iterator is empty, the branch point is removed. If current rule is null, it means that the strategy failed to select a rule for current goal in the last iteration of the engine, hence the proof-search for cur- rent goal has failed. In this case the engine searches the stack for a backtrack point invoking restoreBacktrackPoint(). If a backtrack point exists, the engine ap- propriately updates its state and starts a new iteration. Otherwise, it returns FAILURE to signal that a proof for the input goal does not exist. The method re- storeBacktrackPoint() searches the stack for a backtrack point, that is an element (rule,iterator), where rule is a branch-exists rule or a meta-backtrack rule. If such an element does not exist, null is returned; otherwise, the pair (rule,iterator.next()) is returned. In the latter case, if rule is a branch-exists rule, then iterator.next() is a node-set (the next goal to be proved); otherwise, rule is a meta-backtrack rule and iterator.next() is the next rule to be applied. 51 M. Ferrari, C. Fiorentini, G. Fiorino. JTabWb // goal and prover are the arguments of the engine constructor current goal = goal strategy = prover.getStrategy() next rule = strategy.nextRule(current goal,null) while true do current rule = next rule // the only data about the last iteration if current rule is a regular rule then iterator = current rule.iterator() current goal = iterator.next() next rule = strategy.nextRule(current goal,current rule) if iterator.hasNext() then push(current rule,iterator) end else if current rule is a branch-exists rule then iterator = current rule.iterator() current goal = iterator.next() next rule = strategy.nextRule(current goal,current rule) if iterator.hasNext() then push(current rule,iterator) end else if current rule is a clash-detection rule then if current rule.checkStatus() == SUCCESS then current goal = restoreBranchPoint() // restored is a goal or null if current goal == null then return SUCCESS else next rule = strategy.nextRule(current goal,null) end else next rule = strategy.nextRule(current goal,current rule) end else if current rule is a meta-backtrack rule then iterator = current rule.iterator() next rule = iterator.next() if iterator.hasNext() then push(current rule,iterator) end else if current rule is null then r = restoreBacktrackPoint() // r is a pair if r == null then return FAILURE if snd(r ) is a rule then //rule is a meta-backtrack rule next rule = snd(r ) // snd(r ) is the next rule to try current goal = fst(r ).premise() // restore the goal from the rule else //rule is a branch-exists rule current goal = snd(r ) // snd(r ) is the next sub-goal to try next rule = strategy.nextRule(current goal,current rule) end end end 52 Fig. 3. engine.searchProof() M. Ferrari, C. Fiorentini, G. Fiorino. JTabWb 4 Implemented provers and other features The engine can be executed in verbose mode to get a detailed description of the proof-search or in trace-mode to generate a trace of the proof-search. It is possible to generate the LATEX rendering of the C-trees visited during the proof-search (this only requires to provide the data rendering for node-sets and rule names). The trace of a proof-search can be used to generate the counter- model for unprovable goals. JTabWb also provides some useful support APIs (jtabwbx.* packages): two implementations of propositional formulas, a parser for propositional formulas and a command line launcher for a prover. We have implemented several provers for intuitionistic propositional logic in the JTabWb framework. g3ibu is a prover based on the sequent calculi Gbu and Rbu [3]. lsj is a prover based on the sequent calculi LSJ and RJ [2]. Both these provers allow one to generate counter-models for unprovable sequents. Finally, jpintp provides the implementation of several well-known tableau and sequent calculi for intuitionistic propositional logic. As for future work, we are developing a language to specify the components of a JTabWb prover and a library of formulas implementations and node-set implementations that can be used as building blocks for provers. Finally, we remark that the JTabWb can be used also to implement calculi for first-order logic and, in general, non terminating calculi. What is missing for fruitfully use these kind of calculi is a support which allows the user to directly control the engine execution. We are developing it as an interactive version of the engine. References 1. P. Abate and R. Goré. The tableau workbench. ENTCS, 231:55–67, 2009. 2. M. Ferrari, C. Fiorentini, and G. Fiorino. Contraction-free linear depth sequent cal- culi for intuitionistic propositional logic with the subformula property and minimal depth counter-models. Journal of Automated Reasoning, 51(2):129–149, 2013. 3. M. Ferrari, C. Fiorentini, and G. Fiorino. A terminating evaluation-driven variant of G3i. In D. Galmiche and D. Larchey-Wendling, editors, TABLEAUX 2013, volume 8123 of LNCS, pages 104–118. Springer, 2013. 4. O. Gasquet, A. Herzig, D. Longin, and M. Sahade. LoTREC: Logical tableaux research engineering companion. In B. Beckert, editor, TABLEAUX, volume 3702 of LNCS, pages 318–322. Springer, 2005. 5. D. Tishkovsky, R.A. Schmidt, and M. Khodadadi. The tableau prover generator MetTeL2. In L. Fariñas del Cerro et al., editor, JELIA, volume 7519 of LNCS, pages 492–495. Springer, 2012. 6. A.S. Troelstra and H. Schwichtenberg. Basic Proof Theory, volume 43 of Cambridge Tracts in Theoretical Computer Science. Cambridge University Press, 1996. 53