From 46e4cbd1e3a983cdf1c088cc7bceda15a55e9a45 Mon Sep 17 00:00:00 2001 From: SimonGuilloud <simon.guilloud@bluewin.ch> Date: Tue, 4 Oct 2022 18:31:54 +0200 Subject: [PATCH] Front integration and various changes (#55) * Integration mostly but roughly finished. Code compiles and is adapted to the change of variables representation in the kernel, except for two parts: Unification is not fully working. It is used in many "tactics" deduction rules, expect some of those to not always work. Fornt Macro: Compile-time string interpolation, with quote and so on. Probably easier to correct, but the syntax of that code is very difficult to understand. Dor now, this functionality is disabled (commented). * simplifying, completting and consitentifying representations of schematic nodes in the Kernel. For now, Kernel compiles. * More consistency and completeness changes. Code compile and tests outside the front do pass. Some tests related to unification and rules application in the front don't. * Scaladoc up to date for the whole kernel. * Removed the singleton set symbol from the definition of set theory. It should be an easily derived symbol. Added the missing subset axiom to set theory. Improved documentation for RunningTheory and Set Theory. * Added a new deduction step: Rewrite True. it subsumes Hypothesis and is equivalent to rewriting a sequent that is OCBSL-true. Added top and bot constant predicate labels. Some more documentation. * Rework of the user manual. Contains about everything Kernel-related. Does not contain documentation about the front, nor about the example development. * Commented a test that can't pass until the unifier is repaired. * remove leftover tentative code in Mapping.scala * Make unification tests more readable: * improve error messages when a test fails * name emptyContext and emptyResult instead of using UnificationContext() * split the expected unification result into a separate clause in the method to distinguish visually checkDoesNotUnify(a, b, partial) and checkUnifiesAs(a, b, expected) * Split unification tests into multiple test cases * @Ignore front's proof tests (instead of commenting out) * Improve error messaging in front ProofTests In particular, supply clues when assertions fail and split the tests into test cases to clearly see which tests pass and which fail. * Ignore unification tests until unification works with the new version of Lisa * Run scalafix * Further split unification tests: 1 test corresponds to 1 check * Merge conflicts in the manual * scalafix. * scalafix 2. * scalafix 3. * trying to sastisfy the CI 5. * Trying to satisfy the CI, 6. * Trying to satisfy the CI, 7. * Trying to satisfy the CI, 8. * Front integration (#52) * Large update to the user manual * Merge development in Peano Arithmetic * scalafix. * Front integration (#53) Reintroduce all of the front. Make the parser and printer working with the change on FOL.. Compatibility with the Peano Arithmetic development. Update to the Manual. Co-authored-by: Katja Goltsova <katja.goltsova@protonmail.com> --- Reference Manual/lisa.pdf | Bin 314848 -> 308706 bytes Reference Manual/lisa.tex | 1 + Reference Manual/part1.tex | 50 +- Reference Manual/part2.tex | 6 + build.sbt | 18 +- lisa-examples/src/main/scala/Example.scala | 7 +- .../src/main/scala/lisa/front/fol/FOL.scala | 32 + .../fol/conversions/FrontKernelMappings.scala | 33 + .../from/FormulaConversionsFrom.scala | 43 ++ .../from/TermConversionsFrom.scala | 31 + .../conversions/to/FormulaConversionsTo.scala | 79 +++ .../conversions/to/TermConversionsTo.scala | 45 ++ .../fol/definitions/CommonDefinitions.scala | 41 ++ .../fol/definitions/FormulaDefinitions.scala | 39 ++ .../definitions/FormulaLabelDefinitions.scala | 112 ++++ .../fol/definitions/TermDefinitions.scala | 29 + .../definitions/TermLabelDefinitions.scala | 36 ++ .../scala/lisa/front/fol/ops/CommonOps.scala | 67 ++ .../scala/lisa/front/fol/ops/FormulaOps.scala | 76 +++ .../scala/lisa/front/fol/ops/TermOps.scala | 23 + .../lisa/front/fol/utils/CommonUtils.scala | 93 +++ .../lisa/front/fol/utils/FormulaUtils.scala | 290 +++++++++ .../lisa/front/fol/utils/TermUtils.scala | 109 ++++ .../src/main/scala/lisa/front/package.scala | 7 + .../scala/lisa/front/parser/FrontLexer.scala | 174 ++++++ .../scala/lisa/front/parser/FrontMacro.scala | 431 +++++++++++++ .../scala/lisa/front/parser/FrontParsed.scala | 68 ++ .../scala/lisa/front/parser/FrontParser.scala | 161 +++++ .../scala/lisa/front/parser/FrontReader.scala | 40 ++ .../front/parser/FrontReadingException.scala | 21 + .../lisa/front/parser/FrontResolver.scala | 166 +++++ .../lisa/front/parser/FrontSymbols.scala | 94 +++ .../scala/lisa/front/parser/FrontToken.scala | 59 ++ .../lisa/front/parser/FrontTokensReader.scala | 12 + .../lisa/front/parser/KernelReader.scala | 10 + .../lisa/front/parser/KernelResolver.scala | 299 +++++++++ .../front/parser/KernelRuleIdentifiers.scala | 92 +++ .../printer/FrontPositionedPrinter.scala | 331 ++++++++++ .../lisa/front/printer/FrontPrintNode.scala | 43 ++ .../front/printer/FrontPrintParameters.scala | 18 + .../lisa/front/printer/FrontPrintStyle.scala | 7 + .../main/scala/lisa/front/proof/Proof.scala | 28 + .../proof/sequent/SequentDefinitions.scala | 84 +++ .../lisa/front/proof/sequent/SequentOps.scala | 64 ++ .../state/ProofEnvironmentDefinitions.scala | 224 +++++++ .../state/ProofInterfaceDefinitions.scala | 76 +++ .../proof/state/ProofStateDefinitions.scala | 364 +++++++++++ .../front/proof/state/RuleDefinitions.scala | 168 +++++ .../unification/UnificationDefinitions.scala | 51 ++ .../proof/unification/UnificationUtils.scala | 590 ++++++++++++++++++ .../scala/lisa/front/theory/SetTheory.scala | 52 ++ .../scala/lisa/front/FrontMacroTests.scala | 32 + .../scala/lisa/front/ParserPrinterTests.scala | 68 ++ .../scala/lisa/front/UnificationTests.scala | 256 ++++++++ .../lisa/kernel/fol/CommonDefinitions.scala | 24 +- .../lisa/kernel/fol/EquivalenceChecker.scala | 39 +- .../lisa/kernel/fol/FormulaDefinitions.scala | 99 ++- .../kernel/fol/FormulaLabelDefinitions.scala | 96 +-- .../scala/lisa/kernel/fol/Substitutions.scala | 107 +++- .../lisa/kernel/fol/TermDefinitions.scala | 73 ++- .../kernel/fol/TermLabelDefinitions.scala | 48 +- .../lisa/kernel/proof/RunningTheory.scala | 170 ++--- .../scala/lisa/kernel/proof/SCProof.scala | 2 +- .../lisa/kernel/proof/SCProofChecker.scala | 21 +- .../lisa/kernel/proof/SequentCalculus.scala | 24 +- .../src/main/scala/lisa}/Main.scala | 10 +- .../lisa/settheory/SetTheoryDefinitions.scala | 46 +- .../lisa/settheory}/SetTheoryLibrary.scala | 6 +- .../lisa/settheory/SetTheoryZAxioms.scala | 4 +- .../lisa/settheory/SetTheoryZFAxioms.scala | 2 +- .../main/scala/lisa/tptp/KernelParser.scala | 4 +- .../main/scala/lisa/utils/KernelHelpers.scala | 21 +- .../src/main/scala/lisa/utils/Library.scala | 14 +- .../src/main/scala/lisa/utils/Parser.scala | 20 +- .../main/scala/lisa/utils/ProofsShrink.scala | 296 +++++++++ .../scala/lisa/utils/TheoriesHelpers.scala | 8 +- .../src/test/scala/lisa/kernel/FolTests.scala | 12 +- .../lisa/kernel/InvalidProofPathTests.scala | 2 +- .../scala/lisa/test/TestTheoryLibrary.scala | 4 +- .../test/scala/lisa/utils/ParserTest.scala | 30 +- .../test/scala/lisa/utils/PrinterTest.scala | 28 +- .../lisa/utils/SCProofStepFinderTests.scala | 190 ++++++ .../src/test/scala/lisa/utils/TestUtils.scala | 6 +- src/main/scala/lisa/automation/Proof2.scala | 51 ++ .../lisa/automation/front/predef/Predef.scala | 3 + .../predef/PredefCombinedDefinitions.scala | 25 + .../front/predef/PredefRulesDefinitions.scala | 459 ++++++++++++++ .../predef/PredefTacticsDefinitions.scala | 173 +++++ .../kernel}/Destructors.scala | 7 +- .../kernel}/ProofTactics.scala | 18 +- .../kernel}/SimplePropositionalSolver.scala | 3 +- .../lisa/proven/mathematics/Mapping.scala | 111 ++-- .../lisa/proven/mathematics/SetTheory.scala | 16 +- .../lisa/proven/peano_example/Peano.scala | 12 +- .../peano_example/PeanoArithmetics.scala | 2 +- .../scala/lisa/automation/ProofTests.scala | 392 ++++++++++++ .../scala/lisa/examples/ExampleTests.scala | 29 + .../lisa/proven/InitialProofsTests.scala | 2 +- .../scala/lisa/proven/SimpleProverTests.scala | 2 +- 99 files changed, 7617 insertions(+), 444 deletions(-) create mode 100644 lisa-front/src/main/scala/lisa/front/fol/FOL.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/conversions/FrontKernelMappings.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/conversions/from/FormulaConversionsFrom.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/conversions/from/TermConversionsFrom.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/conversions/to/FormulaConversionsTo.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/conversions/to/TermConversionsTo.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/definitions/CommonDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaLabelDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/definitions/TermDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/definitions/TermLabelDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/ops/CommonOps.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/ops/FormulaOps.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/ops/TermOps.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/utils/CommonUtils.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/utils/FormulaUtils.scala create mode 100644 lisa-front/src/main/scala/lisa/front/fol/utils/TermUtils.scala create mode 100644 lisa-front/src/main/scala/lisa/front/package.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontLexer.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontMacro.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontParsed.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontParser.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontReader.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontReadingException.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontResolver.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontSymbols.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontToken.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/FrontTokensReader.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/KernelReader.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/KernelResolver.scala create mode 100644 lisa-front/src/main/scala/lisa/front/parser/KernelRuleIdentifiers.scala create mode 100644 lisa-front/src/main/scala/lisa/front/printer/FrontPositionedPrinter.scala create mode 100644 lisa-front/src/main/scala/lisa/front/printer/FrontPrintNode.scala create mode 100644 lisa-front/src/main/scala/lisa/front/printer/FrontPrintParameters.scala create mode 100644 lisa-front/src/main/scala/lisa/front/printer/FrontPrintStyle.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/Proof.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/sequent/SequentDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/sequent/SequentOps.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/state/ProofEnvironmentDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/state/ProofInterfaceDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/state/ProofStateDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/state/RuleDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/unification/UnificationDefinitions.scala create mode 100644 lisa-front/src/main/scala/lisa/front/proof/unification/UnificationUtils.scala create mode 100644 lisa-front/src/main/scala/lisa/front/theory/SetTheory.scala create mode 100644 lisa-front/src/test/scala/lisa/front/FrontMacroTests.scala create mode 100644 lisa-front/src/test/scala/lisa/front/ParserPrinterTests.scala create mode 100644 lisa-front/src/test/scala/lisa/front/UnificationTests.scala rename {src/main/scala/lisa/proven => lisa-theories/src/main/scala/lisa}/Main.scala (78%) rename {src/main/scala/lisa/proven => lisa-theories/src/main/scala/lisa/settheory}/SetTheoryLibrary.scala (66%) create mode 100644 lisa-utils/src/main/scala/lisa/utils/ProofsShrink.scala create mode 100644 lisa-utils/src/test/scala/lisa/utils/SCProofStepFinderTests.scala create mode 100644 src/main/scala/lisa/automation/Proof2.scala create mode 100644 src/main/scala/lisa/automation/front/predef/Predef.scala create mode 100644 src/main/scala/lisa/automation/front/predef/PredefCombinedDefinitions.scala create mode 100644 src/main/scala/lisa/automation/front/predef/PredefRulesDefinitions.scala create mode 100644 src/main/scala/lisa/automation/front/predef/PredefTacticsDefinitions.scala rename src/main/scala/lisa/{proven/tactics => automation/kernel}/Destructors.scala (91%) rename src/main/scala/lisa/{proven/tactics => automation/kernel}/ProofTactics.scala (97%) rename src/main/scala/lisa/{proven/tactics => automation/kernel}/SimplePropositionalSolver.scala (98%) create mode 100644 src/test/scala/lisa/automation/ProofTests.scala create mode 100644 src/test/scala/lisa/examples/ExampleTests.scala diff --git a/Reference Manual/lisa.pdf b/Reference Manual/lisa.pdf index 7486581dd85b54c97ded342a5634862f51e17253..bac2001485c9e1952e6c034027af0b0533f7934b 100644 GIT binary patch delta 177452 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
ZQU}Zp!QYQmWA%p@CjGR)tr-$gD6sx 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 F`hQe#{fYnp delta 183215 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%0A3BZkJKx=?-~0TZp6aw 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%0AQ+lpe>M4u9kM{j1uPAL<*PuP61=OmNy&3 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͢ey`f0MRZ6aLtue{Ff3 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 diff --git a/Reference Manual/lisa.tex b/Reference Manual/lisa.tex index 2901e275..bc41e7b4 100644 --- a/Reference Manual/lisa.tex +++ b/Reference Manual/lisa.tex @@ -56,6 +56,7 @@ escapeinside={(*@}{@*)} \begin{document} \maketitle \chapter*{Introduction} + This document aims to give a complete documentation on LISA. Tentatively, every chapter and section will explain a part or concept of LISA, and explains both its implementation and its theoretical foundations \cite{DBLP:conf/tacas/GuilloudK22}. \input{part1.tex} diff --git a/Reference Manual/part1.tex b/Reference Manual/part1.tex index 996c8ede..dd3677f0 100644 --- a/Reference Manual/part1.tex +++ b/Reference Manual/part1.tex @@ -6,18 +6,13 @@ \newcommand\LambdaTF{\operatorname{LambdaTermFormula}} \newcommand\LambdaFF{\operatorname{LambdaFormulaFormula}} - - - - - - \part{Reference Manual} \label{part:manual} \chapter{LISA's trusted code: The Kernel} \label{chapt:kernel} LISA's kernel is the starting point of LISA, formalising the foundations of the whole theorem prover. It is the only trusted code base, meaning that if it is bug-free then no further erroneous or malicious code can violate the soundness property and prove invalid statements. Hence, the two main goals of the kernel are to be efficient and trustworthy. + LISA's foundations are based on very traditional (in the mathematical community) foundational theory of all mathematics: \textbf{First Order Logic}, expressed using \textbf{Sequent Calculus} (augmented with schematic symbols), with axioms of \textbf{Set Theory}. Interestingly, while LISA is built with the goal of using Set Theory, the kernel is actually theory-agnostic and is sound to use with any other set of axioms. Hence, we defer Set Theory to chapter~\ref{chapt:settheory}. @@ -44,10 +39,12 @@ A term is made of a term label and a list of children, whose length must be equa A constant label of arity $0$ is sometimes called just a constant, and a schematic label of arity $0$ a variable. We define the shortcut $$\Var(x) \equiv \operatorname{SchematicTermLabel}(x, 0)$$ \end{defin} + As the definition states, we have two kinds of function symbols: \textit{Constant} ones and \textit{Schematic} ones. Constant labels represent a fixed function symbol in some language, for example the addition ``+'' in Peano arithmetic. Schematic symbols on the other hand, are uninterpreted --- they can represent any possible term and hence can be substituted by any term. Their use will become clearer in the next section when we introduce the concept of deductions. Moreover, variables, which are schematic terms of arity 0, can be bound in formulas, as we explain below. \footnote{In a very traditional presentation of first order logic, we would only have variables, i.e. schematic terms of arity 0, and schematic terms of higher arity would only appear in second order logic. We defer to Part~\ref{part:theory} Section~\ref{sect:theoryfol} the explanation of why our inclusion of schematic function symbols doesn't fundamentally move us out of First Order Logic.} + \begin{defin}[Formulas] The set of Formulas $\mathcal{F}$ is defined similarly: \begin{equation} @@ -64,6 +61,7 @@ Where $\mathcal{L}_{Predicate}$ is the set of \textit{predicate labels}: \mid & \operatorname{SchematicPredicateLabel}(\textnormal{Id}, \textnormal{Arity}) \end{split} \end{equation} + and $\mathcal{L}_{Connector}$ is the set of \textit{connector labels}: \begin{equation} \begin{split} @@ -71,6 +69,7 @@ and $\mathcal{L}_{Connector}$ is the set of \textit{connector labels}: \mid & \operatorname{SchematicConnectorLabel}(\textnormal{Id}, \textnormal{Arity}) \end{split} \end{equation} + A formula can be constructed from a list of terms using a predicate label $${\leq}(x, 7)$$ or from a list of smaller formulas using a connector label @@ -98,7 +97,9 @@ $$ $$ \end{defin} + In this document, as well as in the code documentation, we often write terms and formula in a more conventional way, generally hiding the arity of labels and representing the label with its identifier only, preceded by an interrogation mark ? if the symbol is schematic. When the arity is relevant, we write it with an superscript, for example: + $$ f^3(x,y,z) \equiv \operatorname{Fun}(f, 3)(\List(\Var(x), \Var(y), \Var(z))) $$ @@ -107,11 +108,14 @@ $$ \forall x. \phi \equiv \operatorname{Binder}(\forall, \Var(x), \phi) $$ We also use other usual representations such as symbols in infix position, omitting parenthesis according to usual precedence rules, etc. + Finally, note that we use subscript to emphasize that a variable is possibly free in a term or formula: + $$ t_{x,y,z}, \phi_{x,y,z} $$ + \paragraph{Convention} Throughout this document, and in the code base, we adopt the following conventions: We use $r$, $s$, $t$, $u$ to denote arbitrary terms, $a$, $b$, $c$ to denote constant term symbols of arity $0$ and $f$, $g$, $h$ to denote term symbols of arity non-$0$. We precede those with an interogation mark, such as $?f$ to denote schematic symbols. Moreover, we also use $x$, $y$, $z$ to denote variables (schematic terms of order $0$). For formulas, we use greek letters such as $\phi$, $\psi$, $\tau$ to denote arbitrary formula, $\nu$, $\mu$ to denote formula variables. We use capital letters like $P$, $Q$, $R$ to denote predicate symbols, preceding them similarly with an interrogation mark $?$ for schematic predicates. Schematic connectors are rarer, but when they appear, we precede them by 2 interrogation marks, for example $??c$. Sets or sequences of formula are denoted with capital greek letters $\Pi$, $\Sigma$, $\Gamma$, $\Delta$, etc. @@ -133,11 +137,13 @@ $$ with any fresh variable $z$ (which is not free in $r$ and $\phi$) otherwise. \end{defin} + This definition of substitution is justified by the notion of alpha equivalence: two formulas which are identical up to renaming of bound variables are considered equivalent. In practice, this means that the free variables inside $r$ will never get caught when substituted. We can now define \enquote{lambda terms}. \begin{defin}[Lambda Terms] A lambda term is a meta expression (meaning that it is not part of FOL itself) consisting in a term with ``holes'' that can be filled by other terms. This is represented with specified variables as arguments, similar to lambda calculus. For example, for a functional term with two arguments, we write + $$ L = \Lambdaa(\Var(x), \Var(y))(t_{x,y}) $$ @@ -150,6 +156,7 @@ $$ $$ They are useful because as variables can be substituted by terms, schematic terms labels of arity greater than 0 can be substituted by such functional terms. As the definition of such substitution is rather convoluted to describe, we prefer to show examples and redirect the reader to the source code of LISA for a technical definition. \footnote{Note that in lambda calculus, this would simply be iterated beta-reduction.} + \begin{ex}[Functional terms substitution in terms] \begin{center} \begin{tabular}{|c|r c l|c|} @@ -167,6 +174,7 @@ $?f(x, x+y)$ & $?f$ & $\rightarrow$ & $\lambda x.y. \cos(x-y)*y$ & $\cos(x-(x+y) \end{center} \end{ex} + The definition extends to substitution of schematic terms inside formulas, with capture free substitution for bound variables. For example: \begin{ex}[Functional terms substitution in formulas] @@ -186,6 +194,7 @@ $\exists y. ?f(y) \leq ?f(5)$ & $?f$ & $\rightarrow$ & $\lambda x. x+y$ & $\exis \end{ex} Note that if the lambda expression contains free variables (such as $y$ in the last example), then appropriate alpha-renaming of variables may be needed. + We similarly define functional formulas, except that these can take either term arguments of formulas arguments. Specifically, we use $\LambdaTT$, $\LambdaTF$, $\LambdaFF$ to indicate functional expressions that take terms or formulas as arguments and return a term or formula. \begin{ex}[Typical functional expressions] @@ -200,12 +209,15 @@ $\LambdaFF(\FormulaVar(\nu), \FormulaVar(\mu))$ & $=$ & $\lambda \nu.\mu. \nu \l \end{center} \end{ex} + Note that in the last case, we use $\FormulaVar$ to represent the arguments of the lambda formula. Substitution of functional formulas is completely analogous to (capture free!) substitution of functional terms. + \subsection{The Equivalence Checker} \label{subs:equivalencechecker} + While proving theorems, trivial syntactical transformations such as $p\land q \equiv q\land p$ significantly increase the length of proofs, which is desirable neither for the user nor the machine. Moreover, the proof checker will very often have to check whether two formulas that appear in different sequents are the same. Hence, instead of using pure syntactical equality, LISA implements a powerful equivalence checker able to detect a class of equivalence-preserving logical transformations. As an example, two formulas $p\land q$ and $q\land p$ would be naturally treated as equivalent. For soundness, the relation decided by the algorithm should be contained in the $\Longleftrightarrow$ ``if and only if'' relation of first order logic. It is well known that this relationship is in general undecidable however, and even the $\Longleftrightarrow$ relation for propositional logic is coNP-complete. So, for practicality, we need a relation that is efficiently computable. @@ -229,6 +241,7 @@ Moreover, the implementation in LISA also takes into account symmetry and reflex L11: & $\exists ! x. P = \exists y. \forall x. (x=y) \leftrightarrow P$ & \\ \end{tabular} \ + \caption{Laws LISA's equivalence checker automatically accounts for. LISA's equivalence-checking algorithm is complete (and log-linear time) with respect to laws L1-L11 and L1'-L8'.} \label{tab:OCBSL} @@ -254,6 +267,7 @@ A sequent $\phi \vdash \psi$ is logically but not conceptually equivalent to a s A deduction rule, also called a proof step, has (in general) between zero and two prerequisite sequents (which we call \textit{premises} of the rule) and one conclusion sequent, and possibly take some arguments that describe how the deduction rule is applied. The basic deduction rules used in LISA are shown in Figure~\ref{fig:deduct_rules_1}. + Since we work on first order logic with equality and accept axioms, there are also rules for equality reasoning, which include reflexivity of equality. Moreover, we include equal-for-equal and equivalent-for-equivalent substitutions in Figure~\ref{fig:deduct_rules_2}. While those substitution rules are deduced steps, and hence could technically be omitted, simulating them can sometimes take a high number of steps, so they are included as base steps for efficiency. There are also some special proof steps used to organise proofs, shown in Figure~\ref{fig:deduct_rules_3}. @@ -411,6 +425,7 @@ There are also some special proof steps used to organise proofs, shown in Figure \end{tabular} \end{center} + \caption{Additional deduction rules for substitution and instantiation.} \label{fig:deduct_rules_2} \end{figure} @@ -445,6 +460,7 @@ There are also some special proof steps used to organise proofs, shown in Figure \end{figure} \newpage \subsection{Proofs} + Proof steps can be composed into a directed acyclic graph. The root of the proof shows the conclusive statement, and the leaves are assumptions or tautologies (instances of the \texttt{Hypothesis} rule). Figure~\ref{fig:exampleProof} shows an example of a proof tree for Pierce's Law in strict Sequent Calculus. \begin{figure} @@ -464,6 +480,7 @@ Proof steps can be composed into a directed acyclic graph. The root of the proof \RightLabel{\texttt { RightImplies}} \UnaryInfC{$ \vdash ((\phi \to \psi) \to \phi) \to \phi$} \DisplayProof + \caption{A proof of Pierce's law in Sequent Calculus. The bottommost sequent (root) is the conclusion.} \label{fig:exampleProof} \end{figure} @@ -471,6 +488,7 @@ Proof steps can be composed into a directed acyclic graph. The root of the proof In the Kernel, proof steps are organised linearly, in a list, to form actual proofs. Each proof step refers to its premises using numbers, which indicate the place of the premise in the proof. Moreover, proofs are conditional: they can carry an explicit set of assumed sequents, named ``\lstinline{imports}'', which give some starting points to the proof. Typically, these imports will contain previously proven theorems, definitions, or axioms (More on that in section~\ref{sect:TheoremsAndTheories}). For a proof step to refer to an imported sequent, one uses negative integers. $-1$ corresponds to the first sequent of the import list of the proof, $-2$ to the second, etc. + Formally, a proof is a pair made of a list of proof steps and a list of sequents: $$ \lstinline{Proof(steps:List[ProofStep], imports:List[Sequent])} @@ -503,6 +521,7 @@ In LISA, a proof object has no guarantee to be correct. It is perfectly possible \item Every proof step must be correctly constructed, with the bottom sequent correctly following from the premises by the type of the proof step and its arguments. \end{enumerate} + Given some proof $p$, the proof checker will verify these points. For most proof steps, this typically involve verifying that the premises and the conclusion match according to a transformation specific to the deduction rule. Note that for most cases where there is an intuitive symmetry in arguments, such as \texttt{RightAnd} or \texttt{LeftSubstIff} for example, permutations of those arguments don't matter. Hence, most of the proof checker's work consists in verifying that some formulas, or subformulas thereof, are identical. This is where the equivalence checker comes into play. By checking equivalence rather than strict syntactic equality, a lot of steps become redundant and can be merged. That way, \texttt{LeftAnd}, \texttt{RightOr}, \texttt{LeftIff} become instances of the \texttt{Weakening} rules, and \texttt{RightImplies} an instance of \texttt{RightAnd}. @@ -510,15 +529,18 @@ Hence, most of the proof checker's work consists in verifying that some formulas \texttt{LeftNot}, \texttt{RightNot}, \texttt{LeftImplies}, \texttt{RightImplies}, \texttt{LeftRefl}, \texttt{RightRefl}, \texttt{LeftExistsOne}, \texttt{RightExistsOne} can be omitted altogether. This gives an intuition of how useful the equivalence checker is to cut proof length. It also combines very well with substitution steps. While most proof steps are oblivious to formula transformations allowed by the equivalence checker, they don't allow transformations of the whole sequent: to easily rearrange sequents according to the sequent semantics (\ref{eq:SequentSemantic}), one should use the \texttt{Rewrite} step. + The proof checking function will output a \textit{judgement}: $$\lstinline{SCValidProof(proof: SCProof)}$$ or $$\lstinline{SCInvalidProof(proof: SCProof, path: Seq[Int], message: String)}$$ + \lstinline{SCInvalidProof}{} indicates an erroneous proof. The second argument point to the faulty proofstep (through subproofs), and the third argument is an error message hinting towards why the step is faulty. \section{Theorems and Theories} \label{sect:TheoremsAndTheories} + In mathematics as a discipline, theorems don't exist in isolation. They depend on some agreed upon set of axioms, definitions, and previously proven theorems. Formally, theorems are developed within theories. A theory is defined by a language, which contains the symbols allowed in the theory, and by a set of axioms, which are assumed to hold true within it. In LISA, a \lstinline{theory}{} is a mutable object that starts as the pure theory of predicate logic: It has no known symbols and no axioms. Then we can introduce into it elements of Set Theory (symbols $\in$, $\emptyset$, $\bigcup$ and set theory axioms, see Chapter~\ref{chapt:settheory}) or of any other theory. @@ -530,8 +552,10 @@ To conduct a proof inside a \lstinline{Theory}{}, using its axioms, the proof sh \label{subs:definitions} The user can also introduce definitions in the \lstinline{Theory}{}. LISA's kernel allows to define two kinds of objects: Function (or Term) symbols and Predicate symbols. It is important to remember that in the context of Set Theory, function symbols are not the usual mathematical functions and predicate symbols are not the usual mathematical relations. Indeed, on one hand a function symbol defines an operation on all possible sets, but on the other hand it is impossible to use the symbol alone, without applying it to arguments, or to quantify over function symbol. + Actual mathematical functions on the other hand, are proper sets which contains the graph of a function on some domain. Their domain must be restricted to a proper set, and it is possible to quantify over such set-like functions or to use them without applications. These set-like functions are represented by constant symbols. For example ``$f$ is derivable'' cannot be stated about a function symbol. We will come back to this in Chapter~\ref{chapt:settheory}, but for now let us remember that (non-constant) function symbols are suitable for intersection ($\bigcap$) between sets but not for, say, the Riemann $\zeta$ function. + \begin{figure} A definition in LISA is one of those two kinds of objects: \begin{lstlisting}[frame=single] @@ -575,6 +599,7 @@ Figure \ref{fig:justifications} shows the types of justification in a theory (Th { \def\arraystretch{4} + \begin{figure}[hp] % Justifications: \begin{center} @@ -624,6 +649,7 @@ FunctionDefinition( \\ %\hline \end{tabular} + \caption{The different types of justification in a \lstinline{Theory}{} object.} \label{fig:justifications} \end{center} @@ -784,18 +810,13 @@ This feature is under active development. \subsection{Writing theory files} LISA provides a canonical way of writing and organizing Kernel proofs by mean of a set of utilities and a DSL made possible by some of Scala 3's features such as string interpolation, extension and implicits. -% this exact sentence appears a section above, commenting: -% This is especially directed to people who want to build understanding and intuition regarding formal proofs in Sequent Calculus. The way to write a new theory file to mathematical development is: \begin{lstlisting}[language=Scala, frame=single] object MyTheoryName extends lisa.Main { } \end{lstlisting} -and that's it! To write a theorem, the recommended syntax is -% if it's recommended is it really for example? -% (for example) -: +and that's it! To write a theorem, the recommended syntax is: \begin{lstlisting}[language=Scala, frame=single] object MyTheoryName extends lisa.Main { @@ -838,7 +859,9 @@ object MyTheoryName extends lisa.Main { } \end{lstlisting} -It is important to note that when multiple such files are developed, they all use the same underlying \lstinline{RunningTheory}{}. This makes it possible to use results proved previously by means of a simple \lstinline{import}{} statement as one would import a regular object. Similarly, one should also import as usual automation and tactics developed alongside. It is expected in the medium term that \lstinline{lisa.Main} will come with basic automation. + +======= +It is important to note that when multiple such files are developed, they all use the same underlying \lstinline{RunningTheory}{}. This makes it possible to use results proved previously by means of a simple \lstinline{import}{} statement as one would import a regular object. Similarly, one should also import as usual automation and tactics developed alongside. It is expected in the medium term that \lstinline{lisa.Main}{} will come with basic automation. To check the result of a developed file, and verify that the proofs contain no error, it is possible to run such a library object. % specify which object @@ -908,6 +931,7 @@ Unordered Pair constant & $(\cdot, \cdot )$ & \lstinline$pair(s,t)$ \\ Power Set function & $\mathcal P$ & \lstinline$powerSet(s)$ \\ Set Union/Flatten function & $\bigcup$ & \lstinline$union(x)$ \\ \end{tabular} + \caption{The basic symbols of ZF.} \label{fig:symbolszf} \end{center} diff --git a/Reference Manual/part2.tex b/Reference Manual/part2.tex index edfde3da..409b346a 100644 --- a/Reference Manual/part2.tex +++ b/Reference Manual/part2.tex @@ -8,6 +8,7 @@ An extension by definition is the formal way of introducing new symbols in a mathematical theory. Theories can be extended into new ones by adding new symbols and new axioms to it. We're interested in a special kind of extension, called \textit{conservative extension}. \begin{defin}[Conservative Extension] + A theory $\mathcal{T}_2$ is a conservative extension of a theory $\mathcal{T}_1$ if: \begin{itemize} \item $\mathcal{T}_1 \subset \mathcal{T}_2$ @@ -29,6 +30,7 @@ Moreover, in that case we require that $$ \exists ! y. \phi_{y, x_1,...,x_k} $$ + is a theorem of $\mathcal{T}_1$. \end{itemize} \end{itemize} @@ -38,7 +40,9 @@ We also say that a theory $\mathcal{T}_k$ is an extension by definition of a the For function definition, it is common in logic textbooks to only require the existence of $y$ and not its uniqueness. The axiom one would then obtain would only be $\phi[f(x_1,...,x_n)/y]$ This also leads to conservative extension, but it turns out not to be enough in the presence of axiom schemas (axioms containing schematic symbols). \begin{lemma} + In ZF, an extension by definition without uniqueness doesn't necessarily yield a conservative extension if the use of the new symbol is allowed in axiom schemas. + \end{lemma} \begin{proof} In ZF, consider the formula $\phi_c := \forall x. \exists y. (x \neq \emptyset) \implies y \in x$ expressing that nonempty sets contain an element, which is provable in ZFC. @@ -54,6 +58,7 @@ For the definition with uniqueness, there is a stronger result than only conserv \begin{defin} A theory $\mathcal{T}_2$ is a fully conservative extension over a theory $\mathcal{T}_1$ if: \begin{itemize} + \item it is conservative, and \item for any formula $\phi_2$ with free variables $x_1, ..., x_k$ in the language of $\mathcal{T}_2$, there exists a formula $\phi_1$ in the language of $\mathcal{T}_1$ with free variables among $x_1, ..., x_k$ such that $$\mathcal{T}_2 \vdash \forall x_1...x_k. (\phi_1 \leftrightarrow \phi_2)$$ @@ -64,6 +69,7 @@ An extension by definition with uniqueness is fully conservative. \end{thm} The proof is done by induction on the height of the formula and isn't difficult, but fairly tedious. \begin{thm} + If an extension $\mathcal{T}_2$ of a theory $\mathcal{T}_1$ with axiom schemas is fully conservative, then for any instance of the axiom schemas containing a new symbol $\alpha$, $\Gamma \vdash \alpha$ where $\Gamma$ contains no axiom schema instantiated with new symbols. \end{thm} diff --git a/build.sbt b/build.sbt index aa429a01..82262342 100644 --- a/build.sbt +++ b/build.sbt @@ -17,7 +17,7 @@ inThisBuild( ) val scala2 = "2.13.8" -val scala3 = "3.1.3" +val scala3 = "3.2.1-RC1-bin-20220619-4cb967f-NIGHTLY" val commonSettings2 = Seq( scalaVersion := scala2 @@ -26,11 +26,14 @@ val commonSettings3 = Seq( scalaVersion := scala3, scalacOptions ++= Seq( "-language:implicitConversions", + // "-source:future", re-enable when liancheng/scalafix-organize-imports#221 is fixed "-old-syntax", "-no-indent" + ), libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test", + libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "2.1.1", Test / parallelExecution := false ) @@ -50,8 +53,8 @@ lazy val root = Project( .settings( version := "0.1" ) - .dependsOn(kernel, withTests(utils), theories, tptp) // Everything but `examples` - .aggregate(kernel, utils, theories, tptp) // To run tests on all modules + .dependsOn(kernel, withTests(utils), theories, tptp, front) // Everything but `examples` + .aggregate(kernel, utils, theories, tptp, front) // To run tests on all modules lazy val kernel = Project( id = "lisa-kernel", @@ -66,6 +69,7 @@ lazy val utils = Project( id = "lisa-utils", base = file("lisa-utils") ) + .settings(commonSettings3) .dependsOn(kernel) .dependsOn(silex) @@ -88,6 +92,14 @@ lazy val tptp = Project( ) .dependsOn(withTests(utils)) +lazy val front = Project( + id = "lisa-front", + base = file("lisa-front"), +) + .settings(commonSettings3) + .dependsOn(kernel, utils, theories) + + lazy val examples = Project( id = "lisa-examples", base = file("lisa-examples") diff --git a/lisa-examples/src/main/scala/Example.scala b/lisa-examples/src/main/scala/Example.scala index 57fee33e..d494a522 100644 --- a/lisa-examples/src/main/scala/Example.scala +++ b/lisa-examples/src/main/scala/Example.scala @@ -1,9 +1,10 @@ +import lisa.Main import lisa.kernel.fol.FOL.* import lisa.kernel.proof.SCProof import lisa.kernel.proof.SCProofChecker import lisa.kernel.proof.SCProofChecker.* import lisa.kernel.proof.SequentCalculus.* -import lisa.proven.tactics.SimplePropositionalSolver.solveSequent +import lisa.automation.kernel.SimplePropositionalSolver.solveSequent import lisa.tptp.KernelParser.* import lisa.tptp.ProblemGatherer.* import lisa.tptp.* @@ -26,7 +27,7 @@ object Example { * The last two lines don't need to be changed. */ def proofExample(): Unit = { - object Ex extends lisa.proven.Main { + object Ex extends Main { THEOREM("fixedPointDoubleApplication") of "" PROOF { steps( ???, @@ -139,7 +140,7 @@ object Example { p.formulas.foreach(printAnnotatedFormula) } - val P = SchematicNPredicateLabel("P", 1) + val P = SchematicPredicateLabel("P", 1) val Q = PredicateFormula(VariableFormulaLabel("Q"), Seq()) val R = PredicateFormula(VariableFormulaLabel("R"), Seq()) diff --git a/lisa-front/src/main/scala/lisa/front/fol/FOL.scala b/lisa-front/src/main/scala/lisa/front/fol/FOL.scala new file mode 100644 index 00000000..7119aa09 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/FOL.scala @@ -0,0 +1,32 @@ +package lisa.front.fol + +import lisa.front.fol.conversions.from.* +import lisa.front.fol.conversions.to.* +import lisa.front.fol.definitions.* +import lisa.front.fol.ops.* +import lisa.front.fol.utils.* +import lisa.front.printer.FrontPositionedPrinter + +/** + * The package containing all the definitions and utilities to work with first order logic (FOL). + */ +object FOL + extends FormulaDefinitions + with TermConversionsTo + with FormulaConversionsTo + with TermConversionsFrom + with FormulaConversionsFrom + with TermUtils + with FormulaUtils + with TermOps + with FormulaOps { + + override protected def pretty(term: Term): String = FrontPositionedPrinter.prettyTerm(term) + override protected def pretty(formula: Formula): String = FrontPositionedPrinter.prettyFormula(formula) + + type LabelType = Label + type SchematicLabelType = SchematicLabel + type LabeledTreeType[A <: Label] = LabeledTree[A] + type WithArityType[N <: Arity] = WithArity[N] + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/conversions/FrontKernelMappings.scala b/lisa-front/src/main/scala/lisa/front/fol/conversions/FrontKernelMappings.scala new file mode 100644 index 00000000..9c381ed1 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/conversions/FrontKernelMappings.scala @@ -0,0 +1,33 @@ +package lisa.front.fol.conversions + +import lisa.front.fol.definitions.FormulaDefinitions + +trait FrontKernelMappings extends FormulaDefinitions { + + protected val connectorsTo: Map[ConstantConnectorLabel[?], lisa.kernel.fol.FOL.ConnectorLabel] = Map( + neg -> lisa.kernel.fol.FOL.Neg, + implies -> lisa.kernel.fol.FOL.Implies, + iff -> lisa.kernel.fol.FOL.Iff, + and -> lisa.kernel.fol.FOL.And, + or -> lisa.kernel.fol.FOL.Or + ) + protected val bindersTo: Map[BinderLabel, lisa.kernel.fol.FOL.BinderLabel] = Map( + forall -> lisa.kernel.fol.FOL.Forall, + exists -> lisa.kernel.fol.FOL.Exists, + existsOne -> lisa.kernel.fol.FOL.ExistsOne + ) + protected val predicatesTo: Map[ConstantPredicateLabel[?], lisa.kernel.fol.FOL.ConstantPredicateLabel] = Map( + equality -> lisa.kernel.fol.FOL.equality.asInstanceOf[lisa.kernel.fol.FOL.ConstantPredicateLabel] // Sadly... + ) + + private def reverseMap[U, V](map: Map[U, V]): Map[V, U] = { + val newMap = map.map(_.swap) + assert(newMap.size == map.size) + newMap + } + + protected val connectorsFrom: Map[lisa.kernel.fol.FOL.ConnectorLabel, ConstantConnectorLabel[?]] = reverseMap(connectorsTo) + protected val bindersFrom: Map[lisa.kernel.fol.FOL.BinderLabel, BinderLabel] = reverseMap(bindersTo) + protected val predicatesFrom: Map[lisa.kernel.fol.FOL.ConstantPredicateLabel, ConstantPredicateLabel[?]] = reverseMap(predicatesTo) + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/conversions/from/FormulaConversionsFrom.scala b/lisa-front/src/main/scala/lisa/front/fol/conversions/from/FormulaConversionsFrom.scala new file mode 100644 index 00000000..f3fe0ebb --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/conversions/from/FormulaConversionsFrom.scala @@ -0,0 +1,43 @@ +package lisa.front.fol.conversions.from + +import lisa.front.fol.conversions.FrontKernelMappings +import lisa.front.fol.definitions.FormulaDefinitions + +trait FormulaConversionsFrom extends FormulaDefinitions with TermConversionsFrom with FrontKernelMappings { + + def fromKernel(label: lisa.kernel.fol.FOL.ConstantPredicateLabel): ConstantPredicateLabel[?] = + predicatesFrom.getOrElse(label, ConstantPredicateLabel.unsafe(label.id, label.arity)) + def fromKernel(label: lisa.kernel.fol.FOL.SchematicPredicateLabel): SchematicPredicateLabel[?] = + SchematicPredicateLabel.unsafe(label.id, label.arity) + def fromKernel(label: lisa.kernel.fol.FOL.VariableFormulaLabel): SchematicPredicateLabel[?] = + SchematicPredicateLabel.unsafe(label.id, 0) + + /** + * Lifts a predicate label from the kernel to the front. + * @param label the label in the kernel + * @return the label in the front + */ + def fromKernel(label: lisa.kernel.fol.FOL.PredicateLabel): PredicateLabel[?] = label match { + case schem: lisa.kernel.fol.FOL.SchematicPredicateLabel => fromKernel(schem) + case constant: lisa.kernel.fol.FOL.ConstantPredicateLabel => fromKernel(constant) + case variable: lisa.kernel.fol.FOL.VariableFormulaLabel => fromKernel(variable) + } + + /** + * Lifts a connector label from the kernel to the front. + * @param label the label in the kernel + * @return the label in the front + */ + def fromKernel(label: lisa.kernel.fol.FOL.ConnectorLabel): ConstantConnectorLabel[?] = connectorsFrom(label) + + /** + * Lifts a formula from the kernel to the front. + * @param formula the formula in the kernel + * @return the formula in the front + */ + def fromKernel(formula: lisa.kernel.fol.FOL.Formula): Formula = formula match { + case lisa.kernel.fol.FOL.PredicateFormula(label, args) => PredicateFormula.unsafe(fromKernel(label), args.map(fromKernel)) + case lisa.kernel.fol.FOL.ConnectorFormula(label, args) => ConnectorFormula.unsafe(fromKernel(label), args.map(fromKernel)) + case lisa.kernel.fol.FOL.BinderFormula(label, bound, inner) => BinderFormula(bindersFrom(label), VariableLabel(bound.id), fromKernel(inner)) + } +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/conversions/from/TermConversionsFrom.scala b/lisa-front/src/main/scala/lisa/front/fol/conversions/from/TermConversionsFrom.scala new file mode 100644 index 00000000..c7d85d17 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/conversions/from/TermConversionsFrom.scala @@ -0,0 +1,31 @@ +package lisa.front.fol.conversions.from + +import lisa.front.fol.definitions.TermDefinitions + +trait TermConversionsFrom extends TermDefinitions { + + def fromKernel(label: lisa.kernel.fol.FOL.ConstantFunctionLabel): ConstantFunctionLabel[?] = + ConstantFunctionLabel.unsafe(label.id, label.arity) + def fromKernel(label: lisa.kernel.fol.FOL.SchematicTermLabel): SchematicTermLabel[?] = + SchematicTermLabel.unsafe(label.id, label.arity) + + /** + * Lifts a function label from the kernel to the front. + * @param label the label in the kernel + * @return the label in the front + */ + def fromKernel(label: lisa.kernel.fol.FOL.TermLabel): TermLabel[?] = label match { + case constant: lisa.kernel.fol.FOL.ConstantFunctionLabel => fromKernel(constant) + case schematic: lisa.kernel.fol.FOL.SchematicTermLabel => fromKernel(schematic) + } + + /** + * Lifts a term from the kernel to the front. + * @param term the term in the kernel + * @return the term in the front + */ + def fromKernel(term: lisa.kernel.fol.FOL.Term): Term = term match { + case lisa.kernel.fol.FOL.VariableTerm(label) => VariableTerm(VariableLabel(label.id)) + case lisa.kernel.fol.FOL.Term(label, args) => Term.unsafe(fromKernel(label), args.map(fromKernel)) + } +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/conversions/to/FormulaConversionsTo.scala b/lisa-front/src/main/scala/lisa/front/fol/conversions/to/FormulaConversionsTo.scala new file mode 100644 index 00000000..3dfdbc80 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/conversions/to/FormulaConversionsTo.scala @@ -0,0 +1,79 @@ +package lisa.front.fol.conversions.to + +import lisa.front.fol.conversions.FrontKernelMappings +import lisa.front.fol.definitions.FormulaDefinitions + +trait FormulaConversionsTo extends FormulaDefinitions with TermConversionsTo with FrontKernelMappings { + + def toKernel(label: ConstantConnectorLabel[?]): lisa.kernel.fol.FOL.ConnectorLabel = connectorsTo(label) + + def toKernel(label: ConnectorLabel[?]): lisa.kernel.fol.FOL.ConnectorLabel = label match { + case constant: ConstantConnectorLabel[?] => toKernel(constant) + case _: SchematicConnectorLabel[?] => throw new UnsupportedOperationException + } + + def toKernel(label: ConstantPredicateLabel[?]): lisa.kernel.fol.FOL.ConstantPredicateLabel = + predicatesTo.getOrElse(label, lisa.kernel.fol.FOL.ConstantPredicateLabel(label.id, label.arity)) + + def toKernel(label: SchematicPredicateLabel[0]): lisa.kernel.fol.FOL.VariableFormulaLabel = { + lisa.kernel.fol.FOL.VariableFormulaLabel(label.id) + } + + def toKernel(label: SchematicPredicateLabel[?]): lisa.kernel.fol.FOL.SchematicVarOrPredLabel = { + if (label.arity == 0) lisa.kernel.fol.FOL.VariableFormulaLabel(label.id) + else lisa.kernel.fol.FOL.SchematicPredicateLabel(label.id, label.arity) + } + + def toKernel(label: PredicateLabel[?]): lisa.kernel.fol.FOL.PredicateLabel = label match { + case constant: ConstantPredicateLabel[?] => toKernel(constant) + case schematic: SchematicPredicateLabel[?] => toKernel(schematic) + } + + def toKernel(label: BinderLabel): lisa.kernel.fol.FOL.BinderLabel = bindersTo(label) + + /** + * Translates a label from the front to the kernel. + * @param label the label in the front + * @return the label in the kernel + */ + def toKernel(label: FormulaLabel): lisa.kernel.fol.FOL.FormulaLabel = label match { + case predicate: PredicateLabel[?] => toKernel(predicate) + case connector: ConnectorLabel[?] => toKernel(connector) + case binder: BinderLabel => toKernel(binder) + } + + def toKernel(formula: PredicateFormula): lisa.kernel.fol.FOL.PredicateFormula = + lisa.kernel.fol.FOL.PredicateFormula(toKernel(formula.label), formula.args.map(toKernel)) + + def toKernel(formula: ConnectorFormula): lisa.kernel.fol.FOL.ConnectorFormula = + lisa.kernel.fol.FOL.ConnectorFormula(toKernel(formula.label), formula.args.map(toKernel)) + + def toKernel(formula: BinderFormula): lisa.kernel.fol.FOL.BinderFormula = + lisa.kernel.fol.FOL.BinderFormula(toKernel(formula.label), toKernel(formula.bound), toKernel(formula.inner)) + + /** + * Translates a formula from the front to the kernel. + * @param formula the formula in the front + * @return the formula in the kernel + */ + def toKernel(formula: Formula): lisa.kernel.fol.FOL.Formula = formula match { + case predicate: PredicateFormula => toKernel(predicate) + case connector: ConnectorFormula => toKernel(connector) + case binder: BinderFormula => toKernel(binder) + } + + given Conversion[PredicateFormula, lisa.kernel.fol.FOL.PredicateFormula] = toKernel + given Conversion[ConnectorFormula, lisa.kernel.fol.FOL.ConnectorFormula] = toKernel + given Conversion[BinderFormula, lisa.kernel.fol.FOL.BinderFormula] = toKernel + given Conversion[Formula, lisa.kernel.fol.FOL.Formula] = toKernel + given Conversion[ConstantPredicateLabel[?], lisa.kernel.fol.FOL.ConstantPredicateLabel] = toKernel + given Conversion[SchematicPredicateLabel[0], lisa.kernel.fol.FOL.VariableFormulaLabel] = toKernel + given Conversion[SchematicPredicateLabel[?], lisa.kernel.fol.FOL.SchematicFormulaLabel] = toKernel + given Conversion[PredicateLabel[?], lisa.kernel.fol.FOL.PredicateLabel] = toKernel + given Conversion[ConnectorLabel[?], lisa.kernel.fol.FOL.ConnectorLabel] = toKernel + given Conversion[BinderLabel, lisa.kernel.fol.FOL.BinderLabel] = toKernel + given Conversion[FormulaLabel, lisa.kernel.fol.FOL.FormulaLabel] = toKernel + + given Conversion[(Formula, Formula), (lisa.kernel.fol.FOL.Formula, lisa.kernel.fol.FOL.Formula)] = (a, b) => (a, b) + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/conversions/to/TermConversionsTo.scala b/lisa-front/src/main/scala/lisa/front/fol/conversions/to/TermConversionsTo.scala new file mode 100644 index 00000000..86f7a9e4 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/conversions/to/TermConversionsTo.scala @@ -0,0 +1,45 @@ +package lisa.front.fol.conversions.to + +import lisa.front.fol.definitions.TermDefinitions + +trait TermConversionsTo extends TermDefinitions { + + def toKernel(label: ConstantFunctionLabel[?]): lisa.kernel.fol.FOL.ConstantFunctionLabel = + lisa.kernel.fol.FOL.ConstantFunctionLabel(label.id, label.arity) + + def toKernel(label: SchematicTermLabel[?]): lisa.kernel.fol.FOL.SchematicTermLabel = { + if (label.arity == 0) lisa.kernel.fol.FOL.VariableLabel(label.id) + else lisa.kernel.fol.FOL.SchematicFunctionLabel(label.id, label.arity) + } + + def toKernel(label: SchematicTermLabel[0]): lisa.kernel.fol.FOL.VariableLabel = { + lisa.kernel.fol.FOL.VariableLabel(label.id) + } + + /** + * Translates a label from the front to the kernel. + * @param label the label in the front + * @return the label in the kernel + */ + def toKernel(label: TermLabel[?]): lisa.kernel.fol.FOL.TermLabel = label match { + case label: ConstantFunctionLabel[?] => toKernel(label) + case label: SchematicTermLabel[?] => toKernel(label) + } + + /** + * Translates a term from the front to the kernel. + * @param term the term in the front + * @return the term in the kernel + */ + def toKernel(term: Term): lisa.kernel.fol.FOL.Term = + lisa.kernel.fol.FOL.Term(toKernel(term.label), term.args.map(toKernel)) + + given Conversion[VariableLabel, lisa.kernel.fol.FOL.VariableLabel] = toKernel + given Conversion[ConstantFunctionLabel[?], lisa.kernel.fol.FOL.ConstantFunctionLabel] = toKernel + given Conversion[SchematicTermLabel[?], lisa.kernel.fol.FOL.SchematicTermLabel] = toKernel + given Conversion[TermLabel[?], lisa.kernel.fol.FOL.TermLabel] = toKernel + given Conversion[Term, lisa.kernel.fol.FOL.Term] = toKernel + + given Conversion[(Term, Term), (lisa.kernel.fol.FOL.Term, lisa.kernel.fol.FOL.Term)] = (a, b) => (a, b) + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/definitions/CommonDefinitions.scala b/lisa-front/src/main/scala/lisa/front/fol/definitions/CommonDefinitions.scala new file mode 100644 index 00000000..99ad6340 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/definitions/CommonDefinitions.scala @@ -0,0 +1,41 @@ +package lisa.front.fol.definitions + +trait CommonDefinitions { + + /** + * The label of a node. + */ + private[fol] trait Label { + val id: String + } + + /** + * A label node that is considered schematic (namely one that can be instantiated). + */ + private[fol] trait SchematicLabel extends Label + + /** + * A labeled tree. + * @tparam A the label of that tree + */ + private[fol] trait LabeledTree[A <: Label] { + val label: A + } + + /** + * Statically typed arity. + */ + type Arity = Int & Singleton + + /** + * A node with arity. + * @tparam N the arity as a static type, or `?` if unknown + */ + private[fol] trait WithArity[N <: Arity] { + val arity: N + } + + private[fol] def isLegalApplication(withArity: WithArity[?], args: Seq[?]): Boolean = + withArity.arity == -1 || withArity.arity == args.size + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaDefinitions.scala b/lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaDefinitions.scala new file mode 100644 index 00000000..5a0d7277 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaDefinitions.scala @@ -0,0 +1,39 @@ +package lisa.front.fol.definitions + +trait FormulaDefinitions extends FormulaLabelDefinitions with TermDefinitions { + + protected def pretty(formula: Formula): String + + /** + * @see [[lisa.kernel.fol.FOL.Formula]] + */ + sealed abstract class Formula extends LabeledTree[FormulaLabel] { + override def toString: String = pretty(this) + } + + /** + * @see [[lisa.kernel.fol.FOL.PredicateFormula]] + */ + final case class PredicateFormula protected (label: PredicateLabel[?], args: Seq[Term]) extends Formula { + require(isLegalApplication(label, args)) + } + object PredicateFormula { + def unsafe(label: PredicateLabel[?], args: Seq[Term]): PredicateFormula = PredicateFormula(label, args) + } + + /** + * @see [[lisa.kernel.fol.FOL.ConnectorFormula]] + */ + final case class ConnectorFormula protected (label: ConnectorLabel[?], args: Seq[Formula]) extends Formula { + require(isLegalApplication(label, args)) + } + object ConnectorFormula { + def unsafe(label: ConnectorLabel[?], args: Seq[Formula]): ConnectorFormula = ConnectorFormula(label, args) + } + + /** + * @see [[lisa.kernel.fol.FOL.BinderFormula]] + */ + final case class BinderFormula(label: BinderLabel, bound: VariableLabel, inner: Formula) extends Formula + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaLabelDefinitions.scala b/lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaLabelDefinitions.scala new file mode 100644 index 00000000..3ef97f90 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/definitions/FormulaLabelDefinitions.scala @@ -0,0 +1,112 @@ +package lisa.front.fol.definitions + +trait FormulaLabelDefinitions extends CommonDefinitions { + + /** + * @see [[lisa.kernel.fol.FOL.FormulaLabel]] + */ + sealed abstract class FormulaLabel extends Label + + /** + * @see [[lisa.kernel.fol.FOL.PredicateLabel]] + */ + sealed abstract class PredicateLabel[N <: Arity] extends FormulaLabel with WithArity[N] + + /** + * @see [[lisa.kernel.fol.FOL.ConstantPredicateLabel]] + */ + final case class ConstantPredicateLabel[N <: Arity] protected (id: String, arity: N) extends PredicateLabel[N] + + /** + * @see [[lisa.kernel.fol.FOL.SchematicFormulaLabel]] + */ + final case class SchematicPredicateLabel[N <: Arity] protected (id: String, arity: N) extends PredicateLabel[N] with SchematicLabel + + object ConstantPredicateLabel { + def apply[N <: Arity](id: String)(using v: ValueOf[N]): ConstantPredicateLabel[N] = ConstantPredicateLabel(id, v.value) + def unsafe(id: String, arity: Int): ConstantPredicateLabel[?] = ConstantPredicateLabel(id, arity) + } + object SchematicPredicateLabel { + def apply[N <: Arity](id: String)(using v: ValueOf[N]): SchematicPredicateLabel[N] = SchematicPredicateLabel(id, v.value) + def unsafe(id: String, arity: Int): SchematicPredicateLabel[?] = SchematicPredicateLabel(id, arity) + } + + /** + * @see [[lisa.kernel.fol.FOL.equality]] + */ + val equality: ConstantPredicateLabel[2] = ConstantPredicateLabel("=") + + /** + * For completeness, the front provides constant & schematic connectors. + * The kernel only supports constant such labels. The compromise is to only work with those in the front and avoid + * translating them into the kernel. + * @see [[PredicateLabel]] + * @see [[TermLabelDefinitions.TermLabel]] + */ + sealed abstract class ConnectorLabel[N <: Arity] extends FormulaLabel with WithArity[N] + + /** + * @see [[lisa.kernel.fol.FOL.ConnectorLabel]] + */ + final case class ConstantConnectorLabel[N <: Arity] protected (id: String, arity: N) extends ConnectorLabel[N] + + /** + * A schematic connector label, exclusive to the front. + * @see [[ConnectorLabel]] + */ + final case class SchematicConnectorLabel[N <: Arity] protected (id: String, arity: N) extends ConnectorLabel[N] with SchematicLabel + + object ConstantConnectorLabel { + private[FormulaLabelDefinitions] def apply[N <: Arity](id: String)(using v: ValueOf[N]): ConstantConnectorLabel[N] = ConstantConnectorLabel(id, v.value) + } + object SchematicConnectorLabel { + def apply[N <: Arity](id: String)(using v: ValueOf[N]): SchematicConnectorLabel[N] = SchematicConnectorLabel(id, v.value) + def unsafe(id: String, arity: Int): SchematicConnectorLabel[?] = SchematicConnectorLabel(id, arity) + } + + /** + * @see [[lisa.kernel.fol.FOL.Neg]] + */ + val neg: ConstantConnectorLabel[1] = ConstantConnectorLabel("¬") + + /** + * @see [[lisa.kernel.fol.FOL.Implies]] + */ + val implies: ConstantConnectorLabel[2] = ConstantConnectorLabel("⇒") + + /** + * @see [[lisa.kernel.fol.FOL.Iff]] + */ + val iff: ConstantConnectorLabel[2] = ConstantConnectorLabel("↔") + + /** + * @see [[lisa.kernel.fol.FOL.And]] + */ + val and: ConstantConnectorLabel[2] = ConstantConnectorLabel("∧") + + /** + * @see [[lisa.kernel.fol.FOL.Or]] + */ + val or: ConstantConnectorLabel[2] = ConstantConnectorLabel("∨") + + /** + * @see [[lisa.kernel.fol.FOL.BinderLabel]] + */ + final case class BinderLabel private[FormulaLabelDefinitions] (id: String) extends FormulaLabel + + /** + * @see [[lisa.kernel.fol.FOL.Forall]] + */ + val forall: BinderLabel = BinderLabel("∀") + + /** + * @see [[lisa.kernel.fol.FOL.Exists]] + */ + val exists: BinderLabel = BinderLabel("∃") + + /** + * @see [[lisa.kernel.fol.FOL.ExistsOne]] + */ + val existsOne: BinderLabel = BinderLabel("∃!") + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/definitions/TermDefinitions.scala b/lisa-front/src/main/scala/lisa/front/fol/definitions/TermDefinitions.scala new file mode 100644 index 00000000..f8e94bfe --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/definitions/TermDefinitions.scala @@ -0,0 +1,29 @@ +package lisa.front.fol.definitions + +trait TermDefinitions extends TermLabelDefinitions { + + protected def pretty(term: Term): String + + /** + * @see [[lisa.kernel.fol.FOL.Term]] + */ + final case class Term protected (label: TermLabel[?], args: Seq[Term]) extends LabeledTree[TermLabel[?]] { + require(isLegalApplication(label, args)) + override def toString: String = pretty(this) + } + object Term { + def unsafe(label: TermLabel[?], args: Seq[Term]): Term = Term(label, args) + } + + /** + * @see [[lisa.kernel.fol.FOL.VariableTerm]] + */ + object VariableTerm extends (VariableLabel => Term) { + def apply(label: VariableLabel): Term = Term.unsafe(label, Seq()) + def unapply(t: Term): Option[VariableLabel] = t.label match { + case l: SchematicTermLabel[?] if l.arity == 0 => Some(l.asInstanceOf[VariableLabel]) + case _ => None + } + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/definitions/TermLabelDefinitions.scala b/lisa-front/src/main/scala/lisa/front/fol/definitions/TermLabelDefinitions.scala new file mode 100644 index 00000000..5595dc66 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/definitions/TermLabelDefinitions.scala @@ -0,0 +1,36 @@ +package lisa.front.fol.definitions + +trait TermLabelDefinitions extends CommonDefinitions { + + /** + * @see [[lisa.kernel.fol.FOL.TermLabel]] + */ + sealed abstract class TermLabel[N <: Arity] extends Label with WithArity[N] + + /** + * @see [[lisa.kernel.fol.FOL.ConstantLabel]] + */ + final case class ConstantFunctionLabel[N <: Arity] protected (id: String, arity: N) extends TermLabel[N] + + /** + * @see [[lisa.kernel.fol.FOL.SchematicTermLabel]] + */ + final case class SchematicTermLabel[N <: Arity] protected (id: String, arity: N) extends TermLabel[N] with SchematicLabel + + type VariableLabel = SchematicTermLabel[0] + object VariableLabel { + def unapply(l: VariableLabel): Option[String] = Some(l.id) + def apply(l: String): VariableLabel = SchematicTermLabel[0](l) + } + + object ConstantFunctionLabel { + def apply[N <: Arity](id: String)(using v: ValueOf[N]): ConstantFunctionLabel[N] = ConstantFunctionLabel(id, v.value) + def unsafe(id: String, arity: Int): ConstantFunctionLabel[?] = ConstantFunctionLabel(id, arity) + } + + object SchematicTermLabel { + def apply[N <: Arity](id: String)(using v: ValueOf[N]): SchematicTermLabel[N] = SchematicTermLabel(id, v.value) + def unsafe(id: String, arity: Int): SchematicTermLabel[?] = SchematicTermLabel(id, arity) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/ops/CommonOps.scala b/lisa-front/src/main/scala/lisa/front/fol/ops/CommonOps.scala new file mode 100644 index 00000000..e1a0cccf --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/ops/CommonOps.scala @@ -0,0 +1,67 @@ +package lisa.front.fol.ops + +import lisa.front.fol.definitions.CommonDefinitions + +import scala.compiletime.ops.int.- + +trait CommonOps extends CommonDefinitions { + + /** + * Creates a tuple type with <code>N</code> types <code>T</code>. + * @tparam T the type of that tuple's members + * @tparam N the arity of that tuple + */ + protected type FillTuple[T, N <: Arity] <: Tuple & Matchable = N match { + case 0 => EmptyTuple + case _ => T *: FillTuple[T, N - 1] + } + + /** + * Similar to [[FillTuple]], except that for convenience when used as function arguments, the tuple of arity one + * is replaced by its own element. + * @tparam T + * @tparam N + */ + type FillArgs[T <: Matchable, N <: Arity] <: (T | Tuple) & Matchable = N match { + case 1 => T + case _ => FillTuple[T, N] + } + + // given liftArgsConversion1[U, V]: Conversion[V, FillArgs[U, 0] => V] = v => _ => v + // given liftArgsConversion2[U, V]: Conversion[() => V, FillArgs[U, 0] => V] = v => _ => v() + + private[front] def fillTupleParameters[N <: Arity, T <: Matchable, U](name: String => T, n: N, f: FillArgs[T, N] => U, taken: Set[String] = Set.empty): (FillArgs[T, N], U) = { + val newIds = LazyList.from(0).map(i => s"x$i").filter(!taken.contains(_)).take(n).toIndexedSeq + val parameters = fillTuple[T, N](n, i => name(newIds(i))) + (parameters, f(parameters)) + } + + protected def tuple2seq[T <: Matchable, N <: Arity](any: FillArgs[T, N]): Seq[T] = + any match { + case tuple: Tuple => tuple.productIterator.toSeq.asInstanceOf[Seq[T]] + case _ => Seq(any.asInstanceOf[T]) // Safe cast + } + + /** + * Fills a tuple with <code>n</code> elements of type <code>T</code>. + * @param n the arity of the tuple + * @param f the function generating the elements + * @tparam T the type of the elements + * @tparam N the arity type + * @return the generated tuple + */ + def fillTuple[T <: Matchable, N <: Arity](n: N, f: Int => T): FillArgs[T, N] = + if (n == 1) + f(0).asInstanceOf[FillArgs[T, N]] + else + (0 until n).foldRight(EmptyTuple: Tuple)((i, acc) => f(i) *: acc).asInstanceOf[FillArgs[T, N]] + + extension [T <: Matchable, N <: Arity](tuple: FillArgs[T, N]) { + + /** + * Converts a tuple into a sequence of value. Loses all typing information, but simplifies the usage. + */ + def toSeq: Seq[T] = tuple2seq(tuple) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/ops/FormulaOps.scala b/lisa-front/src/main/scala/lisa/front/fol/ops/FormulaOps.scala new file mode 100644 index 00000000..455582c0 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/ops/FormulaOps.scala @@ -0,0 +1,76 @@ +package lisa.front.fol.ops + +import lisa.front.fol.definitions.FormulaDefinitions + +trait FormulaOps extends FormulaDefinitions with CommonOps { + + // lampepfl/dotty#14907 + + // extension[N <: Arity] (label: PredicateLabel[N]) + // def apply(args: FillArgs[Term, N]): PredicateFormula = PredicateFormula.unsafe(label, tuple2seq(args)) + extension (label: PredicateLabel[2]) { def apply(a: Term, b: Term): PredicateFormula = PredicateFormula.unsafe(label, Seq(a, b)) } + extension (label: PredicateLabel[1]) { def apply(a: Term): PredicateFormula = PredicateFormula.unsafe(label, Seq(a)) } + extension (label: PredicateLabel[0]) { def apply(): PredicateFormula = PredicateFormula.unsafe(label, Seq.empty) } + + // extension[N <: Arity] (label: ConnectorLabel[N]) + // def apply(args: FillArgs[Formula, N]): ConnectorFormula = ConnectorFormula.unsafe(label, tuple2seq(args)) + extension (label: ConnectorLabel[2]) { def apply(a: Formula, b: Formula): ConnectorFormula = ConnectorFormula.unsafe(label, Seq(a, b)) } + extension (label: ConnectorLabel[1]) { def apply(a: Formula): ConnectorFormula = ConnectorFormula.unsafe(label, Seq(a)) } + extension (label: ConnectorLabel[0]) { def apply(): ConnectorFormula = ConnectorFormula.unsafe(label, Seq.empty) } + + extension [N <: Arity](label: BinderLabel) { def apply(bound: VariableLabel, inner: Formula): BinderFormula = BinderFormula(label, bound, inner) } + + given Conversion[ConstantPredicateLabel[0], PredicateFormula] = PredicateFormula.unsafe(_, Seq.empty) + given Conversion[SchematicPredicateLabel[0], PredicateFormula] = PredicateFormula.unsafe(_, Seq.empty) + given Conversion[PredicateLabel[0], PredicateFormula] = PredicateFormula.unsafe(_, Seq.empty) + + given Conversion[ConstantConnectorLabel[0], ConnectorFormula] = ConnectorFormula.unsafe(_, Seq.empty) + given Conversion[SchematicConnectorLabel[0], ConnectorFormula] = ConnectorFormula.unsafe(_, Seq.empty) + given Conversion[ConnectorLabel[0], ConnectorFormula] = ConnectorFormula.unsafe(_, Seq.empty) + + @deprecated + given Conversion[Formula, FormulaLabel] = _.label + + extension (f: Formula) { + def unary_! : ConnectorFormula = ConnectorFormula.unsafe(neg, Seq(f)) + infix def ==>(g: Formula): ConnectorFormula = ConnectorFormula.unsafe(implies, Seq(f, g)) + infix def <=>(g: Formula): ConnectorFormula = ConnectorFormula.unsafe(iff, Seq(f, g)) + infix def /\(g: Formula): ConnectorFormula = ConnectorFormula.unsafe(and, Seq(f, g)) + infix def \/(g: Formula): ConnectorFormula = ConnectorFormula.unsafe(or, Seq(f, g)) + } + + extension (t: Term) { + infix def ===(u: Term): PredicateFormula = PredicateFormula.unsafe(equality, Seq(t, u)) + } + + // Extractors + + object ! { + def unapply(f: Formula): Option[Formula] = f match { + case ConnectorFormula(`neg`, Seq(g)) => Some(g) + case _ => None + } + } + + sealed abstract class UnapplyBinaryConnector(label: ConnectorLabel[2]) { + def unapply(f: Formula): Option[(Formula, Formula)] = f match { + case ConnectorFormula(`label`, Seq(a, b)) => Some((a, b)) + case _ => None + } + } + + object ==> extends UnapplyBinaryConnector(implies) + object <=> extends UnapplyBinaryConnector(iff) + object /\ extends UnapplyBinaryConnector(and) + object \/ extends UnapplyBinaryConnector(or) + + sealed abstract class UnapplyBinaryPredicate(label: PredicateLabel[2]) { + def unapply(f: Formula): Option[(Term, Term)] = f match { + case PredicateFormula(`label`, Seq(a, b)) => Some((a, b)) + case _ => None + } + } + + object === extends UnapplyBinaryPredicate(equality) + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/ops/TermOps.scala b/lisa-front/src/main/scala/lisa/front/fol/ops/TermOps.scala new file mode 100644 index 00000000..3361d8d8 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/ops/TermOps.scala @@ -0,0 +1,23 @@ +package lisa.front.fol.ops + +import lisa.front.fol.definitions.TermDefinitions + +trait TermOps extends TermDefinitions with CommonOps { + + extension [N <: Arity](label: TermLabel[N]) { + def apply(args: FillArgs[Term, N]): Term = Term.unsafe(label, tuple2seq(args)) + } + // extension (label: TermLabel[2]) + // def apply(a: Term, b: Term): Term = Term.unsafe(label, Seq(a, b)) + // extension (label: TermLabel[1]) + // def apply(a: Term): Term = Term.unsafe(label, Seq(a)) + extension (label: TermLabel[0]) { def apply(): Term = Term.unsafe(label, Seq.empty) } + + given Conversion[ConstantFunctionLabel[0], Term] = Term.unsafe(_, Seq.empty) + given Conversion[SchematicTermLabel[0], Term] = Term.unsafe(_, Seq.empty) + given Conversion[TermLabel[0], Term] = Term.unsafe(_, Seq.empty) + + @deprecated + given Conversion[Term, TermLabel[?]] = _.label + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/utils/CommonUtils.scala b/lisa-front/src/main/scala/lisa/front/fol/utils/CommonUtils.scala new file mode 100644 index 00000000..37bcbbc2 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/utils/CommonUtils.scala @@ -0,0 +1,93 @@ +package lisa.front.fol.utils + +import lisa.front.fol.definitions.CommonDefinitions +import lisa.front.fol.ops.CommonOps + +import scala.annotation.targetName + +trait CommonUtils extends CommonDefinitions with CommonOps { + + /** + * Creates a fresh id with respect to a set of taken ids. A base id can optionally be specified. + * @param taken the set of taken ids + * @param base an optional base id + * @return a fresh id + */ + def freshId(taken: Set[String], base: String = "x"): String = { + def findFirst(i: Int): String = { + val id = s"${base}_$i" + if (taken.contains(id)) findFirst(i + 1) else id + } + findFirst(0) + } + + /** + * Creates a sequence of fresh ids with respect to a set of taken ids. A base id can optionally be specified. + * @param taken the set of taken ids + * @param base an optional base id + * @return a sequent of fresh ids + */ + def freshIds(taken: Set[String], n: Int, base: String = "x"): Seq[String] = { + require(n >= 0) + def findMany(i: Int, n: Int, taken: Set[String], acc: Seq[String]): Seq[String] = { + if (n > 0) { + val id = s"${base}_$i" + if (taken.contains(id)) findMany(i + 1, n, taken, acc) else findMany(i + 1, n - 1, taken + id, id +: acc) + } else { + acc + } + } + findMany(0, n, taken, Seq.empty).reverse + } + + /** + * Represents the renaming of a label. + * @param from the label that should be renamed + * @param to the label it should be renamed to + */ + case class RenamedLabel[L <: Label & WithArity[?], A <: L & SchematicLabel, B <: L] private (from: A, to: B) + object RenamedLabel { + @targetName("applySafe") + def apply[N <: Arity, L <: Label & WithArity[N], A <: L & SchematicLabel, B <: L](from: A, to: B): RenamedLabel[L, A, B] = new RenamedLabel(from, to) + def unsafe[L <: Label & WithArity[?], A <: L & SchematicLabel, B <: L](from: A, to: B): RenamedLabel[L, A, B] = new RenamedLabel(from, to) + } + extension [L <: Label & WithArity[?], A <: L & SchematicLabel](renamed: RenamedLabel[L, A, A]) { + def swap: RenamedLabel[L, A, A] = RenamedLabel.unsafe(renamed.to, renamed.from) + } + + /** + * A lambda definition, namely an anonymous function taking some arguments and returning a result. + * Arguments are represented as holes, thus the body of the function is known at runtime. + */ + protected abstract class LambdaDefinition[N <: Arity, S <: SchematicLabel & WithArity[?], T <: LabeledTree[?]] extends WithArity[N] { + type U <: LabeledTree[? >: S] + + val parameters: Seq[S] + val body: T + + def apply(args: FillArgs[U, N]): T = unsafe(args.toSeq) + def unsafe(args: Seq[U]): T = { + require(args.size == arity) + instantiate(args) + } + protected def instantiate(args: Seq[U]): T + + override val arity: N = parameters.size.asInstanceOf[N] + + require(parameters.forall(_.arity == 0)) + require(parameters.distinct.size == parameters.size) + } + + /** + * Represents the instantiation of a schema. + */ + protected abstract class AssignedSchema[R <: SchematicLabel & WithArity[?], S <: SchematicLabel & WithArity[?]] { + type L <: LambdaDefinition[?, S, ? <: LabeledTree[? >: R]] + + val schema: R + val lambda: L + + require(schema.arity == lambda.arity) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/utils/FormulaUtils.scala b/lisa-front/src/main/scala/lisa/front/fol/utils/FormulaUtils.scala new file mode 100644 index 00000000..342a8596 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/utils/FormulaUtils.scala @@ -0,0 +1,290 @@ +package lisa.front.fol.utils + +import lisa.front.fol.conversions.to.FormulaConversionsTo +import lisa.front.fol.definitions.FormulaDefinitions +import lisa.front.fol.ops.FormulaOps + +trait FormulaUtils extends TermUtils with FormulaDefinitions with FormulaConversionsTo with FormulaOps { + + type RenamedPredicate[B <: PredicateLabel[?]] = RenamedLabel[PredicateLabel[?], SchematicPredicateLabel[?], B] + type RenamedPredicateSchema = RenamedPredicate[SchematicPredicateLabel[?]] + extension [L <: PredicateLabel[?]](renamed: RenamedPredicate[L]) { + def toAssignment: AssignedPredicate = { + val parameters = freshIds(Set.empty, renamed.from.arity).map(SchematicTermLabel.apply[0]) + AssignedPredicate.unsafe(renamed.from, LambdaPredicate.unsafe(parameters, PredicateFormula.unsafe(renamed.to, parameters.map(Term.unsafe(_, Seq.empty))))) + } + } + type RenamedConnector[B <: ConnectorLabel[?]] = RenamedLabel[ConnectorLabel[?], SchematicConnectorLabel[?], B] + type RenamedConnectorSchema = RenamedConnector[SchematicConnectorLabel[?]] + extension [L <: ConnectorLabel[?]](renamed: RenamedConnector[L]) { + def toAssignment: AssignedConnector = { + val parameters = freshIds(Set.empty, renamed.from.arity).map(SchematicPredicateLabel.apply[0]) + AssignedConnector.unsafe(renamed.from, LambdaConnector.unsafe(parameters, ConnectorFormula.unsafe(renamed.to, parameters.map(PredicateFormula.unsafe(_, Seq.empty))))) + } + } + + case class LambdaPredicate[N <: Arity] private (parameters: Seq[SchematicTermLabel[0]], body: Formula) extends LambdaDefinition[N, SchematicTermLabel[0], Formula] { + override type U = Term + override protected def instantiate(args: Seq[Term]): Formula = + instantiateFormulaSchemas(body, functions = parameters.zip(args).map { case (from, to) => AssignedFunction(from, LambdaFunction(_ => to)) }) + } + object LambdaPredicate { + def apply[N <: Arity](f: FillArgs[SchematicTermLabel[0], N] => Formula)(using v: ValueOf[N]): LambdaPredicate[N] = { + val n = v.value + val dummyVariable = SchematicTermLabel[0]("") + val taken = freeSchematicTermLabelsOf(fillTupleParameters(_ => dummyVariable, n, f)._2).map(_.id) + val (params, body) = fillTupleParameters(SchematicTermLabel.apply[0](_), n, f, taken) + new LambdaPredicate(params.toSeq, body) + } + def apply(f: Formula): LambdaPredicate[0] = LambdaPredicate(Seq.empty, f) + def unsafe(parameters: Seq[SchematicTermLabel[0]], body: Formula): LambdaPredicate[?] = new LambdaPredicate(parameters, body) + } + + case class AssignedPredicate private (schema: SchematicPredicateLabel[?], lambda: LambdaPredicate[?]) extends AssignedSchema[SchematicPredicateLabel[?], SchematicTermLabel[0]] { + override type L = LambdaPredicate[?] + } + object AssignedPredicate { + def apply[N <: Arity](schema: SchematicPredicateLabel[N], lambda: LambdaPredicate[N])(using v: ValueOf[N]): AssignedPredicate = new AssignedPredicate(schema, lambda) + def unsafe(schema: SchematicPredicateLabel[?], lambda: LambdaPredicate[?]): AssignedPredicate = new AssignedPredicate(schema, lambda) + } + given Conversion[Formula, LambdaPredicate[0]] = LambdaPredicate.apply + given labelToLambdaPredicate[T](using Conversion[T, Formula]): Conversion[T, LambdaPredicate[0]] = (t: T) => { + val formula: Formula = t + formula + } + // given lambdaToLambdaPredicate[N <: Arity](using ValueOf[N]): Conversion[FillArgs[SchematicTermLabel[0], N] => Formula, LambdaPredicate[N]] = LambdaPredicate.apply + given lambdaToLambdaPredicate1: Conversion[SchematicTermLabel[0] => Formula, LambdaPredicate[1]] = LambdaPredicate.apply + given lambdaToLambdaPredicate2: Conversion[((SchematicTermLabel[0], SchematicTermLabel[0])) => Formula, LambdaPredicate[2]] = LambdaPredicate.apply + + case class LambdaConnector[N <: Arity] private (parameters: Seq[SchematicPredicateLabel[0]], body: Formula) extends LambdaDefinition[N, SchematicPredicateLabel[0], Formula] { + override type U = Formula + override protected def instantiate(args: Seq[Formula]): Formula = + instantiateFormulaSchemas(body, predicates = parameters.zip(args).map { case (from, to) => AssignedPredicate(from, LambdaPredicate(_ => to)) }) + } + object LambdaConnector { + def apply[N <: Arity](f: FillArgs[SchematicPredicateLabel[0], N] => Formula)(using v: ValueOf[N]): LambdaConnector[N] = { + val n = v.value + val dummyVariable = SchematicPredicateLabel[0]("") + val taken = schematicPredicatesOf(fillTupleParameters(_ => dummyVariable, n, f)._2).map(_.id) + val (params, body) = fillTupleParameters(SchematicPredicateLabel.apply[0](_), n, f, taken) + new LambdaConnector(params.toSeq, body) + } + def apply(f: Formula): LambdaConnector[0] = LambdaConnector(Seq.empty, f) + def unsafe(parameters: Seq[SchematicPredicateLabel[0]], body: Formula): LambdaConnector[?] = new LambdaConnector(parameters, body) + } + given Conversion[Formula, LambdaConnector[0]] = LambdaConnector.apply + given labelToLambdaConnector[T](using Conversion[T, Formula]): Conversion[T, LambdaConnector[0]] = LambdaConnector.apply + given lambdaToLambdaConnector1: Conversion[SchematicPredicateLabel[0] => Formula, LambdaConnector[1]] = LambdaConnector.apply + given lambdaToLambdaConnector2: Conversion[((SchematicPredicateLabel[0], SchematicPredicateLabel[0])) => Formula, LambdaConnector[2]] = LambdaConnector.apply + + case class AssignedConnector private (schema: SchematicConnectorLabel[?], lambda: LambdaConnector[?]) extends AssignedSchema[SchematicConnectorLabel[?], SchematicPredicateLabel[0]] { + override type L = LambdaConnector[?] + } + object AssignedConnector { + def apply[N <: Arity](schema: SchematicConnectorLabel[N], lambda: LambdaConnector[N])(using v: ValueOf[N]): AssignedConnector = new AssignedConnector(schema, lambda) + def unsafe(schema: SchematicConnectorLabel[?], lambda: LambdaConnector[?]): AssignedConnector = new AssignedConnector(schema, lambda) + } + + object Assigned { + def apply[N <: Arity](schema: SchematicTermLabel[N], lambda: LambdaFunction[N])(using v: ValueOf[N]): AssignedFunction = AssignedFunction(schema, lambda) + def apply[N <: Arity](schema: SchematicPredicateLabel[N], lambda: LambdaPredicate[N])(using v: ValueOf[N]): AssignedPredicate = AssignedPredicate(schema, lambda) + def apply[N <: Arity](schema: SchematicConnectorLabel[N], lambda: LambdaConnector[N])(using v: ValueOf[N]): AssignedConnector = AssignedConnector(schema, lambda) + } + + def toKernel(lambda: LambdaPredicate[?]): lisa.kernel.fol.FOL.LambdaTermFormula = + lisa.kernel.fol.FOL.LambdaTermFormula( + lambda.parameters.map((label: SchematicTermLabel[0]) => { + val r = toKernel(label) + r + }), + lambda.body + ) + given Conversion[LambdaPredicate[?], lisa.kernel.fol.FOL.LambdaTermFormula] = toKernel + + def toKernel(lambda: LambdaConnector[?]): lisa.kernel.fol.FOL.LambdaFormulaFormula = + lisa.kernel.fol.FOL.LambdaFormulaFormula(lambda.parameters.map(toKernel), lambda.body) + given Conversion[LambdaConnector[?], lisa.kernel.fol.FOL.LambdaFormulaFormula] = toKernel + + /** + * A simple procedure to handle the fact that the kernel does not support schematic connectors. + * These will be converted into fresh constant connectors, which can be considered equivalent when used in some context (e.g. equivalence checking). + * @param formulas the formulas to be adapted + * @return new formulas that have been adapted + */ + def adaptConnectorSchemas(formulas: IndexedSeq[Formula]): IndexedSeq[Formula] = { + def recursive( + formula: Formula, + predicates: Set[SchematicPredicateLabel[?]], + translation: Map[ConnectorFormula, SchematicPredicateLabel[?]] + ): (Formula, Set[SchematicPredicateLabel[?]], Map[ConnectorFormula, SchematicPredicateLabel[?]]) = formula match { + case other: PredicateFormula => (other, predicates, translation) + case connector @ ConnectorFormula(label, args) => + label match { + case schematic: SchematicConnectorLabel[?] => + translation.get(connector) match { + case Some(predicate) => (PredicateFormula.unsafe(predicate, Seq.empty), predicates, translation) + case None => + val newId = freshId(predicates.map(_.id), schematic.id) + val newLabel = SchematicPredicateLabel[0](newId) + (PredicateFormula.unsafe(newLabel, Seq.empty), predicates + newLabel, translation + (connector -> newLabel)) + } + case _ => + val (newFormulas, newAllPredicates, newAllTranslation) = args.foldLeft((Seq.empty[Formula], predicates, translation)) { case ((acc, accPredicates, accTranslation), arg) => + val (newFormula, np, nt) = recursive(arg, accPredicates, accTranslation) + (acc :+ newFormula, np, nt) + } + (ConnectorFormula.unsafe(label, newFormulas), newAllPredicates, newAllTranslation) + } + case BinderFormula(label, bound, inner) => + val (newInner, newPredicates, newTranslation) = recursive(inner, predicates, translation) + (BinderFormula(label, bound, newInner), newPredicates, newTranslation) + } + val schematicPredicates = formulas.flatMap(schematicPredicatesOf).toSet + val (translatedFormulas, _, _) = formulas.foldLeft((IndexedSeq.empty[Formula], schematicPredicates, Map.empty[ConnectorFormula, SchematicPredicateLabel[?]])) { + case ((acc, taken, currentTranslation), formula) => + val (translatedFormula, newTaken, newTranslation) = recursive(formula, taken, currentTranslation) + (acc :+ translatedFormula, newTaken, newTranslation) + } + translatedFormulas + } + + /** + * @see [[lisa.kernel.fol.FOL.isSame]] + */ + def isSame(f1: Formula, f2: Formula): Boolean = + adaptConnectorSchemas(IndexedSeq(f1, f2)) match { + case IndexedSeq(af1, af2) => + lisa.kernel.fol.FOL.isSame(af1, af2) + case e => throw new MatchError(e) + } + + /** + * @see [[lisa.kernel.fol.FOL.Formula#freeVariables]] + */ + def freeVariablesOf(formula: Formula): Set[VariableLabel] = formula match { + case PredicateFormula(_, args) => args.flatMap(freeVariablesOf).toSet + case ConnectorFormula(_, args) => args.flatMap(freeVariablesOf).toSet + case BinderFormula(_, bound, inner) => freeVariablesOf(inner) - bound + } + + def freeTermLabelsOf(formula: Formula): Set[TermLabel[?]] = formula match { + case PredicateFormula(_, args) => args.flatMap(termLabelsOf).toSet + case ConnectorFormula(_, args) => args.flatMap(termLabelsOf).toSet + case BinderFormula(_, bound, inner) => termLabelsOf(inner) - bound + } + def termLabelsOf(formula: Formula): Set[TermLabel[?]] = formula match { + case PredicateFormula(_, args) => args.flatMap(termLabelsOf).toSet + case ConnectorFormula(_, args) => args.flatMap(termLabelsOf).toSet + case BinderFormula(_, bound, inner) => termLabelsOf(inner) + } + + /** + * @see [[lisa.kernel.fol.FOL.Formula#schematicFunctions]] + */ + def freeSchematicTermLabelsOf(formula: Formula): Set[SchematicTermLabel[?]] = + freeTermLabelsOf(formula).collect { case schematic: SchematicTermLabel[?] => schematic } + + def schematicTermLabelsOf(formula: Formula): Set[SchematicTermLabel[?]] = + termLabelsOf(formula).collect { case schematic: SchematicTermLabel[?] => schematic } + + def predicatesOf(formula: Formula): Set[PredicateLabel[?]] = formula match { + case PredicateFormula(label, _) => Set(label) + case ConnectorFormula(_, args) => args.flatMap(predicatesOf).toSet + case BinderFormula(_, _, inner) => predicatesOf(inner) + } + + /** + * @see [[lisa.kernel.fol.FOL.Formula#schematicPredicates]] + */ + def schematicPredicatesOf(formula: Formula): Set[SchematicPredicateLabel[?]] = + predicatesOf(formula).collect { case schematic: SchematicPredicateLabel[?] => schematic } + + def schematicConnectorsOf(formula: Formula): Set[SchematicConnectorLabel[?]] = formula match { + case PredicateFormula(_, _) => Set.empty + case ConnectorFormula(label, args) => + val set = label match { + case _: ConstantConnectorLabel[?] => Set.empty + case schematic: SchematicConnectorLabel[?] => Set(schematic) + } + set ++ args.flatMap(schematicConnectorsOf) + case BinderFormula(_, _, inner) => schematicConnectorsOf(inner) + } + + def declaredBoundVariablesOf(formula: Formula): Set[VariableLabel] = formula match { + case PredicateFormula(_, _) => Set.empty + case ConnectorFormula(_, args) => args.flatMap(declaredBoundVariablesOf).toSet + case BinderFormula(_, bound, inner) => declaredBoundVariablesOf(inner) + bound + } + + protected def isFormulaWellFormed(formula: Formula)(using ctx: Scope): Boolean = formula match { + case PredicateFormula(label, args) => args.forall(isWellFormed) + case ConnectorFormula(_: SchematicConnectorLabel[?], Seq()) => false // Use nullary predicates instead + case ConnectorFormula(label, args) => args.forall(isFormulaWellFormed) + case BinderFormula(_, bound, inner) => + !ctx.boundVariables.contains(bound) && isFormulaWellFormed(inner)(using ctx.copy(boundVariables = ctx.boundVariables + bound)) + } + + def isWellFormed(formula: Formula): Boolean = isFormulaWellFormed(formula)(using Scope()) + + def substituteVariables(formula: Formula, map: Map[VariableLabel, Term]): Formula = formula match { + case PredicateFormula(label, args) => PredicateFormula.unsafe(label, args.map(substituteVariables(_, map))) + case ConnectorFormula(label, args) => ConnectorFormula.unsafe(label, args.map(substituteVariables(_, map))) + case BinderFormula(label, bound, inner) => + val newSubst = map - bound + val fv = map.values.flatMap(freeVariablesOf).toSet + if (fv.contains(bound)) { + val newBoundVariable = VariableLabel(freshId(fv.map(_.id), bound.id)) + val newInner = substituteVariables(inner, Map(bound -> VariableTerm(newBoundVariable))) + BinderFormula(label, newBoundVariable, substituteVariables(newInner, newSubst)) + } else { + BinderFormula(label, bound, substituteVariables(inner, newSubst)) + } + } + + def instantiateFormulaSchemas( + formula: Formula, + functions: Seq[AssignedFunction] = Seq.empty, + predicates: Seq[AssignedPredicate] = Seq.empty, + connectors: Seq[AssignedConnector] = Seq.empty + ): Formula = { + val predicatesMap: Map[SchematicPredicateLabel[?], LambdaPredicate[?]] = predicates.map(i => i.schema -> i.lambda).toMap + val connectorsMap: Map[SchematicConnectorLabel[?], LambdaConnector[?]] = connectors.map(i => i.schema -> i.lambda).toMap + def instantiateInternal(formula: Formula): Formula = formula match { + case PredicateFormula(label, args) => + lazy val newArgs = args.map(instantiateTermSchemas(_, functions)) + label match { + case f: SchematicPredicateLabel[?] if predicatesMap.contains(f) => predicatesMap(f).unsafe(newArgs) + case _ => PredicateFormula.unsafe(label, newArgs) + } + case ConnectorFormula(label, args) => + lazy val newArgs = args.map(instantiateInternal) + label match { + case f: SchematicConnectorLabel[?] if connectorsMap.contains(f) => connectorsMap(f).unsafe(newArgs) + case _ => ConnectorFormula.unsafe(label, newArgs) + } + case BinderFormula(label, bound, inner) => + // TODO Requires testing. Match against substituteVariables + val newFuns = functions.filterNot(i => i.schema == bound) + val fv = newFuns.flatMap(f => freeVariablesOf(f.lambda.body)).toSet + if (fv.contains(bound)) { + val newBoundVariable = VariableLabel(freshId(fv.map(_.id), bound.id)) + val newInner = substituteVariables(inner, Map(bound -> VariableTerm(newBoundVariable))) + BinderFormula(label, newBoundVariable, instantiateFormulaSchemas(newInner, newFuns, predicates, connectors)) + } else { + BinderFormula(label, bound, instantiateFormulaSchemas(inner, newFuns, predicates, connectors)) + } + } + instantiateInternal(formula) + } + + def unsafeRenameVariables(formula: Formula, map: Map[VariableLabel, VariableLabel]): Formula = formula match { + case PredicateFormula(label, args) => + PredicateFormula.unsafe(label, args.map(unsafeRenameVariables(_, map))) + case ConnectorFormula(label, args) => + ConnectorFormula.unsafe(label, args.map(unsafeRenameVariables(_, map))) + case BinderFormula(label, bound, inner) => + val newBound = map.getOrElse(bound, bound) + BinderFormula(label, newBound, unsafeRenameVariables(inner, map)) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/fol/utils/TermUtils.scala b/lisa-front/src/main/scala/lisa/front/fol/utils/TermUtils.scala new file mode 100644 index 00000000..fab52e69 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/fol/utils/TermUtils.scala @@ -0,0 +1,109 @@ +package lisa.front.fol.utils + +import lisa.front.fol.conversions.to.TermConversionsTo +import lisa.front.fol.definitions.TermDefinitions +import lisa.front.fol.ops.CommonOps + +import scala.annotation.targetName + +trait TermUtils extends TermDefinitions with TermConversionsTo with CommonOps with CommonUtils { + + type RenamedFunction[B <: TermLabel[?]] = RenamedLabel[TermLabel[?], SchematicTermLabel[?], B] + type RenamedFunctionSchema = RenamedFunction[SchematicTermLabel[?]] + + extension [L <: TermLabel[?]](renamed: RenamedFunction[L]) { + def toAssignment: AssignedFunction = { + val parameters = freshIds(Set.empty, renamed.from.arity).map(SchematicTermLabel.apply[0]) + AssignedFunction.unsafe(renamed.from, LambdaFunction.unsafe(parameters, Term.unsafe(renamed.to, parameters.map(Term.unsafe(_, Seq.empty))))) + } + } + + def toKernel(lambda: LambdaFunction[?]): lisa.kernel.fol.FOL.LambdaTermTerm = + lisa.kernel.fol.FOL.LambdaTermTerm(lambda.parameters.map(toKernel), lambda.body) + given Conversion[LambdaFunction[?], lisa.kernel.fol.FOL.LambdaTermTerm] = toKernel + + case class LambdaFunction[N <: Arity] private (parameters: Seq[SchematicTermLabel[0]], body: Term) extends LambdaDefinition[N, SchematicTermLabel[0], Term] { + override type U = Term + override protected def instantiate(args: Seq[Term]): Term = + instantiateTermSchemas(body, parameters.zip(args).map { case (from, to) => AssignedFunction(from, LambdaFunction(_ => to)) }) + } + object LambdaFunction { + def apply[N <: Arity](f: FillArgs[SchematicTermLabel[0], N] => Term)(using v: ValueOf[N]): LambdaFunction[N] = { + val n = v.value + val dummyVariable = SchematicTermLabel[0]("") // Used to identify the existing free variables, doesn't matter if this name collides + val taken = schematicTermLabelsOf(fillTupleParameters(_ => dummyVariable, n, f)._2).map(_.id) + val (params, body) = fillTupleParameters(SchematicTermLabel.apply[0](_), n, f, taken) + new LambdaFunction(params.toSeq, body) + } + def apply(t: Term): LambdaFunction[0] = LambdaFunction(Seq.empty, t) + def unsafe(parameters: Seq[SchematicTermLabel[0]], body: Term): LambdaFunction[?] = new LambdaFunction(parameters, body) + } + + case class AssignedFunction private (schema: SchematicTermLabel[?], lambda: LambdaFunction[?]) extends AssignedSchema[SchematicTermLabel[?], SchematicTermLabel[0]] { + override type L = LambdaFunction[?] + } + object AssignedFunction { + def apply[N <: Arity](schema: SchematicTermLabel[N], lambda: LambdaFunction[N])(using v: ValueOf[N]): AssignedFunction = new AssignedFunction(schema, lambda) + def unsafe(schema: SchematicTermLabel[?], lambda: LambdaFunction[?]): AssignedFunction = new AssignedFunction(schema, lambda) + } + + given Conversion[Term, LambdaFunction[0]] = LambdaFunction.apply + given labelToLambdaFunction[T](using Conversion[T, Term]): Conversion[T, LambdaFunction[0]] = LambdaFunction.apply + given lambdaToLambdaFunction1: Conversion[SchematicTermLabel[0] => Term, LambdaFunction[1]] = LambdaFunction.apply + given lambdaToLambdaFunction2: Conversion[((SchematicTermLabel[0], SchematicTermLabel[0])) => Term, LambdaFunction[2]] = LambdaFunction.apply + + /** + * @see [[lisa.kernel.fol.FOL.isSame]] + */ + def isSame(t1: Term, t2: Term): Boolean = + lisa.kernel.fol.FOL.isSame(t1, t2) + + /** + * @see [[lisa.kernel.fol.FOL.Term#freeVariables]] + */ + def freeVariablesOf(term: Term): Set[VariableLabel] = term match { + case VariableTerm(label) => Set(label) + case Term(label, args) => args.flatMap(freeVariablesOf).toSet + } + + def termLabelsOf(term: Term): Set[TermLabel[?]] = term match { + case Term(label, args) => args.flatMap(termLabelsOf).toSet + label + } + + /** + * @see [[lisa.kernel.fol.FOL.Term#schematicFunctions]] + */ + def schematicTermLabelsOf(term: Term): Set[SchematicTermLabel[?]] = + termLabelsOf(term).collect { case schematic: SchematicTermLabel[?] => schematic } + + protected case class Scope(boundVariables: Set[VariableLabel] = Set.empty) + + /** + * Checks whether a term is well-formed. Currently returns <code>true</code> at all times. + * @param term the term to check + * @return if it is well-formed + */ + def isWellFormed(term: Term): Boolean = true + + def substituteVariables(term: Term, map: Map[VariableLabel, Term]): Term = term match { + case VariableTerm(label) => map.getOrElse(label, term) + case Term(label, args) => Term.unsafe(label, args.map(substituteVariables(_, map))) + } + + def instantiateTermSchemas(term: Term, functions: Seq[AssignedFunction]): Term = { + val map: Map[SchematicTermLabel[?], LambdaFunction[?]] = functions.map(i => i.schema -> i.lambda).toMap + def instantiateInternal(term: Term): Term = term match { + case Term(label, args) => + lazy val newArgs = args.map(instantiateInternal) + label match { + case f: SchematicTermLabel[?] if map.contains(f) => map(f).unsafe(newArgs) + case _ => Term.unsafe(label, newArgs) + } + } + instantiateInternal(term) + } + + def unsafeRenameVariables(term: Term, map: Map[VariableLabel, VariableLabel]): Term = + substituteVariables(term, map.view.mapValues(VariableTerm.apply).toMap) + +} diff --git a/lisa-front/src/main/scala/lisa/front/package.scala b/lisa-front/src/main/scala/lisa/front/package.scala new file mode 100644 index 00000000..e5b62a2a --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/package.scala @@ -0,0 +1,7 @@ +package lisa.front + +export lisa.front.fol.FOL.{_, given} +export lisa.front.proof.Proof.{_, given} +export lisa.front.parser.FrontReader.* +export lisa.front.parser.FrontMacro.{_, given} +export lisa.front.printer.FrontPositionedPrinter.* diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontLexer.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontLexer.scala new file mode 100644 index 00000000..dbdd7804 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontLexer.scala @@ -0,0 +1,174 @@ +package lisa.front.parser + +import lisa.front.parser.FrontReadingException.LexingException +import lisa.front.parser.FrontSymbols +import lisa.front.parser.FrontToken +import lisa.front.parser.FrontToken.* + +import scala.util.matching.Regex +import scala.util.parsing.combinator.RegexParsers + +private trait FrontLexer extends RegexParsers { + + override def skipWhitespace: Boolean = true + override protected val whiteSpace: Regex = "[ \t\f]+".r + + protected val S: FrontSymbols + + protected def initialIndentation: Parser[InitialIndentation] = positioned( + " *".r ^^ (str => InitialIndentation(str.length)) + ) + protected def newLine: Parser[NewLineWithIndentation] = positioned( + "\r?\n *".r ^^ (str => NewLineWithIndentation(str.count(_ == ' '))) + ) + + private val identifierPattern = "[a-zA-Z_][a-zA-Z0-9_]*" + + private def identifier: Parser[Identifier] = positioned( + identifierPattern.r ^^ (str => Identifier(str)) + ) + private def schematicIdentifier: Parser[SchematicIdentifier] = positioned( + (raw"\${S.QuestionMark}$identifierPattern").r ^^ (str => SchematicIdentifier(str.tail)) + ) + private def schematicConnectorIdentifier: Parser[SchematicConnectorIdentifier] = positioned( + (raw"\${S.QuestionMark}\${S.QuestionMark}$identifierPattern").r ^^ (str => SchematicConnectorIdentifier(str.tail.tail)) + ) + + private def keywords: Parser[FrontToken] = positioned( + S.Forall ^^^ Forall() + | S.ExistsOne ^^^ ExistsOne() + | S.Exists ^^^ Exists() + | S.Iff ^^^ Iff() + | S.Implies ^^^ Implies() + | S.Or ^^^ Or() + | S.And ^^^ And() + | S.Exclamation ^^^ Not() + | S.Turnstile ^^^ Turnstile() + | S.Ellipsis ^^^ Ellipsis() + | S.Subset ^^^ Subset() + | S.Membership ^^^ Membership() + | S.EmptySet ^^^ EmptySet() + | S.Equal ^^^ Equal() + | S.Tilde ^^^ SameCardinality() + | S.Backslash ^^^ LocalBinder() + | S.CurlyBracketOpen ^^^ CurlyBracketOpen() + | S.CurlyBracketClose ^^^ CurlyBracketClose() + | S.ParenthesisOpen ^^^ ParenthesisOpen() + | S.ParenthesisClose ^^^ ParenthesisClose() + | S.Dot ^^^ Dot() + | S.Comma ^^^ Comma() + | S.Semicolon ^^^ Semicolon() + ) + + protected final def standardTokens: Parser[FrontToken] = + keywords | newLine | schematicConnectorIdentifier | schematicIdentifier | identifier + + // Order matters! Special keywords should be matched before identifiers + protected def tokens: Parser[Seq[FrontToken]] = + phrase(initialIndentation ~ rep(standardTokens) ^^ { case h ~ t => h +: t }) + + final def apply(str: String): Seq[FrontToken] = + parse(tokens, str) match { + case e @ NoSuccess(msg, next) => throw LexingException(e.toString, next.pos) + case Success(result, next) => result + case e => throw new MatchError(e) + } +} + +/** + * The lexer converts a sequence of characters into low-level tokens ([[FrontToken]]), for instance identifiers, symbols, separators. + */ +object FrontLexer { + + private trait FrontLexerExtended extends FrontLexer { + private val kernelRuleIdentifiers = KernelRuleIdentifiers(S) + import kernelRuleIdentifiers.* + + private def integerLiteral: Parser[IntegerLiteral] = positioned( + "0|-?[1-9][0-9]*".r ^^ { str => IntegerLiteral(str.toInt) } + ) + + private def rules: Parser[RuleName] = + positioned( + (Hypothesis + | Cut + | Rewrite + | Weakening + | LeftAnd + | RightAnd + | LeftOr + | RightOr + | LeftImplies + | RightImplies + | LeftIff + | RightIff + | LeftNot + | RightNot + | LeftForall + | RightForall + | LeftExists + | RightExists + | LeftExistsOne + | RightExistsOne + | LeftRefl + | RightRefl + | LeftSubstEq + | RightSubstEq + | LeftSubstIff + | RightSubstIff + | FunInstantiation + | PredInstantiation + | SubproofHidden // Must come before `SubproofShown` + | SubproofShown // + | Import) ^^ RuleName.apply + ) + + override protected def tokens: Parser[Seq[FrontToken]] = + phrase(initialIndentation ~ rep(rules | integerLiteral | (S.SquareBracketOpen ^^^ SquareBracketOpen()) | (S.SquareBracketClose ^^^ SquareBracketClose()) | standardTokens) ^^ { case h ~ t => + h +: t + }) + } + + private trait FrontLexerAscii extends FrontLexer { + override protected val S: FrontSymbols = FrontSymbols.FrontAsciiSymbols + } + private object FrontLexerStandardAscii extends FrontLexerAscii + + private trait FrontLexerUnicode extends FrontLexer { + override protected val S: FrontSymbols = FrontSymbols.FrontUnicodeSymbols + } + private object FrontLexerStandardUnicode extends FrontLexerUnicode + private object FrontLexerExtendedUnicode extends FrontLexerUnicode with FrontLexerExtended // Order of inheritance matter + + private def postProcessor(lines: Boolean, indentation: Boolean)(tokens: Seq[FrontToken]): Seq[FrontToken] = { + val tokensWithEnd = tokens :+ End() + tokensWithEnd.flatMap { + case token @ NewLineWithIndentation(n) => + val tokenLine = NewLine() + tokenLine.pos = token.pos + val tokenIndentation = Indentation(n) + tokenIndentation.pos = token.pos + if (indentation) + Seq(tokenLine, tokenIndentation) + else if (lines) + Seq(tokenLine) + else + Seq.empty + case token @ InitialIndentation(n) => + val newToken = Indentation(n) + newToken.pos = token.pos + if (indentation) Seq(newToken) else Seq.empty + case other => Seq(other) + } + } + + def lexingAscii(str: String, lines: Boolean = false, indentation: Boolean = false): Seq[FrontToken] = + postProcessor(lines, indentation)(FrontLexerStandardAscii(str)) + + def lexingUnicode(str: String, lines: Boolean = false, indentation: Boolean = false): Seq[FrontToken] = + postProcessor(lines, indentation)(FrontLexerStandardUnicode(str)) + + def lexingExtendedUnicode(str: String): Seq[FrontToken] = + postProcessor(lines = true, indentation = true)(FrontLexerExtendedUnicode(str)) + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontMacro.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontMacro.scala new file mode 100644 index 00000000..dbca0a4a --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontMacro.scala @@ -0,0 +1,431 @@ +package lisa.front.parser + +import lisa.front.fol.FOL.* +import lisa.front.printer.FrontPositionedPrinter +import lisa.front.proof.Proof.* + +// Note: on Intellij you may want to disable syntax highlighting for this file +// ("File Types" => "Text" => "Ignored Files and Folders", add "FrontMacro.scala") + +/** + * Macros to enable compile-time string interpolation. For instance: + * <pre> + * val f: Formula = ... + * formula"|- (a /\ $f); ?c" + * </pre> + */ +object FrontMacro { + import scala.quoted.* + + // https://github.com/lampepfl/dotty/issues/8577#issuecomment-1014729373 + + extension (inline sc: StringContext) { + transparent inline def term: Any = ${ SIParts.scMacro[TermParts]('sc) } + transparent inline def formula: Any = ${ SIParts.scMacro[FormulaParts]('sc) } + transparent inline def sequent: Any = ${ SIParts.scMacro[SequentParts]('sc) } + transparent inline def partial: Any = ${ SIParts.scMacro[PartialSequentParts]('sc) } + } + + class TermParts[P <: Tuple](parts: P) { + transparent inline def apply(inline args: Any*): Term = ${ termApplyMacro('parts, 'args) } + // transparent inline def unapplySeq(inline arg: Any): Option[Seq[Any]] = ${ termUnapplyMacro('parts, 'arg) } + } + class FormulaParts[P <: Tuple](parts: P) { + transparent inline def apply(inline args: Any*): Formula = ${ formulaApplyMacro('parts, 'args) } + } + class SequentParts[P <: Tuple](parts: P) { + transparent inline def apply(inline args: Any*): Sequent = ${ sequentApplyMacro('parts, 'args) } + } + class PartialSequentParts[P <: Tuple](parts: P) { + transparent inline def apply(inline args: Any*): PartialSequent = ${ partialSequentApplyMacro('parts, 'args) } + } + + trait SIParts[P <: Tuple](parts: P) + object SIParts { + def scMacro[SI[_ <: Tuple]](sc: Expr[StringContext])(using Quotes, Type[SI]): Expr[Any] = { + import quotes.reflect.* + val args = sc match { + case '{ StringContext(${ Varargs(args) }*) } => args + } + val tplExpr = Expr.ofTupleFromSeq(args) + val tplTpe = tplExpr.asTerm.tpe + val siTpe = TypeRepr.of[SI[Tuple]].asInstanceOf[TypeRepr & Matchable] match { + case AppliedType(siTpe, _) => siTpe + } + val siSym = siTpe.typeSymbol + val siTree = + New(TypeTree.of[SI[Tuple]]) + .select(siSym.primaryConstructor) + .appliedToType(tplTpe) + .appliedTo(tplExpr.asTerm) + siTree.asExpr + } + } + + /*private def termUnapplyMacro[P <: Tuple](parts: Expr[P], arg: Expr[Any])(using Quotes, Type[P]): Expr[Option[Seq[Any]]] = { + '{ None: Option[Seq[Term]] } + }*/ + + enum Variable { + val expr: Expr[Any] + case FunctionLabelVariable(expr: Expr[TermLabel[?]], placeholder: SchematicTermLabel[?]) + case PredicateLabelVariable(expr: Expr[PredicateLabel[?]], placeholder: SchematicPredicateLabel[?]) + case ConnectorLabelVariable(expr: Expr[ConnectorLabel[?]], placeholder: SchematicConnectorLabel[?]) + case VariableLabelVariable(expr: Expr[VariableLabel], placeholder: VariableLabel) + case TermVariable(expr: Expr[Term], placeholder: SchematicTermLabel[0]) + case FormulaVariable(expr: Expr[Formula], placeholder: SchematicPredicateLabel[0]) + } + import Variable.* + + case class Interpolator(idsAndVariables: Seq[(String, Variable)], tokens: Seq[FrontToken]) { + val variables: Seq[Variable] = idsAndVariables.map { case (_, variable) => variable } + val map: Map[String, Variable] = idsAndVariables.toMap + } + + private def toTokens[P <: Tuple](parts: Expr[P], args: Expr[Seq[Any]])(using Quotes, Type[P]): Interpolator = { + import quotes.reflect.{Term as _, *} + + // throw new Error(s"illegal interpolation variable: ${TypeTree.of[other]}") + // TypeTree[ConstantType(Constant({))] + def evaluateParts[Q <: Tuple](scrutiny: Type[Q], acc: Seq[String]): Seq[String] = scrutiny match { + case '[EmptyTuple] => acc + case '[head *: tail] => + val string = TypeTree.of[head].tpe.asInstanceOf[TypeRepr & Matchable] match { + case ConstantType(cst) => cst.value.asInstanceOf[String] // Should always match and succeed + } + evaluateParts(Type.of[tail], string +: acc) + } + // `Type.of[P]` is equivalent to `summon[Type[P]]` + val evaluatedParts: Seq[String] = evaluateParts(Type.of[P], Seq.empty).reverse + + val partsTokens: Seq[Seq[FrontToken]] = evaluatedParts.map(FrontLexer.lexingAscii(_)).map(_.init) + val takenNames: Set[String] = partsTokens.flatten.collect { + case FrontToken.Identifier(id) => id + case FrontToken.SchematicIdentifier(id) => id + case FrontToken.SchematicConnectorIdentifier(id) => id + }.toSet + + val argsSeq: Seq[Expr[Any]] = args match { + case Varargs(es) => es + } + + // TODO raise warning when using infix notation + + def resolveArity[N <: Arity](expr: Expr[LabelType & WithArityType[N]])(using Type[N]): Int = + TypeTree.of[N].tpe.asInstanceOf[TypeRepr & Matchable] match { + case ConstantType(cst) => cst.value.asInstanceOf[Int] + case _ => report.errorAndAbort(s"loosely typed label variable, the arity must be known at compile time: ${Type.show[N]}", expr) + } + + val idsAndVariables: Seq[(String, Variable)] = argsSeq.zipWithIndex + .foldLeft((Seq.empty[(String, Variable)], Map.empty[Any, String], takenNames)) { case ((acc, hashmap, taken), (expr, i)) => + val id = hashmap.getOrElse( + expr.asTerm.toString, { // FIXME: `asTerm.toString` is not a safe way to check whether two expressions are `=:=` + val base = s"x$i" + if (taken.contains(base)) freshId(taken, base) else base + } + ) + val variable = expr match { + case '{ $label: TermLabel[n] } => FunctionLabelVariable(label, SchematicTermLabel.unsafe(id, resolveArity(label))) + case '{ $label: PredicateLabel[n] } => PredicateLabelVariable(label, SchematicPredicateLabel.unsafe(id, resolveArity(label))) + case '{ $label: ConnectorLabel[n] } => ConnectorLabelVariable(label, SchematicConnectorLabel.unsafe(id, resolveArity(label))) + case '{ $label: VariableLabel } => VariableLabelVariable(label, VariableLabel(id)) + case '{ $term: Term } => TermVariable(term, SchematicTermLabel[0](id)) + case '{ $formula: Formula } => FormulaVariable(formula, SchematicPredicateLabel[0](id)) + case '{ $t: q } => report.errorAndAbort(s"unsupported variable type: ${Type.show[q]}", expr) + } + ((id, variable) +: acc, hashmap + (expr.asTerm.toString -> id), taken + id) + } + ._1 + .reverse + + val variables = idsAndVariables.map { case (_, variable) => variable } + + val variablesTokens: Seq[FrontToken] = variables.map { + case FunctionLabelVariable(_, placeholder) => FrontToken.SchematicIdentifier(placeholder.id) + case PredicateLabelVariable(_, placeholder) => FrontToken.SchematicIdentifier(placeholder.id) + case ConnectorLabelVariable(_, placeholder) => FrontToken.SchematicConnectorIdentifier(placeholder.id) + case VariableLabelVariable(_, placeholder) => FrontToken.Identifier(placeholder.id) + case TermVariable(_, placeholder) => FrontToken.SchematicIdentifier(placeholder.id) + case FormulaVariable(_, placeholder) => FrontToken.SchematicIdentifier(placeholder.id) + } + + val tokens: Seq[FrontToken] = partsTokens.head ++ variablesTokens.zip(partsTokens.tail).flatMap { case (v, p) => v +: p } :+ FrontToken.End() + + Interpolator(idsAndVariables, tokens) + } + + private def getRenaming(variables: Seq[Variable])(using Quotes): Expr[ + ( + Seq[AssignedFunction], + Seq[AssignedPredicate], + Seq[AssignedConnector], + Map[VariableLabel, VariableLabel] + ) + ] = { + import LiftFOL.{_, given} + + def substMap[T, U](seq: Seq[(Expr[T], Expr[U])])(using Quotes, Type[T], Type[U]): Expr[Map[T, U]] = { + val list: Seq[Expr[(T, U)]] = seq.map { case (k, v) => + '{ $k -> $v } + } + '{ ${ liftSeq(list) }.toMap } + } + + val functionsMap: Expr[Seq[AssignedFunction]] = liftSeq(variables.collect { case FunctionLabelVariable(label, placeholder) => + '{ RenamedLabel.unsafe(${ Expr(placeholder) }, $label).toAssignment } + }) + val predicatesMap: Expr[Seq[AssignedPredicate]] = liftSeq(variables.collect { case PredicateLabelVariable(label, placeholder) => + '{ RenamedLabel.unsafe(${ Expr(placeholder) }, $label).toAssignment } + }) + val connectorsMap: Expr[Seq[AssignedConnector]] = liftSeq(variables.collect { case ConnectorLabelVariable(label, placeholder) => + '{ RenamedLabel.unsafe(${ Expr(placeholder) }, $label).toAssignment } + }) + val variablesMap: Expr[Map[VariableLabel, VariableLabel]] = substMap(variables.collect { case VariableLabelVariable(label, placeholder) => + Expr(placeholder) -> label + }) + + val termsMap: Expr[Seq[AssignedFunction]] = liftSeq(variables.collect { case TermVariable(term, placeholder) => + '{ AssignedFunction.unsafe(${ Expr(placeholder)(using toExprFunction0) }, LambdaFunction.unsafe(Seq.empty, $term)) } + }) + val formulasMap: Expr[Seq[AssignedPredicate]] = liftSeq(variables.collect { case FormulaVariable(formula, placeholder) => + '{ AssignedPredicate.unsafe(${ Expr(placeholder)(using toExprPredicate0) }, LambdaPredicate.unsafe(Seq.empty, $formula)) } + }) + + '{ ($functionsMap ++ $termsMap, $predicatesMap ++ $formulasMap, $connectorsMap, $variablesMap) } + } + + def unsafeFixPointTermInstantiate(term: Term, functions: Seq[AssignedFunction], map: Map[VariableLabel, VariableLabel]): Term = { + val next = instantiateTermSchemas(unsafeRenameVariables(term, map), functions) + if (next == term) term else unsafeFixPointTermInstantiate(next, functions, map) + } + + def unsafeFixPointFormulaInstantiate( + formula: Formula, + functions: Seq[AssignedFunction], + predicates: Seq[AssignedPredicate], + connectors: Seq[AssignedConnector], + map: Map[VariableLabel, VariableLabel] + ): Formula = { + val next = instantiateFormulaSchemas(unsafeRenameVariables(formula, map), functions, predicates, connectors) + if (next == formula) formula else unsafeFixPointFormulaInstantiate(next, functions, predicates, connectors, map) + } + + private def typeCheck( + interpolator: Interpolator, + functions: Set[TermLabel[?]], + predicates: Set[PredicateLabel[?]], + connectors: Set[SchematicConnectorLabel[?]], + variables: Set[VariableLabel] + )(using Quotes): Unit = { + import quotes.reflect.* + + def reportArityMismatch(expr: Expr[?], expected: Int, actual: Int): Nothing = + report.errorAndAbort(s"arity mismatch: variable label expects $expected arguments but you provided $actual", expr) + + // Either function or predicate + functions.flatMap(f => interpolator.map.get(f.id).map(f -> _)).foreach { case (f, variable) => + variable match { + case FunctionLabelVariable(label, placeholder) => + if (f.arity != placeholder.arity) { + reportArityMismatch(label, placeholder.arity, f.arity) + } + case TermVariable(label, placeholder) => + if (f.arity != placeholder.arity) { + report.errorAndAbort("variable term does not expect any arguments", label) + } + case VariableLabelVariable(label, _) => report.errorAndAbort("undeclared free variable", label) + case other => report.errorAndAbort("expected term, got formula", other.expr) + } + } + // Ditto + predicates.flatMap(f => interpolator.map.get(f.id).map(f -> _)).foreach { case (f, variable) => + variable match { + case PredicateLabelVariable(label, placeholder) => + if (f.arity != placeholder.arity) { + reportArityMismatch(label, placeholder.arity, f.arity) + } + case FormulaVariable(label, placeholder) => + if (f.arity != placeholder.arity) { + report.errorAndAbort("variable formula does not expect any arguments", label) + } + case VariableLabelVariable(label, _) => report.errorAndAbort("undeclared free variable", label) + case other => report.errorAndAbort("expected formula, got term", other.expr) + } + } + // Connectors are disjoint from anything else + connectors.flatMap(f => interpolator.map.get(f.id).map(f -> _)).foreach { case (f, variable) => + variable match { + case ConnectorLabelVariable(label, placeholder) => + if (f.arity != placeholder.arity) { + reportArityMismatch(label, placeholder.arity, f.arity) + } + case other => throw new Error // Shouldn't happen + } + } + // Variable are also apart + variables.flatMap(f => interpolator.map.get(f.id).map(f -> _)).foreach { case (f, variable) => + variable match { + case VariableLabelVariable(_, _) => () + case other => report.errorAndAbort("expected term, got formula", other.expr) + } + } + } + + private def termApplyMacro[P <: Tuple](parts: Expr[P], args: Expr[Seq[Any]])(using Quotes, Type[P]): Expr[Term] = { + import quotes.reflect.* + import LiftFOL.{_, given} + + val interpolator = toTokens(parts, args) + val resolved = FrontResolver.resolveTerm(FrontParser.parseTopTermOrFormula(interpolator.tokens)) + + typeCheck(interpolator, termLabelsOf(resolved), Set.empty, Set.empty, freeVariablesOf(resolved)) + + '{ + val (functionsMap, _, _, variablesMap) = ${ getRenaming(interpolator.variables) } + unsafeFixPointTermInstantiate(${ Expr(resolved) }, functionsMap, variablesMap) + } + } + private def formulaApplyMacro[P <: Tuple](parts: Expr[P], args: Expr[Seq[Any]])(using Quotes, Type[P]): Expr[Formula] = { + import quotes.reflect.* + import LiftFOL.{_, given} + + val interpolator = toTokens(parts, args) + val resolved = FrontResolver.resolveFormula(FrontParser.parseTopTermOrFormula(interpolator.tokens)) + + typeCheck(interpolator, termLabelsOf(resolved), predicatesOf(resolved), schematicConnectorsOf(resolved), freeVariablesOf(resolved)) + + '{ + val (functionsMap, predicatesMap, connectorsMap, variablesMap) = ${ getRenaming(interpolator.variables) } + unsafeFixPointFormulaInstantiate(${ Expr(resolved) }, functionsMap, predicatesMap, connectorsMap, variablesMap) + } + } + private def sequentApplyMacro[P <: Tuple](parts: Expr[P], args: Expr[Seq[Any]])(using Quotes, Type[P]): Expr[Sequent] = { + import quotes.reflect.* + import LiftFOL.{_, given} + + val interpolator = toTokens(parts, args) + val resolved = FrontResolver.resolveSequent(FrontParser.parseSequent(interpolator.tokens)) + + typeCheck(interpolator, functionsOfSequent(resolved), predicatesOfSequent(resolved), schematicConnectorsOfSequent(resolved), freeVariablesOfSequent(resolved)) + + '{ + val (functionsMap, predicatesMap, connectorsMap, variablesMap) = ${ getRenaming(interpolator.variables) } + def rename(formula: Formula): Formula = + unsafeFixPointFormulaInstantiate(formula, functionsMap, predicatesMap, connectorsMap, variablesMap) + Sequent(${ liftSeq(resolved.left.toSeq.map(Expr.apply)) }.toIndexedSeq.map(rename), ${ liftSeq(resolved.right.toSeq.map(Expr.apply)) }.toIndexedSeq.map(rename)) + } + } + private def partialSequentApplyMacro[P <: Tuple](parts: Expr[P], args: Expr[Seq[Any]])(using Quotes, Type[P]): Expr[PartialSequent] = { + import quotes.reflect.* + import LiftFOL.{_, given} + + val interpolator = toTokens(parts, args) + val resolved = FrontResolver.resolvePartialSequent(FrontParser.parsePartialSequent(interpolator.tokens)) + + typeCheck(interpolator, functionsOfSequent(resolved), predicatesOfSequent(resolved), schematicConnectorsOfSequent(resolved), freeVariablesOfSequent(resolved)) + + '{ + val (functionsMap, predicatesMap, connectorsMap, variablesMap) = ${ getRenaming(interpolator.variables) } + def rename(formula: Formula): Formula = + unsafeFixPointFormulaInstantiate(formula, functionsMap, predicatesMap, connectorsMap, variablesMap) + PartialSequent( + ${ liftSeq(resolved.left.toSeq.map(Expr.apply)) }.toIndexedSeq.map(rename), + ${ liftSeq(resolved.right.toSeq.map(Expr.apply)) }.toIndexedSeq.map(rename), + ${ Expr(resolved.partialLeft) }, + ${ Expr(resolved.partialRight) } + ) + } + } + + private object LiftFOL { + def liftSeq[T](seq: Seq[Expr[T]])(using Quotes, Type[T]): Expr[Seq[T]] = + seq.foldRight('{ Seq.empty[T] })((e, acc) => '{ $e +: $acc }) + + // TODO support the generic type conversion (it's harder than it looks) + + given ToExpr[SchematicTermLabel[?]] with { + def apply(f: SchematicTermLabel[?])(using Quotes): Expr[SchematicTermLabel[?]] = + '{ SchematicTermLabel.unsafe(${ Expr(f.id) }, ${ Expr(f.arity.asInstanceOf[Int]) }) } + } + given ToExpr[ConstantFunctionLabel[?]] with { + def apply(f: ConstantFunctionLabel[?])(using Quotes): Expr[ConstantFunctionLabel[?]] = + '{ ConstantFunctionLabel.unsafe(${ Expr(f.id) }, ${ Expr(f.arity.asInstanceOf[Int]) }) } + } + given ToExpr[SchematicPredicateLabel[?]] with { + def apply(f: SchematicPredicateLabel[?])(using Quotes) = + '{ SchematicPredicateLabel.unsafe(${ Expr(f.id) }, ${ Expr(f.arity.asInstanceOf[Int]) }) } + } + given ToExpr[ConstantPredicateLabel[?]] with { + def apply(f: ConstantPredicateLabel[?])(using Quotes): Expr[ConstantPredicateLabel[?]] = + '{ ConstantPredicateLabel.unsafe(${ Expr(f.id) }, ${ Expr(f.arity.asInstanceOf[Int]) }) } + } + given ToExpr[SchematicConnectorLabel[?]] with { + def apply(f: SchematicConnectorLabel[?])(using Quotes) = + '{ SchematicConnectorLabel.unsafe(${ Expr(f.id) }, ${ Expr(f.arity.asInstanceOf[Int]) }) } + } + given ToExpr[VariableLabel] with { + def apply(l: VariableLabel)(using Quotes) = + '{ VariableLabel(${ Expr(l.id) }) } + } + given ToExpr[BinderLabel] with { + def apply(l: BinderLabel)(using Quotes) = + l match { + case `forall` => '{ forall } + case `exists` => '{ exists } + case `existsOne` => '{ existsOne } + } + } + + // FIXME "hack" otherwise the two givens would clash + val toExprFunction0: ToExpr[SchematicTermLabel[0]] = new { + def apply(f: SchematicTermLabel[0])(using Quotes): Expr[SchematicTermLabel[0]] = + '{ SchematicTermLabel[0](${ Expr(f.id) }) } + } + val toExprPredicate0: ToExpr[SchematicPredicateLabel[0]] = new { + def apply(f: SchematicPredicateLabel[0])(using Quotes): Expr[SchematicPredicateLabel[0]] = + '{ SchematicPredicateLabel[0](${ Expr(f.id) }) } + } + + given ToExpr[TermLabel[?]] with { + def apply(f: TermLabel[?])(using Quotes): Expr[TermLabel[?]] = f match { + case constant: ConstantFunctionLabel[?] => Expr(constant)(using summon[ToExpr[ConstantFunctionLabel[?]]]) + case schematic: SchematicTermLabel[?] => Expr(schematic)(using summon[ToExpr[SchematicTermLabel[?]]]) + } + } + given ToExpr[PredicateLabel[?]] with { + def apply(f: PredicateLabel[?])(using Quotes): Expr[PredicateLabel[?]] = f match { + case constant: ConstantPredicateLabel[?] => Expr(constant)(using summon[ToExpr[ConstantPredicateLabel[?]]]) + case schematic: SchematicPredicateLabel[?] => Expr(schematic)(using summon[ToExpr[SchematicPredicateLabel[?]]]) + } + } + given ToExpr[ConnectorLabel[?]] with { + def apply(f: ConnectorLabel[?])(using Quotes): Expr[ConnectorLabel[?]] = f match { + case constant: ConstantConnectorLabel[?] => + constant match { + case `neg` => '{ neg } + case `implies` => '{ implies } + case `iff` => '{ iff } + case `and` => '{ and } + case `or` => '{ or } + } + case schematic: SchematicConnectorLabel[?] => Expr(schematic)(using summon[ToExpr[SchematicConnectorLabel[?]]]) + } + } + + given ToExpr[Term] with { + def apply(t: Term)(using Quotes): Expr[Term] = t match { + case VariableTerm(label) => '{ VariableTerm(${ Expr(label) }) } + case Term(label, args) => '{ Term.unsafe(${ Expr(label) }, ${ liftSeq(args.map(Expr.apply(_))) }) } + } + } + given ToExpr[Formula] with { + def apply(f: Formula)(using Quotes): Expr[Formula] = f match { + case PredicateFormula(label, args) => '{ PredicateFormula.unsafe(${ Expr(label) }, ${ liftSeq(args.map(Expr.apply(_))) }) } + case ConnectorFormula(label, args) => '{ ConnectorFormula.unsafe(${ Expr(label) }, ${ liftSeq(args.map(Expr.apply(_))) }) } + case BinderFormula(label, bound, inner) => '{ BinderFormula(${ Expr(label) }, ${ Expr(bound) }, ${ Expr(inner) }) } + } + } + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontParsed.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontParsed.scala new file mode 100644 index 00000000..02b33f03 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontParsed.scala @@ -0,0 +1,68 @@ +package lisa.front.parser + +import scala.util.parsing.input.Position +import scala.util.parsing.input.Positional + +sealed abstract class FrontParsed extends Positional + +/** + * The intermediate representation for first order logic and sequents. + */ +private[parser] object FrontParsed { + + case class ParsedSequent(freeVariables: Seq[String], left: Seq[ParsedTermOrFormula], right: Seq[ParsedTermOrFormula]) extends FrontParsed + case class ParsedPartialSequent(freeVariables: Seq[String], left: Seq[ParsedTermOrFormula], right: Seq[ParsedTermOrFormula], partialLeft: Boolean, partialRight: Boolean) extends FrontParsed + + case class ParsedTopTermOrFormula(freeVariables: Seq[String], termOrFormula: ParsedTermOrFormula) extends FrontParsed + + sealed abstract class ParsedTermOrFormula extends FrontParsed + + sealed abstract class ParsedName extends ParsedTermOrFormula { + val identifier: String + } + case class ParsedConstant(identifier: String) extends ParsedName + case class ParsedSchema(identifier: String, connector: Boolean) extends ParsedName + + case class ParsedApplication(name: ParsedName, args: Seq[ParsedTermOrFormula]) extends ParsedTermOrFormula + + sealed abstract class ParsedBinaryOperator extends ParsedTermOrFormula { + val left: ParsedTermOrFormula + val right: ParsedTermOrFormula + } + case class ParsedAnd(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + case class ParsedOr(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + case class ParsedImplies(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + case class ParsedIff(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + + case class ParsedEqual(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + case class ParsedMembership(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + case class ParsedSubset(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + case class ParsedSameCardinality(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedBinaryOperator + + case class ParsedPower(termOrFormula: ParsedTermOrFormula) extends ParsedTermOrFormula + case class ParsedUnion(termOrFormula: ParsedTermOrFormula) extends ParsedTermOrFormula + + case class ParsedNot(termOrFormula: ParsedTermOrFormula) extends ParsedTermOrFormula + + sealed abstract class ParsedProduct extends ParsedTermOrFormula { + val left: ParsedTermOrFormula + val right: ParsedTermOrFormula + } + case class ParsedOrderedPair(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedProduct + case class ParsedSet2(left: ParsedTermOrFormula, right: ParsedTermOrFormula) extends ParsedProduct + // case class ParsedSet1(termOrFormula: ParsedTermOrFormula) extends ParsedTermOrFormula + case class ParsedSet0() extends ParsedTermOrFormula + + sealed abstract class ParsedBinder extends ParsedTermOrFormula { + val bound: Seq[String] + val termOrFormula: ParsedTermOrFormula + } + case class ParsedForall(bound: Seq[String], termOrFormula: ParsedTermOrFormula) extends ParsedBinder + case class ParsedExists(bound: Seq[String], termOrFormula: ParsedTermOrFormula) extends ParsedBinder + case class ParsedExistsOne(bound: Seq[String], termOrFormula: ParsedTermOrFormula) extends ParsedBinder + + case class ParsedProofStep(stepPosition: Position, indentation: Int, line: Int, ruleName: String, premises: Seq[Int], conclusion: ParsedSequent, parameters: Seq[ParsedTopTermOrFormula]) + extends FrontParsed + case class ParsedProof(steps: IndexedSeq[ParsedProofStep]) extends FrontParsed + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontParser.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontParser.scala new file mode 100644 index 00000000..de771385 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontParser.scala @@ -0,0 +1,161 @@ +package lisa.front.parser + +import lisa.front.parser.FrontParsed +import lisa.front.parser.FrontParsed.* +import lisa.front.parser.FrontReadingException.ParsingException +import lisa.front.parser.FrontToken +import lisa.front.parser.FrontToken.* + +import scala.util.parsing.combinator.Parsers + +/** + * The parser convert low-level tokens into the [[FrontParsed]] intermediate representation. + */ +private[parser] object FrontParser extends Parsers { + + override type Elem = FrontToken + + private def identifier: Parser[Identifier] = + positioned(accept("identifier", { case id: Identifier => id })) + private def schematicIdentifier: Parser[SchematicIdentifier] = + positioned(accept("schematic identifier", { case id: SchematicIdentifier => id })) + private def schematicConnectorIdentifier: Parser[SchematicConnectorIdentifier] = + positioned(accept("schematic connector identifier", { case id: SchematicConnectorIdentifier => id })) + + private def integerLiteral: Parser[IntegerLiteral] = + positioned(accept("integer literal", { case lit: IntegerLiteral => lit })) + private def ruleName: Parser[RuleName] = + positioned(accept("rule", { case rule: RuleName => rule })) + + private def indentation: Parser[Indentation] = + positioned(accept("indentation", { case indentation: Indentation => indentation })) + private def newLine: Parser[NewLine] = + positioned(accept("new line", { case line: NewLine => line })) + + private def identifierOrSchematic: Parser[Identifier | SchematicIdentifier | SchematicConnectorIdentifier] = + positioned((identifier: Parser[Identifier | SchematicIdentifier | SchematicConnectorIdentifier]) | schematicIdentifier | schematicConnectorIdentifier) + + private def binder: Parser[ParsedTermOrFormula] = positioned( + (Forall() ^^^ ParsedForall.apply | Exists() ^^^ ParsedExists.apply | ExistsOne() ^^^ ParsedExistsOne.apply) ~ + rep1sep(identifier, Comma()) ~ Dot() ~ termOrFormula ^^ { case f ~ bs ~ _ ~ t => f(bs.map(_.identifier), t) } + ) + + private def termOrFormula: Parser[ParsedTermOrFormula] = positioned(termOrFormulaIff) + + private def termOrFormulaIff: Parser[ParsedTermOrFormula] = + positioned(termOrFormulaImplies ~ rep(Iff() ~> termOrFormulaImplies) ^^ { case h ~ t => (h +: t).reduceRight(ParsedIff.apply) }) + private def termOrFormulaImplies: Parser[ParsedTermOrFormula] = + positioned(termOrFormulaOr ~ rep(Implies() ~> termOrFormulaOr) ^^ { case h ~ t => (h +: t).reduceRight(ParsedImplies.apply) }) + private def termOrFormulaOr: Parser[ParsedTermOrFormula] = + positioned(termOrFormulaAnd ~ rep(Or() ~> termOrFormulaAnd) ^^ { case h ~ t => (h +: t).reduceRight(ParsedOr.apply) }) + private def termOrFormulaAnd: Parser[ParsedTermOrFormula] = + positioned(termOrFormulaPredicate ~ rep(And() ~> termOrFormulaPredicate) ^^ { case h ~ t => (h +: t).reduceRight(ParsedAnd.apply) }) + private def termOrFormulaPredicate: Parser[ParsedTermOrFormula] = + positioned( + termNotBinder ~ + rep( + (Membership() ^^^ ParsedMembership.apply | Subset() ^^^ ParsedSubset.apply | SameCardinality() ^^^ ParsedSameCardinality.apply | Equal() ^^^ ParsedEqual.apply) ~ + termNotBinder + ) ^^ { case t1 ~ ts => + ts.foldRight(t1) { case (f ~ tr, tl) => f(tl, tr) } + } + ) + + private def termNotBinder: Parser[ParsedTermOrFormula] = + positioned( + atom + | Not() ~> atom ^^ ParsedNot.apply + | binder + ) + + private def atom: Parser[ParsedTermOrFormula] = positioned( + (Identifier("P") ^^^ ParsedPower.apply | Identifier("U") ^^^ ParsedUnion.apply) ~ ParenthesisOpen() ~ termOrFormula ~ ParenthesisClose() ^^ { case f ~ _ ~ t ~ _ => + f(t) + } + | identifierOrSchematic ~ (ParenthesisOpen() ~> rep1sep(termOrFormula, Comma()) <~ ParenthesisClose()).? ^^ { case v ~ argsOpt => + val name = v match { + case Identifier(identifier) => ParsedConstant(identifier) + case SchematicIdentifier(identifier) => ParsedSchema(identifier, connector = false) + case SchematicConnectorIdentifier(identifier) => ParsedSchema(identifier, connector = true) + } + argsOpt.map(ParsedApplication(name, _)).getOrElse(name) + } + | ParenthesisOpen() ~ termOrFormula ~ (Comma() ~> termOrFormula <~ ParenthesisClose()).? ~ ParenthesisClose() ^^ { case _ ~ t1 ~ opt ~ _ => + opt match { + case Some(t2) => ParsedOrderedPair(t1, t2) + case None => t1 + } + } + | CurlyBracketOpen() ~> (termOrFormula ~ (Comma() ~> termOrFormula).?).? <~ CurlyBracketClose() ^^ { + case Some(t1 ~ opt2) => + opt2 match { + case Some(t2) => ParsedSet2(t1, t2) + case None => ParsedSet2(t1, t1) + } + case None => ParsedSet0() + } + | EmptySet() ^^^ ParsedSet0() + ) + + private def localBinder: Parser[Seq[String]] = + LocalBinder() ~> rep1sep(identifier, Comma()) <~ Dot() ^^ (fv => fv.map(_.identifier)) + private def localBinderOptional: Parser[Seq[String]] = localBinder.? ^^ (fv => fv.getOrElse(Seq.empty)) + + private def topTermOrFormula: Parser[ParsedTopTermOrFormula] = + localBinderOptional ~ termOrFormula ^^ { case fv ~ t => ParsedTopTermOrFormula(fv, t) } + + private def termOrFormulaSequence: Parser[Seq[ParsedTermOrFormula]] = + repsep(termOrFormula, Semicolon()) + + private def sequent: Parser[ParsedSequent] = + positioned(localBinderOptional ~ termOrFormulaSequence ~ Turnstile() ~ termOrFormulaSequence ^^ { case fv ~ l ~ _ ~ r => ParsedSequent(fv, l, r) }) + + private def partialSequent: Parser[ParsedPartialSequent] = + positioned( + localBinderOptional ~ ( + ((Ellipsis() ~> (Semicolon() ~> rep1sep(termOrFormula, Semicolon())).?) ^^ (opt => (opt.getOrElse(Seq.empty), true))) | + termOrFormulaSequence ^^ (seq => (seq, false)) + ) ~ Turnstile() ~ + ((Ellipsis() ^^^ Seq.empty | (rep1sep(termOrFormula, Semicolon()) <~ (Semicolon() ~ Ellipsis()))) ^^ (seq => (seq, true)) | + termOrFormulaSequence ^^ (seq => (seq, false))) ^^ { case fv ~ (l, pl) ~ _ ~ (r, pr) => + ParsedPartialSequent(fv, l, r, pl, pr) + } + ) + + private def proofStepParameters: Parser[Seq[ParsedTopTermOrFormula]] = + SquareBracketOpen() ~> repsep(topTermOrFormula, Semicolon()) <~ SquareBracketClose() ^^ (_.toSeq) + + private def proofStep: Parser[ParsedProofStep] = positioned( + indentation ~ integerLiteral ~ ruleName ~ repsep(integerLiteral, Comma()) ~ sequent ~ proofStepParameters.? ^^ { case i ~ l ~ r ~ p ~ s ~ ps => + ParsedProofStep(l.pos, i.spaces, l.value, r.name, p.map(_.value), s, ps.getOrElse(Seq.empty)) + } + ) + + private def proof: Parser[ParsedProof] = positioned( + (indentation ~ newLine).* ~> rep1sep(proofStep, newLine) <~ (newLine ~ indentation).* ^^ (steps => ParsedProof(steps.toIndexedSeq)) + ) + + private def parse[T](parser: Parser[T])(tokens: Seq[FrontToken]): T = { + val reader = new FrontTokensReader(tokens) + parser(reader) match { + case e @ NoSuccess(msg, next) => throw ParsingException(msg, next.pos) + case Success(result, next) => result + case e => throw new MatchError(e) + } + } + + def parseTermOrFormula(tokens: Seq[FrontToken]): ParsedTermOrFormula = + parse(positioned(termOrFormula <~ End()))(tokens) + + def parseTopTermOrFormula(tokens: Seq[FrontToken]): ParsedTopTermOrFormula = + parse(positioned(topTermOrFormula <~ End()))(tokens) + + def parseSequent(tokens: Seq[FrontToken]): ParsedSequent = + parse(positioned(sequent <~ End()))(tokens) + + def parsePartialSequent(tokens: Seq[FrontToken]): ParsedPartialSequent = + parse(positioned(partialSequent <~ End()))(tokens) + + def parseProof(tokens: Seq[FrontToken]): ParsedProof = + parse(positioned(proof <~ End()))(tokens) +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontReader.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontReader.scala new file mode 100644 index 00000000..36b2b375 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontReader.scala @@ -0,0 +1,40 @@ +package lisa.front.parser + +import lisa.front.fol.FOL.* +import lisa.front.parser.FrontResolver +import lisa.front.proof.Proof.* + +/** + * The reading API; parses string into first order logic or sequent elements. + * Reading exceptions can be found in [[FrontReadingException]]. + */ +object FrontReader { + + private def lexing(str: String, ascii: Boolean, multiline: Boolean): Seq[FrontToken] = { + val lexer = if (ascii) FrontLexer.lexingAscii else FrontLexer.lexingUnicode + lexer(str, !multiline, false) + } + + def readTerm(str: String, ascii: Boolean = true, toplevel: Boolean = true, multiline: Boolean = false): Term = { + val tokens = lexing(str, ascii, multiline) + if (toplevel) + FrontResolver.resolveTerm(FrontParser.parseTopTermOrFormula(tokens)) + else + FrontResolver.resolveTerm(FrontParser.parseTermOrFormula(tokens)) + } + + def readFormula(str: String, ascii: Boolean = true, toplevel: Boolean = true, multiline: Boolean = false): Formula = { + val tokens = lexing(str, ascii, multiline) + if (toplevel) + FrontResolver.resolveFormula(FrontParser.parseTopTermOrFormula(tokens)) + else + FrontResolver.resolveFormula(FrontParser.parseTermOrFormula(tokens)) + } + + def readSequent(str: String, ascii: Boolean = true, multiline: Boolean = false): Sequent = + FrontResolver.resolveSequent(FrontParser.parseSequent(lexing(str, ascii, multiline))) + + def readPartialSequent(str: String, ascii: Boolean = true, multiline: Boolean = false): PartialSequent = + FrontResolver.resolvePartialSequent(FrontParser.parsePartialSequent(lexing(str, ascii, multiline))) + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontReadingException.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontReadingException.scala new file mode 100644 index 00000000..c4bbade3 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontReadingException.scala @@ -0,0 +1,21 @@ +package lisa.front.parser + +import scala.util.parsing.input.Position + +/** + * An exception that can occur during reading. + */ +sealed abstract class FrontReadingException extends Exception { + val message: String + val position: Position + + override def toString: String = s"[$position] failure: $message\n\n${position.longString}" +} + +object FrontReadingException { + + case class LexingException(message: String, position: Position) extends FrontReadingException + case class ParsingException(message: String, position: Position) extends FrontReadingException + case class ResolutionException(message: String, position: Position) extends FrontReadingException + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontResolver.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontResolver.scala new file mode 100644 index 00000000..c6556524 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontResolver.scala @@ -0,0 +1,166 @@ +package lisa.front.parser + +import lisa.front.fol.FOL.* +import lisa.front.parser.FrontParsed.* +import lisa.front.parser.FrontReadingException.ResolutionException +import lisa.front.proof.Proof.* +import lisa.front.theory.SetTheory + +import scala.collection.View +import scala.util.Failure +import scala.util.Success +import scala.util.Try +import scala.util.parsing.input.Position +import scala.util.parsing.input.Positional + +/** + * Resolves the intermediate representation ([[FrontParsed]]) into actual first order logic or sequent elements. + */ +private[parser] object FrontResolver { + + // Free variables must appear in the context, otherwise they will be treated as + // nullary function terms + + case class ScopedContext(boundVariables: Set[String], freeVariables: Set[String]) { + def variables: Set[String] = boundVariables ++ freeVariables + } + + private def emptyScopedContext: ScopedContext = ScopedContext(Set.empty, Set.empty) + + private def resolveFunctionTermLabel(name: ParsedName, arity: Int): TermLabel[?] = name match { + case ParsedConstant(identifier) => ConstantFunctionLabel.unsafe(identifier, arity) + case ParsedSchema(identifier, connector) => + if (!connector) + SchematicTermLabel.unsafe(identifier, arity) + else + throw ResolutionException("Type error: expected term, got schematic connector formula", name.pos) + } + + private def resolvePredicateOrConnectorFormulaLabel(name: ParsedName, arity: Int): PredicateLabel[?] | SchematicConnectorLabel[?] = name match { + case ParsedConstant(identifier) => ConstantPredicateLabel.unsafe(identifier, arity) + case ParsedSchema(identifier, connector) => + if (connector) + SchematicConnectorLabel.unsafe(identifier, arity) + else + SchematicPredicateLabel.unsafe(identifier, arity) + } + + private def resolveTermContext(tree: ParsedTermOrFormula)(implicit ctx: ScopedContext): Term = tree match { + case name: ParsedName => + name match { + case ParsedConstant(identifier) => + // If the name in context then it must be a variable. Otherwise we fallback to a constant function + if (ctx.variables.contains(identifier)) { + VariableTerm(VariableLabel(identifier)) + } else { + ConstantFunctionLabel[0](identifier) + } + case ParsedSchema(identifier, connector) => + if (!connector) { + SchematicTermLabel[0](identifier) + } else { + throw ResolutionException("Type error: expected term, got schematic connector formula", tree.pos) + } + } + // If the name is in the context, we decide that it is a variable + + case ParsedApplication(name, args) => + Term.unsafe(resolveFunctionTermLabel(name, args.size), args.map(resolveTermContext(_))) + case ParsedOrderedPair(left, right) => + ConstantFunctionLabel[2]("ordered_pair")(resolveTermContext(left), resolveTermContext(right)) + case ParsedSet2(left, right) => + SetTheory.unorderedPairSet(resolveTermContext(left), resolveTermContext(right)) + // case ParsedSet1(subtree) => + // SetTheory.singletonSet(resolveTermContext(subtree)) + case ParsedSet0() => + SetTheory.emptySet + case ParsedPower(subtree) => + SetTheory.powerSet(resolveTermContext(subtree)) + case ParsedUnion(subtree) => + SetTheory.unionSet(resolveTermContext(subtree)) + case _ => throw ResolutionException("Type error: expected term, got formula", tree.pos) + } + + private def resolveFormulaContext(tree: ParsedTermOrFormula)(implicit ctx: ScopedContext): Formula = tree match { + case name: ParsedName => + resolvePredicateOrConnectorFormulaLabel(name, 0) match { + case predicate: PredicateLabel[?] => PredicateFormula.unsafe(predicate, Seq.empty) + case connector: SchematicConnectorLabel[?] => + throw ResolutionException("Illegal: the arity of schematic connectors must be strictly positive", tree.pos) + } + case ParsedApplication(name, args) => + resolvePredicateOrConnectorFormulaLabel(name, args.size) match { + case predicate: PredicateLabel[?] => PredicateFormula.unsafe(predicate, args.map(resolveTermContext(_))) + case connector: SchematicConnectorLabel[?] => ConnectorFormula.unsafe(connector, args.map(resolveFormulaContext(_))) + } + case operator: ParsedBinaryOperator => + val label: Either[PredicateLabel[?], ConnectorLabel[?]] = operator match { + case _: ParsedEqual => Left(equality) + case _: ParsedMembership => Left(ConstantPredicateLabel[2]("set_membership")) + case _: ParsedSubset => Left(ConstantPredicateLabel[2]("subset_of")) + case _: ParsedSameCardinality => Left(ConstantPredicateLabel[2]("same_cardinality")) + case _: ParsedAnd => Right(and) + case _: ParsedOr => Right(or) + case _: ParsedImplies => Right(implies) + case _: ParsedIff => Right(iff) + } + val args = Seq(operator.left, operator.right) + label match { + case Left(label) => PredicateFormula.unsafe(label, args.map(resolveTermContext(_))) + case Right(label) => ConnectorFormula.unsafe(label, args.map(resolveFormulaContext(_))) + } + case ParsedNot(termOrFormula) => + ConnectorFormula.unsafe(neg, Seq(resolveFormulaContext(termOrFormula))) + case binder: ParsedBinder => + binder.bound.find(ctx.variables.contains).orElse(binder.bound.diff(binder.bound.distinct).headOption) match { + case Some(bound) => throw ResolutionException(s"Name conflict: ${binder.bound}", binder.pos) + case None => () + } + val label = binder match { + case _: ParsedForall => forall + case _: ParsedExists => exists + case _: ParsedExistsOne => existsOne + } + binder.bound.foldRight(resolveFormulaContext(binder.termOrFormula)(ctx.copy(boundVariables = ctx.boundVariables ++ binder.bound)))((bound, body) => + BinderFormula(label, VariableLabel(bound), body) + ) + case _ => throw ResolutionException("Type error: expected formula, got term", tree.pos) + } + + def resolveTerm(tree: ParsedTermOrFormula): Term = + resolveTermContext(tree)(emptyScopedContext) + + def resolveTerm(tree: ParsedTopTermOrFormula): Term = + resolveTermContext(tree.termOrFormula)(freeVariablesToContext(tree.freeVariables, tree.pos)) + + def resolveFormula(tree: ParsedTermOrFormula): Formula = + resolveFormulaContext(tree)(emptyScopedContext) + + private def freeVariablesToContext(freeVariables: Seq[String], position: Position): ScopedContext = { + val repeated = freeVariables.diff(freeVariables.distinct).distinct + if (repeated.isEmpty) { + ScopedContext(Set.empty, freeVariables.toSet) + } else { + throw ResolutionException(s"Repeated free variable declaration: ${repeated.mkString(", ")}", position) + } + } + + def resolveFormula(tree: ParsedTopTermOrFormula): Formula = + resolveFormulaContext(tree.termOrFormula)(freeVariablesToContext(tree.freeVariables, tree.pos)) + + def resolveSequent(tree: ParsedSequent): Sequent = { + val ctx = freeVariablesToContext(tree.freeVariables, tree.pos) + Sequent(tree.left.map(resolveFormulaContext(_)(ctx)).toIndexedSeq, tree.right.map(resolveFormulaContext(_)(ctx)).toIndexedSeq) + } + + def resolvePartialSequent(tree: ParsedPartialSequent): PartialSequent = { + val ctx = freeVariablesToContext(tree.freeVariables, tree.pos) + PartialSequent( + tree.left.map(resolveFormulaContext(_)(ctx)).toIndexedSeq, + tree.right.map(resolveFormulaContext(_)(ctx)).toIndexedSeq, + tree.partialLeft, + tree.partialRight + ) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontSymbols.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontSymbols.scala new file mode 100644 index 00000000..644a2d2a --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontSymbols.scala @@ -0,0 +1,94 @@ +package lisa.front.parser + +/** + * Symbols to be used by the parser and printer. + * There exists two variants, [[FrontSymbols.FrontAsciiSymbols]] and [[FrontSymbols.FrontUnicodeSymbols]]. + */ +private[front] sealed abstract class FrontSymbols { + val Forall: String + val Exists: String + val ExistsOne: String + val Iff: String + val Implies: String + val Or: String + val And: String + val Exclamation: String + val Turnstile: String + val Ellipsis: String + val Subset: String + val Membership: String + val EmptySet: String + val Top: String = "⊤" + val Bot: String = "⊥" + val Equal: String = "=" + val Tilde: String = "~" + val Backslash: String = "\\" + val CurlyBracketOpen: String = "{" + val CurlyBracketClose: String = "}" + val SquareBracketOpen: String = "[" + val SquareBracketClose: String = "]" + val ParenthesisOpen: String = "(" + val ParenthesisClose: String = ")" + val Dot: String = "." + val Comma: String = "," + val Semicolon: String = ";" + val QuestionMark: String = "?" + val PowerSet: String = "P" + val UnionSet: String = "U" +} + +private[front] object FrontSymbols { + object FrontAsciiSymbols extends FrontSymbols { + override val Forall: String = "forall" + override val Exists: String = "exists" + override val ExistsOne: String = "existsone" + override val Iff: String = "<=>" + override val Implies: String = "=>" + override val Or: String = raw"\/" + override val And: String = """/\""" + override val Exclamation: String = "!" + override val Turnstile: String = "|-" + override val Ellipsis: String = "..." + override val Membership: String = "in" + override val Subset: String = "sub" + override val EmptySet: String = "{}" + } + + object FrontUnicodeSymbols extends FrontSymbols { + override val Forall: String = "∀" + override val Exists: String = "∃" + override val ExistsOne: String = "∃!" + override val Iff: String = "↔" + override val Implies: String = "→" + override val Or: String = "∨" + override val And: String = "∧" + override val Exclamation: String = "¬" + override val Turnstile: String = "⊢" + override val Ellipsis: String = "…" + override val Membership: String = "∈" + override val Subset: String = "⊆" + override val EmptySet: String = "∅" + } + + object FrontLatexSymbols extends FrontSymbols { + override val Forall: String = raw"\forall" + override val Exists: String = raw"\exists" + override val ExistsOne: String = raw"\exists!" + override val Iff: String = raw"\Leftrightarrow" + override val Implies: String = raw"\Rightarrow" + override val Or: String = raw"\lor" + override val And: String = raw"\land" + override val Exclamation: String = raw"\neg" + override val Turnstile: String = raw"\vdash" + override val Ellipsis: String = raw"\ldots" + override val Membership: String = raw"\in" + override val Subset: String = raw"\subseteq" + override val EmptySet: String = raw"\varnothing" + override val Tilde: String = raw"\sim" + override val Backslash: String = raw"\setminus" + override val CurlyBracketOpen: String = raw"\{" + override val CurlyBracketClose: String = raw"\}" + override val PowerSet: String = raw"\mathcal{P}" + override val UnionSet: String = raw"\mathcal{U}" + } +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontToken.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontToken.scala new file mode 100644 index 00000000..5478031c --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontToken.scala @@ -0,0 +1,59 @@ +package lisa.front.parser + +import scala.util.parsing.input.Positional + +/** + * Low-level tokens used by the lexer ([[FrontLexer]]). + */ +private[parser] enum FrontToken extends Positional { + + case Identifier(identifier: String) + case SchematicIdentifier(identifier: String) + case SchematicConnectorIdentifier(identifier: String) + + case IntegerLiteral(value: Int) + + case Indentation(spaces: Int) + + case NewLineWithIndentation(spaces: Int) + case InitialIndentation(spaces: Int) + + // The reason these *must* be case classes is because they extend `Positional`, + // which contains a position attribute (that shouldn't be shared between instances) + + case Turnstile() + case And() + case Or() + case Implies() + case Iff() + case Equal() + case Membership() + case Subset() + case SameCardinality() + case Forall() + case Exists() + case ExistsOne() + case Not() + case EmptySet() + case LocalBinder() + + case Ellipsis() + + case CurlyBracketOpen() + case CurlyBracketClose() + case SquareBracketOpen() + case SquareBracketClose() + case ParenthesisOpen() + case ParenthesisClose() + + case Dot() + case Comma() + case Semicolon() + + case RuleName(name: String) + + case NewLine() + + case End() + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/FrontTokensReader.scala b/lisa-front/src/main/scala/lisa/front/parser/FrontTokensReader.scala new file mode 100644 index 00000000..08e143cf --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/FrontTokensReader.scala @@ -0,0 +1,12 @@ +package lisa.front.parser + +import scala.util.parsing.input.NoPosition +import scala.util.parsing.input.Position +import scala.util.parsing.input.Reader + +private[parser] class FrontTokensReader(tokens: Seq[FrontToken]) extends Reader[FrontToken] { + override def first: FrontToken = tokens.head + override def atEnd: Boolean = tokens.isEmpty + override def pos: Position = tokens.headOption.map(_.pos).getOrElse(NoPosition) + override def rest: Reader[FrontToken] = new FrontTokensReader(tokens.tail) +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/KernelReader.scala b/lisa-front/src/main/scala/lisa/front/parser/KernelReader.scala new file mode 100644 index 00000000..95bfc801 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/KernelReader.scala @@ -0,0 +1,10 @@ +package lisa.front.parser + +import lisa.kernel.proof.SCProof + +object KernelReader { + + def readProof(str: String): SCProof = + KernelResolver.resolveProof(FrontParser.parseProof(FrontLexer.lexingExtendedUnicode(str)), FrontSymbols.FrontUnicodeSymbols) + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/KernelResolver.scala b/lisa-front/src/main/scala/lisa/front/parser/KernelResolver.scala new file mode 100644 index 00000000..3179d0a0 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/KernelResolver.scala @@ -0,0 +1,299 @@ +package lisa.front.parser + +import lisa.front.parser.FrontParsed.* +import lisa.front.parser.FrontReadingException.ResolutionException +import lisa.front.parser.FrontResolver.* +import lisa.kernel.fol.FOL.* +import lisa.kernel.proof.SCProof +import lisa.kernel.proof.SequentCalculus.* + +import scala.util.Failure +import scala.util.Success +import scala.util.Try +import scala.util.parsing.input.Position + +private[parser] object KernelResolver { + + private def tryResolve[T](t: => T): Option[T] = Try(t) match { + case Success(value) => Some(value) + case Failure(exception) => + exception match { + case _: ResolutionException => None + case _ => throw new Exception(exception) + } + } + private def all[T](t: => Seq[Option[T]]): Option[Seq[T]] = if (t.forall(_.nonEmpty)) Some(t.flatten) else None + + private object Formula { + def unapply(tree: ParsedTopTermOrFormula): Option[Formula] = tryResolve(resolveFormula(tree)) + } + private object FormulaSeq { + def unapplySeq(trees: Seq[ParsedTopTermOrFormula]): Option[Seq[Formula]] = all(trees.map(tree => tryResolve(resolveFormula(tree)))) + } + private object Term { + def unapply(tree: ParsedTopTermOrFormula): Option[Term] = tryResolve(resolveTerm(tree)) + } + private object TermSeq { + def unapplySeq(trees: Seq[ParsedTopTermOrFormula]): Option[Seq[Term]] = all(trees.map(tree => tryResolve(resolveTerm(tree)))) + } + private object Identifier { + def unapply(tree: ParsedTopTermOrFormula): Option[String] = tree.termOrFormula match { + case ParsedConstant(identifier) => Some(identifier) + case _ => None + } + } + + private object FunctionMap { + def unapplySeq(trees: Seq[ParsedTopTermOrFormula]): Option[Seq[(SchematicTermLabel, LambdaTermTerm)]] = { + all(trees.map { + case ParsedTopTermOrFormula(freeVariables, ParsedEqual(left, term)) => + val opt = left match { + case ParsedSchema(identifier, false) => Some((identifier, Seq.empty)) + case ParsedApplication(ParsedSchema(identifier, false), args) => Some((identifier, args)) + case _ => None + } + opt.flatMap { case (identifier, args) => + all(args.map { + case ParsedSchema(arg, false) => Some(VariableLabel(arg)) + case _ => None + }).flatMap(arguments => + Term + .unapply(ParsedTopTermOrFormula(freeVariables, term)) + .map(body => SchematicFunctionLabel(identifier, args.size) -> LambdaTermTerm(arguments, body)) + ) + } + case _ => None + }) + } + } + private object PredicateMap { + def unapplySeq(trees: Seq[ParsedTopTermOrFormula]): Option[Seq[(SchematicVarOrPredLabel, LambdaTermFormula)]] = { + all(trees.map { + case ParsedTopTermOrFormula(freeVariables, ParsedIff(left, term)) => + val opt = left match { + case ParsedSchema(identifier, true) => Some((identifier, Seq.empty)) + case ParsedApplication(ParsedSchema(identifier, true), args) => Some((identifier, args)) + case _ => None + } + opt.flatMap { case (identifier, args) => + all(args.map { + case ParsedSchema(arg, false) => Some(VariableLabel(arg)) + case _ => None + }).flatMap(arguments => + Formula + .unapply(ParsedTopTermOrFormula(freeVariables, term)) + .map(body => SchematicPredicateLabel(identifier, args.size) -> LambdaTermFormula(arguments, body)) + ) + } + case _ => None + }) + } + } + + private def resolveProofStep(tree: ParsedProofStep, kernelRuleIdentifiers: KernelRuleIdentifiers): SCProofStep = { + val R = kernelRuleIdentifiers + require(!Seq(R.Import, R.SubproofShown, R.SubproofHidden).contains(tree.ruleName)) + + val placeholder = "_" + def throwInvalidStep(): Nothing = + throw ResolutionException(s"Incorrect premises and/or arguments for step '${tree.ruleName}'", tree.stepPosition) + + val bot = resolveSequent(tree.conclusion) + (tree.ruleName, tree.premises, tree.parameters) match { + case (R.Hypothesis, Seq(), Seq(Formula(phi))) => Hypothesis(bot, phi) + case (R.Cut, Seq(t1, t2), Seq(Formula(phi))) => Cut(bot, t1, t2, phi) + case (R.Rewrite, Seq(t1), Seq()) => Rewrite(bot, t1) + case (R.RewriteTrue, Seq(), Seq()) => RewriteTrue(bot) + case (R.Weakening, Seq(t1), Seq()) => Weakening(bot, t1) + case (R.LeftAnd, Seq(t1), Seq(Formula(phi), Formula(psi))) => LeftAnd(bot, t1, phi, psi) + case (R.RightAnd, premises, FormulaSeq(conjuncts*)) if conjuncts.size == premises.size => RightAnd(bot, premises, conjuncts) + case (R.LeftOr, premises, FormulaSeq(conjuncts*)) if conjuncts.size == premises.size => LeftOr(bot, premises, conjuncts) + case (R.RightOr, Seq(t1), Seq(Formula(phi), Formula(psi))) => RightOr(bot, t1, phi, psi) + case (R.LeftImplies, Seq(t1, t2), Seq(Formula(phi), Formula(psi))) => LeftImplies(bot, t1, t2, phi, psi) + case (R.RightImplies, Seq(t1), Seq(Formula(phi), Formula(psi))) => RightImplies(bot, t1, phi, psi) + case (R.LeftIff, Seq(t1), Seq(Formula(phi), Formula(psi))) => LeftIff(bot, t1, phi, psi) + case (R.RightIff, Seq(t1, t2), Seq(Formula(phi), Formula(psi))) => RightIff(bot, t1, t2, phi, psi) + case (R.LeftNot, Seq(t1), Seq(Formula(phi))) => LeftNot(bot, t1, phi) + case (R.RightNot, Seq(t1), Seq(Formula(phi))) => RightNot(bot, t1, phi) + case (R.LeftForall, Seq(t1), Seq(Formula(phi), Identifier(x), Term(t))) => LeftForall(bot, t1, phi, VariableLabel(x), t) + case (R.RightForall, Seq(t1), Seq(Formula(phi), Identifier(x))) => RightForall(bot, t1, phi, VariableLabel(x)) + case (R.LeftExists, Seq(t1), Seq(Formula(phi), Identifier(x))) => LeftExists(bot, t1, phi, VariableLabel(x)) + case (R.RightExists, Seq(t1), Seq(Formula(phi), Identifier(x), Term(t))) => RightExists(bot, t1, phi, VariableLabel(x), t) + case (R.LeftExistsOne, Seq(t1), Seq(Formula(phi), Identifier(x))) => LeftExistsOne(bot, t1, phi, VariableLabel(x)) + case (R.RightExistsOne, Seq(t1), Seq(Formula(phi), Identifier(x))) => RightExistsOne(bot, t1, phi, VariableLabel(x)) + case (R.LeftRefl, Seq(t1), Seq(Formula(fa))) => LeftRefl(bot, t1, fa) + case (R.RightRefl, Seq(), Seq(Formula(fa))) => RightRefl(bot, fa) + case (R.LeftSubstEq, Seq(t1), parameters) if parameters.size % 2 == 1 => + (parameters.init, parameters.last) match { + case (TermSeq(terms*), Formula(ConnectorFormula(Iff, Seq(PredicateFormula(ConstantPredicateLabel(`placeholder`, _), args), body)))) => + all(args.map { + case VariableTerm(label) => Some(label) + case _ => None + }) match { + case Some(vars) => LeftSubstEq(bot, t1, terms.grouped(2).collect { case Seq(a, b) => (a, b) }.toList, LambdaTermFormula(vars, body)) + case None => throwInvalidStep() + } + case _ => throwInvalidStep() + } + case (R.RightSubstEq, Seq(t1), parameters) if parameters.size % 2 == 1 => + (parameters.init, parameters.last) match { + case (TermSeq(terms*), Formula(ConnectorFormula(Iff, Seq(PredicateFormula(ConstantPredicateLabel(`placeholder`, _), args), body)))) => + all(args.map { + case VariableTerm(label) => Some(label) + case _ => None + }) match { + case Some(vars) => RightSubstEq(bot, t1, terms.grouped(2).collect { case Seq(a, b) => (a, b) }.toList, LambdaTermFormula(vars, body)) + case None => throwInvalidStep() + } + case _ => throwInvalidStep() + } + case (R.LeftSubstIff, Seq(t1), parameters) if parameters.size % 2 == 1 => + (parameters.init, parameters.last) match { + case (FormulaSeq(formulas*), Formula(ConnectorFormula(Iff, Seq(PredicateFormula(ConstantPredicateLabel(`placeholder`, _), args), body)))) => + all(args.map { + case VariableTerm(label) => Some(VariableFormulaLabel(label.id)) + case _ => None + }) match { + case Some(vars) => LeftSubstIff(bot, t1, formulas.grouped(2).collect { case Seq(a, b) => (a, b) }.toList, LambdaFormulaFormula(vars, body)) + case None => throwInvalidStep() + } + case _ => throwInvalidStep() + } + case (R.RightSubstIff, Seq(t1), parameters) if parameters.size % 2 == 1 => + (parameters.init, parameters.last) match { + case (FormulaSeq(formulas*), Formula(ConnectorFormula(Iff, Seq(PredicateFormula(ConstantPredicateLabel(`placeholder`, _), args), body)))) => + all(args.map { + case VariableTerm(label) => Some(VariableFormulaLabel(label.id)) + case _ => None + }) match { + case Some(vars) => RightSubstIff(bot, t1, formulas.grouped(2).collect { case Seq(a, b) => (a, b) }.toList, LambdaFormulaFormula(vars, body)) + case None => throwInvalidStep() + } + case _ => throwInvalidStep() + } + case (R.FunInstantiation, Seq(t1), FunctionMap(map*)) => InstFunSchema(bot, t1, map.toMap) + case (R.PredInstantiation, Seq(t1), PredicateMap(map*)) => InstPredSchema(bot, t1, map.toMap) + case _ => throwInvalidStep() + } + } + + def resolveProof(tree: ParsedProof, symbols: FrontSymbols): SCProof = { + val R = KernelRuleIdentifiers(symbols) + + case class StackEntry(steps: IndexedSeq[SCProofStep], imports: IndexedSeq[Sequent], subproofPremises: Seq[Int], nextLineNumber: Int, indentation: Int) + + def foldSubproofs(stack: Seq[StackEntry], position: Position): Option[SCSubproof] = { + stack.foldLeft(None: Option[SCSubproof]) { (subproofOpt, entry) => + val premises = entry.subproofPremises + val newSteps = subproofOpt match { + case Some(proof) => entry.steps :+ proof.copy(premises = premises) + case None => entry.steps + } + if (newSteps.isEmpty) { + throw ResolutionException("This proof or subproof is incomplete", position) + } + Some(SCSubproof(SCProof(newSteps, entry.imports))) + } + } + + val (finalStack, finalExpectedDeeperIndentation) = tree.steps.foldLeft( + (Seq.empty[StackEntry], true) + ) { case ((stack, expectedDeeperIndentation), parsedStep) => + // The true indentation of the current step + val rightIndentation = parsedStep.indentation + parsedStep.line.toString.length + + val newStack = + if (expectedDeeperIndentation) { // Either the first line of a subproof or the first line of the proof + stack match { + case entry +: _ => // First step inside a subproof + if (rightIndentation <= entry.indentation) { + throw ResolutionException("The content of this subproof must be indented further", parsedStep.stepPosition) + } + val importsSize = entry.subproofPremises.size + if (-parsedStep.line != importsSize) { + throw ResolutionException(s"The parent subproof declared $importsSize premise(s), therefore this line must start with index ${-importsSize}", parsedStep.stepPosition) + } + case _ => // Very first line of the proof + () + } + if (parsedStep.line > 0) { + throw ResolutionException(s"The index of the first proof step cannot be strictly positive", parsedStep.stepPosition) + } + val entry = StackEntry(IndexedSeq.empty, IndexedSeq.empty, Seq.empty, parsedStep.line, rightIndentation) + entry +: stack + } else { // A line at the same level or lower + assert(stack.nonEmpty) // Invariant + + val indentationIndex = stack.zipWithIndex.find { case (entry, _) => entry.indentation == rightIndentation }.map(_._2) + indentationIndex match { + case Some(delta) => + val previousEntry: StackEntry = stack(delta) + previousEntry.copy(steps = previousEntry.steps ++ foldSubproofs(stack.take(delta), parsedStep.stepPosition).map(sp => sp.copy(premises = previousEntry.subproofPremises)).toSeq) +: stack + .drop(delta + 1) + case None => + throw ResolutionException("This step is not properly indented", parsedStep.stepPosition) + } + } + + assert(newStack.nonEmpty) // Invariant + + val entry = newStack.head + val tail = newStack.tail + + if (parsedStep.line != entry.nextLineNumber) { + throw ResolutionException(s"Expected line to be numbered ${entry.nextLineNumber}, but got ${parsedStep.line} instead", parsedStep.stepPosition) + } + + val isImport = parsedStep.ruleName == R.Import + val isSubproof = parsedStep.ruleName == R.SubproofShown // Hidden is excluded from this + + if (parsedStep.ruleName == R.SubproofHidden) { + throw ResolutionException("Cannot parse a hidden subproof", parsedStep.stepPosition) + } + + if (parsedStep.line < 0) { + if (!isImport) { + throw ResolutionException("Negative step indices must necessarily be import statements", parsedStep.stepPosition) + } + } else { + if (isImport) { + throw ResolutionException("Import statements can only appear on negative indices steps", parsedStep.stepPosition) + } + } + + if (isImport && parsedStep.premises.nonEmpty) { + throw ResolutionException("Import statements cannot have premises", parsedStep.stepPosition) + } + + if (!isImport) { + parsedStep.premises.foreach { premise => + if ((premise < 0 && -premise > entry.imports.size) || (premise >= 0 && premise >= entry.steps.size)) { + throw ResolutionException(s"Premise $premise is out of bounds", parsedStep.stepPosition) + } + } + } + + val sequent = resolveSequent(parsedStep.conclusion) + + val newEntry = entry.copy(nextLineNumber = entry.nextLineNumber + 1, subproofPremises = Seq.empty) + + if (isImport) { + (newEntry.copy(imports = sequent +: newEntry.imports) +: tail, false) + } else if (isSubproof) { + (newEntry.copy(subproofPremises = parsedStep.premises) +: tail, true) + } else { + val newStep = resolveProofStep(parsedStep, R) + (newEntry.copy(steps = newEntry.steps :+ newStep) +: tail, isSubproof) // FIXME spurious `isSubproof` (variable is always false here) + } + } + + val lastPosition = tree.steps.last.stepPosition + if (finalExpectedDeeperIndentation) { + throw ResolutionException("Empty trailing subproof", lastPosition) + } + + val finalStep = foldSubproofs(finalStack, lastPosition) + finalStep.get.sp + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/parser/KernelRuleIdentifiers.scala b/lisa-front/src/main/scala/lisa/front/parser/KernelRuleIdentifiers.scala new file mode 100644 index 00000000..4a66a974 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/parser/KernelRuleIdentifiers.scala @@ -0,0 +1,92 @@ +package lisa.front.parser + +import lisa.kernel.proof.SequentCalculus.SCProofStep +import lisa.kernel.proof.SequentCalculus as SC + +private[front] case class KernelRuleIdentifiers(symbols: FrontSymbols) { + + private val isLatex: Boolean = symbols.isInstanceOf[FrontSymbols.FrontLatexSymbols.type] + + private val Left: String = "Left" + private val Right: String = "Right" + private val Subst: String = "Subst." + private val Refl: String = "Refl." + private val Instantiation: String = "Instantiation" + private val Subproof: String = "Subproof" + + private def symbol(s: String): String = if (isLatex) s"{$s}" else s + private def text(s: String): String = if (isLatex) raw"\text{$s}" else s + private def space: String = if (isLatex) "~" else " " + private def left(s: String): String = s"${text(Left)}$space${symbol(s)}" + private def right(s: String): String = s"${text(Right)}$space${symbol(s)}" + private def leftSubst(s: String): String = s"${text(s"$Left $Subst")}$space${symbol(s)}" + private def rightSubst(s: String): String = s"${text(s"$Right $Subst")}$space${symbol(s)}" + + val Hypothesis: String = text("Hypo.") + val Cut: String = text("Cut") + val Rewrite: String = text("Rewrite") + val RewriteTrue: String = text("Rewrite " + symbols.Top) + val Weakening: String = text("Weakening") + val LeftAnd: String = left(symbols.And) + val RightAnd: String = right(symbols.And) + val LeftOr: String = left(symbols.Or) + val RightOr: String = right(symbols.Or) + val LeftImplies: String = left(symbols.Implies) + val RightImplies: String = right(symbols.Implies) + val LeftIff: String = left(symbols.Iff) + val RightIff: String = right(symbols.Iff) + val LeftNot: String = left(symbols.Exclamation) + val RightNot: String = right(symbols.Exclamation) + val LeftForall: String = left(symbols.Forall) + val RightForall: String = right(symbols.Forall) + val LeftExists: String = left(symbols.Exists) + val RightExists: String = right(symbols.Exists) + val LeftExistsOne: String = left(symbols.ExistsOne) + val RightExistsOne: String = right(symbols.ExistsOne) + val LeftRefl: String = left(Refl) + val RightRefl: String = right(Refl) + val LeftSubstEq: String = leftSubst(symbols.Equal) + val RightSubstEq: String = rightSubst(symbols.Equal) + val LeftSubstIff: String = leftSubst(symbols.Iff) + val RightSubstIff: String = rightSubst(symbols.Iff) + val FunInstantiation: String = text(s"?Fun. $Instantiation") + val PredInstantiation: String = text(s"?Pred. $Instantiation") + val SubproofShown: String = text(Subproof) + val SubproofHidden: String = text(s"$Subproof (hidden)") + val Import: String = text("Import") + + def identify(step: SCProofStep): String = step match { + case _: SC.Hypothesis => Hypothesis + case _: SC.Cut => Cut + case _: SC.Rewrite => Rewrite + case _: SC.RewriteTrue => RewriteTrue + case _: SC.Weakening => Weakening + case _: SC.LeftAnd => LeftAnd + case _: SC.RightAnd => RightAnd + case _: SC.LeftOr => LeftOr + case _: SC.RightOr => RightOr + case _: SC.LeftImplies => LeftImplies + case _: SC.RightImplies => RightImplies + case _: SC.LeftIff => LeftIff + case _: SC.RightIff => RightIff + case _: SC.LeftNot => LeftNot + case _: SC.RightNot => RightNot + case _: SC.LeftForall => LeftForall + case _: SC.RightForall => RightForall + case _: SC.LeftExists => LeftExists + case _: SC.RightExists => RightExists + case _: SC.LeftExistsOne => LeftExistsOne + case _: SC.RightExistsOne => RightExistsOne + case _: SC.LeftRefl => LeftRefl + case _: SC.RightRefl => RightRefl + case _: SC.LeftSubstEq => LeftSubstEq + case _: SC.RightSubstEq => RightSubstEq + case _: SC.LeftSubstIff => LeftSubstIff + case _: SC.RightSubstIff => RightSubstIff + case _: SC.InstFunSchema => FunInstantiation + case _: SC.InstPredSchema => PredInstantiation + case SC.SCSubproof(_, _, true) => SubproofShown + case SC.SCSubproof(_, _, false) => SubproofHidden + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/printer/FrontPositionedPrinter.scala b/lisa-front/src/main/scala/lisa/front/printer/FrontPositionedPrinter.scala new file mode 100644 index 00000000..6f013ad6 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/printer/FrontPositionedPrinter.scala @@ -0,0 +1,331 @@ +package lisa.front.printer + +import lisa.front.fol.FOL.* +import lisa.front.parser.FrontSymbols +import lisa.front.printer.FrontPrintNode.* +import lisa.front.printer.FrontPrintParameters +import lisa.front.proof.Proof.* +import lisa.front.theory.SetTheory + +/** + * A set of methods to positioned-print kernel trees. + */ +object FrontPositionedPrinter { + + private val rName = "[a-zA-Z_][a-zA-Z0-9_]*".r + private def isNamePrintable(name: String): Boolean = rName.matches(name) + + private def isTermPrintableInternal(term: Term, variables: Set[String]): Boolean = term match { + case VariableTerm(label) => + assert(variables.contains(label.id)) // By assumption, thus `isNamePrintable` is true + true + case Term(label, args) => + val isLabelPrintable = label match { + case SchematicTermLabel(id, _) => !variables.contains(id) + case _ => true + } + isNamePrintable(label.id) && isLabelPrintable && args.forall(isTermPrintableInternal(_, variables)) + } + + private def isTermPrintable(term: Term, freeVariables: Set[VariableLabel]): Boolean = + freeVariables.map(_.id).forall(isNamePrintable) && isWellFormed(term) && isTermPrintableInternal(term, freeVariables.map(_.id)) + + private def isFormulaPrintableInternal(formula: Formula, variables: Set[String]): Boolean = formula match { + case PredicateFormula(label, args) => + (!label.isInstanceOf[SchematicLabelType] || isNamePrintable(label.id)) && args.forall(isTermPrintableInternal(_, variables)) + case ConnectorFormula(label, args) => + (!label.isInstanceOf[SchematicLabelType] || isNamePrintable(label.id)) && args.forall(isFormulaPrintableInternal(_, variables)) + case BinderFormula(label, bound, inner) => + isNamePrintable(bound.id) && !variables.contains(bound.id) && isFormulaPrintableInternal(inner, variables + bound.id) + } + + private def isFormulaPrintable(formula: Formula, freeVariables: Set[VariableLabel]): Boolean = + freeVariables.map(_.id).forall(isNamePrintable) && isWellFormed(formula) && isFormulaPrintableInternal(formula, freeVariables.map(_.id)) + + private def mkSep(items: FrontPrintNode*)(separator: FrontLeaf): IndexedSeq[FrontPrintNode] = { + val nodes = items match { + case head +: tail => + head +: tail.flatMap(Seq(separator, _)) + case other => other + } + nodes.toIndexedSeq + } + + private def spaceSeparator(using p: FrontPrintParameters): String = if (p.compact) "" else " " + private def commaSeparator(separator: String)(using FrontPrintParameters): String = s"$separator$spaceSeparator" + private def commaSeparator(using p: FrontPrintParameters): String = commaSeparator(p.s.Comma) + + private def prettyName(name: String)(using p: FrontPrintParameters): String = + if (p.symbols == FrontPrintStyle.Latex && p.compact) s"{$name}" else name + private def prettyLabel(label: LabelType, double: Boolean = false)(using p: FrontPrintParameters): String = { + val (result, mustWrap) = label match { + case _: SchematicLabelType => + val s = s"${if (double) p.s.QuestionMark else ""}${p.s.QuestionMark}${label.id}" + (s, true) + case _ => (label.id, false) + } + if ((mustWrap && p.symbols == FrontPrintStyle.Latex) || (p.symbols == FrontPrintStyle.Latex && p.compact)) s"{$result}" else result + } + + private def positionedParentheses(content: FrontPrintNode)(using p: FrontPrintParameters): IndexedSeq[FrontPrintNode] = + IndexedSeq(p.s.ParenthesisOpen, content, p.s.ParenthesisClose) + + private def positionedFunction(name: String, args: Seq[FrontBranch], dropParentheses: Boolean = true)(using p: FrontPrintParameters): FrontBranch = { + if (dropParentheses && args.isEmpty) + FrontBranch(name) + else + FrontBranch(FrontLeaf(s"$name${p.s.ParenthesisOpen}") +: mkSep(args*)(commaSeparator) :+ FrontLeaf(p.s.ParenthesisClose)) + } + + private def positionedInfix(operator: String, left: FrontPrintNode, right: FrontPrintNode)(using FrontPrintParameters): FrontBranch = + FrontBranch(mkSep(left, operator, right)(spaceSeparator)) + private def positionedInfix(operator: FrontPrintNode, left: IndexedSeq[FrontPrintNode], right: IndexedSeq[FrontPrintNode])(using FrontPrintParameters): FrontBranch = + FrontBranch(left ++ Seq(FrontLeaf(spaceSeparator)) ++ IndexedSeq(operator) ++ Seq(FrontLeaf(spaceSeparator)) ++ right) + + // Special symbols that aren't defined in this theory + private val (membership, subsetOf, sameCardinality) = ( + SetTheory.membership, + SetTheory.subset, + SetTheory.sameCardinality + ) + private val (emptySet, unorderedPair, orderedPair, powerSet, unionSet) = ( + SetTheory.emptySet, + SetTheory.unorderedPairSet, + ConstantFunctionLabel[2]("ordered_pair"), + // SetTheory.singletonSet, + SetTheory.powerSet, + SetTheory.unionSet + ) + private val nonAtomicPredicates = Set[PredicateLabel[?]](equality, membership, subsetOf, sameCardinality) // Predicates which require parentheses (for readability) + + private def positionedFormulaInternal(formula: Formula, isRightMost: Boolean)(using p: FrontPrintParameters): FrontBranch = formula match { + case PredicateFormula(label, args) => + label match { + case `equality` => + args match { + case Seq(l, r) => positionedInfix(p.s.Equal, positionedTermInternal(l), positionedTermInternal(r)) + case _ => throw new Error + } + case `membership` => + args match { + case Seq(l, r) => positionedInfix(p.s.Membership, positionedTermInternal(l), positionedTermInternal(r)) + case _ => throw new Error + } + case `subsetOf` => + args match { + case Seq(l, r) => positionedInfix(p.s.Subset, positionedTermInternal(l), positionedTermInternal(r)) + case _ => throw new Error + } + case `sameCardinality` => + args match { + case Seq(l, r) => positionedInfix(p.s.Tilde, positionedTermInternal(l), positionedTermInternal(r)) + case _ => throw new Error + } + case _ => + positionedFunction(prettyLabel(label), args.map(positionedTermInternal(_))) + } + case ConnectorFormula(label, args) => + (label, args) match { + case (`neg`, Seq(arg)) => + val isAtomic = arg match { + case PredicateFormula(label, _) => !nonAtomicPredicates.contains(label) + case ConnectorFormula(`neg`, _) => true + case _ => false + } + val bodyString = positionedFormulaInternal(arg, isRightMost) + val bodyParenthesized = if (isAtomic) IndexedSeq(bodyString) else positionedParentheses(bodyString) + FrontBranch(FrontLeaf(p.s.Exclamation) +: bodyParenthesized) + case (binary @ (`implies` | `iff` | `and` | `or`), Seq(l, r)) => + val precedences: Map[ConnectorLabel[?], Int] = Map( + and -> 1, + or -> 2, + implies -> 3, + iff -> 4 + ) + val symbols: Map[ConnectorLabel[?], String] = Map( + and -> p.s.And, + or -> p.s.Or, + implies -> p.s.Implies, + iff -> p.s.Iff + ) + val precedence = precedences(binary) + val isLeftParentheses = l match { + case _: BinderFormula => true + case PredicateFormula(leftLabel, _) => nonAtomicPredicates.contains(leftLabel) + case ConnectorFormula(leftLabel, _) => precedences.get(leftLabel).exists(_ >= precedence) + } + val isRightParentheses = r match { + case _: BinderFormula => !isRightMost + case PredicateFormula(leftLabel, _) => nonAtomicPredicates.contains(leftLabel) + case ConnectorFormula(rightLabel, _) => precedences.get(rightLabel).exists(_ > precedence) + } + val (leftString, rightString) = (positionedFormulaInternal(l, isLeftParentheses), positionedFormulaInternal(r, isRightMost || isRightParentheses)) + val leftParenthesized = if (isLeftParentheses) positionedParentheses(leftString) else IndexedSeq(leftString) + val rightParenthesized = if (isRightParentheses) positionedParentheses(rightString) else IndexedSeq(rightString) + positionedInfix(symbols(label), leftParenthesized, rightParenthesized) + case (nary @ (`and` | `or`), args) if args.nonEmpty => + // FIXME wrong indexing if we do that + // Rewriting to match the above case; namely op(a) --> a, and op(a, ...rest) --> op(a, op(...rest)) + // Empty args aren't allowed here + // Invariant: args.size > 2 + if (args.sizeIs == 1) { + positionedFormulaInternal(args.head, isRightMost) + } else { + positionedFormulaInternal(ConnectorFormula.unsafe(nary, Seq(args.head, ConnectorFormula.unsafe(nary, args.tail))), isRightMost) + } + case _ => positionedFunction(prettyLabel(label, double = true), args.map(a => positionedFormulaInternal(a, isRightMost))) + } + case BinderFormula(label, bound, inner) => + val symbols: Map[BinderLabel, String] = Map( + forall -> p.s.Forall, + exists -> p.s.Exists, + existsOne -> p.s.ExistsOne + ) + def accumulateNested(f: Formula, acc: Seq[VariableLabel]): (Seq[VariableLabel], Formula) = f match { + case BinderFormula(`label`, nestBound, nestInner) => accumulateNested(nestInner, nestBound +: acc) + case _ => (acc, f) + } + val (bounds, innerNested) = accumulateNested(inner, Seq(bound)) + + val innerTree = FrontBranch( + mkSep( + FrontLeaf(s"${symbols(label)}${if (p.symbols == FrontPrintStyle.Ascii || p.symbols == FrontPrintStyle.Latex) " " else ""}${bounds.reverse.map(_.id).mkString(commaSeparator)}${p.s.Dot}"), + positionedFormulaInternal(innerNested, true) + )(spaceSeparator) + ) + bounds.tail.foldLeft(innerTree)((acc, _) => FrontBranch(acc)) + } + + private def positionedExpression(freeVariables: Set[VariableLabel], expression: FrontBranch)(using p: FrontPrintParameters): FrontBranch = { + FrontBranch(expression.children) + } + + /** + * Returns a string representation of this formula. See also [[positionedTerm]]. + * Example output: + * <pre> + * ∀x, y. (∀z. (z ∈ x) ↔ (z ∈ y)) ↔ (x = y) + * </pre> + * @param formula the formula + * @param ascii whether it should be printed in ASCII or unicode + * @param compact whether spaces should be omitted between tokens + * @return the string representation of this formula + */ + def positionedFormula(formula: Formula, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): FrontBranch = { + given FrontPrintParameters = FrontPrintParameters(symbols, compact) + val f = positionedFormulaInternal(formula, true) + val freeVariables = freeVariablesOf(formula) + if (strict) { + require(isFormulaPrintable(formula, freeVariables)) + } + positionedExpression(freeVariables, f) + } + + def prettyFormula(formula: Formula, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false): String = + positionedFormula(formula, symbols, compact).print + + private def positionedTermInternal(term: Term)(using p: FrontPrintParameters): FrontBranch = term match { + // case VariableTerm(label) => FrontBranch(prettyName(label.id)) + case Term(label, args) => + label match { + case `emptySet` => + args match { + case Seq() => positionedFunction(p.s.EmptySet, Seq.empty, dropParentheses = true) + case _ => throw new Error + } + case `unorderedPair` => + args match { + case Seq(l, r) => FrontBranch(p.s.CurlyBracketOpen, positionedTermInternal(l), commaSeparator, positionedTermInternal(r), p.s.CurlyBracketClose) + case _ => throw new Error + } + case `orderedPair` => + args match { + case Seq(l, r) => FrontBranch(p.s.ParenthesisOpen, positionedTermInternal(l), commaSeparator, positionedTermInternal(r), p.s.ParenthesisClose) + case _ => throw new Error + } + + case `powerSet` => + args match { + case Seq(s) => positionedFunction(p.s.PowerSet, Seq(positionedTermInternal(s))) + case _ => throw new Error + } + case `unionSet` => + args match { + case Seq(s) => positionedFunction(p.s.UnionSet, Seq(positionedTermInternal(s))) + case _ => throw new Error + } + case _ => + positionedFunction(prettyLabel(label), args.map(positionedTermInternal(_))) + } + } + + /** + * Returns a string representation of this term. See also [[positionedFormula]]. + * Example output: + * <pre> + * f({w, (x, y)}, z) + * </pre> + * @param term the term + * @param ascii whether it should be printed in ASCII or unicode + * @param compact whether spaces should be omitted between tokens + * @return the string representation of this term + */ + def positionedTerm(term: Term, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): FrontBranch = { + if (strict) { + require(isTermPrintable(term, Set.empty)) // Trivially true + } + positionedTermInternal(term)(using FrontPrintParameters(symbols, compact)) + } + + def prettyTerm(term: Term, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): String = + positionedTerm(term, symbols, compact, strict).print + + private def positionedSequentBase(sequent: SequentBase, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): FrontBranch = { + given p: FrontPrintParameters = FrontPrintParameters(symbols, compact) + val (partialLeft, partialRight) = sequent match { + case _: Sequent => (false, false) + case PartialSequent(_, _, partialLeft, partialRight) => (partialLeft, partialRight) + } + def positionedEllipsis(display: Boolean): Seq[FrontPrintNode] = if (display) Seq(p.s.Ellipsis) else Seq.empty + def sortedFormulas(seq: IndexedSeq[Formula]): IndexedSeq[FrontPrintNode] = + seq.map(positionedFormulaInternal(_, true)).sortBy(_.print) + val (lhs, rhs) = ( + mkSep((positionedEllipsis(partialLeft) ++ sortedFormulas(sequent.left))*)(commaSeparator(p.s.Semicolon)), + mkSep((sortedFormulas(sequent.right) ++ positionedEllipsis(partialRight))*)(commaSeparator(p.s.Semicolon)) + ) + def spaceFor(seq: IndexedSeq[FrontPrintNode]): Seq[FrontPrintNode] = if (seq.nonEmpty) Seq(spaceSeparator) else Seq.empty + val expression = FrontBranch( + ( + lhs ++ spaceFor(lhs) ++ Seq(FrontLeaf(p.s.Turnstile)) ++ spaceFor(rhs) ++ rhs + )* + ) + val freeVariables = freeVariablesOfSequent(sequent) + if (strict) { + require(sequent.formulas.forall(isFormulaPrintable(_, freeVariables))) + } + positionedExpression(freeVariables, expression) + } + + /** + * Returns a string representation of this sequent. + * Example output: + * <pre> + * ⊢ ∀x, y. (∀z. (z ∈ x) ↔ (z ∈ y)) ↔ (x = y) + * </pre> + * @param sequent the sequent + * @param ascii whether it should be printed in ASCII or unicode + * @param compact whether spaces should be omitted between tokens + * @return the string representation of this sequent + */ + def positionedSequent(sequent: Sequent, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): FrontBranch = + positionedSequentBase(sequent, symbols, compact, strict) + + def prettySequent(sequent: Sequent, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): String = + positionedSequent(sequent, symbols, compact, strict).print + + def positionedPartialSequent(sequent: PartialSequent, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): FrontBranch = + positionedSequentBase(sequent, symbols, compact, strict) + + def prettyPartialSequent(sequent: PartialSequent, symbols: FrontPrintStyle = FrontPrintStyle.Unicode, compact: Boolean = false, strict: Boolean = false): String = + positionedPartialSequent(sequent, symbols, compact, strict).print +} diff --git a/lisa-front/src/main/scala/lisa/front/printer/FrontPrintNode.scala b/lisa-front/src/main/scala/lisa/front/printer/FrontPrintNode.scala new file mode 100644 index 00000000..5399cc20 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/printer/FrontPrintNode.scala @@ -0,0 +1,43 @@ +package lisa.front.printer + +/** + * Represents the result of printing expressed as a tree. + * This allows to extract positional information. + */ +sealed abstract class FrontPrintNode { + import FrontPrintNode.* + + def print: String = this match { + case FrontLeaf(value) => value + case FrontBranch(children) => children.map(_.print).mkString + } +} + +object FrontPrintNode { + case class FrontLeaf(value: String) extends FrontPrintNode + case class FrontBranch(children: IndexedSeq[FrontPrintNode]) extends FrontPrintNode { + def locate(pos: Seq[Int]): (Int, Int) = { + def locate(that: FrontBranch, pos: Seq[Int], start: Int): (Int, Int) = pos match { + case h +: t => + val (child, index) = that.children.view.zipWithIndex + .collect { case (branch: FrontBranch, i) => + (branch, i) + } + .drop(h) + .head + val newStart = start + that.children.take(index).map(_.print.length).sum + locate(child, t, newStart) + case _ => + val length = that.children.map(_.print.length).sum + (start, length) + } + locate(this, pos, 0) + } + } + + object FrontBranch { + def apply(children: FrontPrintNode*): FrontBranch = new FrontBranch(children.toIndexedSeq) + } + + given Conversion[String, FrontLeaf] = FrontLeaf.apply +} diff --git a/lisa-front/src/main/scala/lisa/front/printer/FrontPrintParameters.scala b/lisa-front/src/main/scala/lisa/front/printer/FrontPrintParameters.scala new file mode 100644 index 00000000..b7823131 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/printer/FrontPrintParameters.scala @@ -0,0 +1,18 @@ +package lisa.front.printer + +import lisa.front.parser.FrontSymbols + +private[printer] case class FrontPrintParameters(s: FrontSymbols, symbols: FrontPrintStyle, compact: Boolean) { + // export S.* +} + +private[printer] object FrontPrintParameters { + def apply(symbols: FrontPrintStyle, compact: Boolean): FrontPrintParameters = { + val s = symbols match { + case FrontPrintStyle.Ascii => FrontSymbols.FrontAsciiSymbols + case FrontPrintStyle.Unicode => FrontSymbols.FrontUnicodeSymbols + case FrontPrintStyle.Latex => FrontSymbols.FrontLatexSymbols + } + FrontPrintParameters(s, symbols, compact) + } +} diff --git a/lisa-front/src/main/scala/lisa/front/printer/FrontPrintStyle.scala b/lisa-front/src/main/scala/lisa/front/printer/FrontPrintStyle.scala new file mode 100644 index 00000000..59f595ad --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/printer/FrontPrintStyle.scala @@ -0,0 +1,7 @@ +package lisa.front.printer + +enum FrontPrintStyle { + case Ascii + case Unicode + case Latex +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/Proof.scala b/lisa-front/src/main/scala/lisa/front/proof/Proof.scala new file mode 100644 index 00000000..f1b18c22 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/Proof.scala @@ -0,0 +1,28 @@ +package lisa.front.proof + +import lisa.front.printer.FrontPositionedPrinter +import lisa.front.proof.state.* + +/** + * The proof package. + */ +object Proof extends ProofInterfaceDefinitions with RuleDefinitions { + + override protected def pretty(sequent: Sequent): String = FrontPositionedPrinter.prettySequent(sequent) + override protected def pretty(sequent: PartialSequent): String = FrontPositionedPrinter.prettyPartialSequent(sequent) + + val fallback: TacticFallback.type = TacticFallback + val combine: TacticCombine.type = TacticCombine + + val justification: TacticApplyJustification.type = TacticApplyJustification + + extension (tactic: Tactic) { + infix def + : TacticRepeat = TacticRepeat(tactic) + infix def |(other: Tactic): TacticFallback = TacticFallback(Seq(tactic, other)) + infix def ~(other: Tactic): TacticCombine = tactic match { + case TacticCombine(tactics) => TacticCombine(tactics :+ other) + case _ => TacticCombine(Seq(tactic, other)) + } + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/sequent/SequentDefinitions.scala b/lisa-front/src/main/scala/lisa/front/proof/sequent/SequentDefinitions.scala new file mode 100644 index 00000000..d18bc80a --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/sequent/SequentDefinitions.scala @@ -0,0 +1,84 @@ +package lisa.front.proof.sequent + +import lisa.front.fol.FOL.* + +trait SequentDefinitions { + + protected def pretty(sequent: Sequent): String + protected def pretty(sequent: PartialSequent): String + + /** + * The base sequent object; used to represent both the concrete sequent and its partial counterpart. + */ + sealed abstract class SequentBase { + val left: IndexedSeq[Formula] + val right: IndexedSeq[Formula] + + def formulas: IndexedSeq[Formula] = left ++ right + } + + /** + * A sequent is a pair of indexable collections of formulas. + * @param left the left hand side of this sequent + * @param right the right hand side of this sequent + */ + final case class Sequent(left: IndexedSeq[Formula], right: IndexedSeq[Formula]) extends SequentBase { + override def toString: String = pretty(this) + } + + /** + * A partial sequent is a representation of a sequent where only a part of the formulas are known. + * @param left the left hand side of this partial sequent + * @param right the right hand side of this partial sequent + * @param partialLeft whether the left hand side is partial + * @param partialRight whether the right hand side is partial + */ + final case class PartialSequent(left: IndexedSeq[Formula], right: IndexedSeq[Formula], partialLeft: Boolean = true, partialRight: Boolean = true) extends SequentBase { + override def toString: String = pretty(this) + } + + def functionsOfSequent(sequent: SequentBase): Set[TermLabel[?]] = sequent.formulas.flatMap(termLabelsOf).toSet + + def predicatesOfSequent(sequent: SequentBase): Set[PredicateLabel[?]] = sequent.formulas.flatMap(predicatesOf).toSet + + def schematicFunctionsOfSequent(sequent: SequentBase): Set[SchematicTermLabel[?]] = + functionsOfSequent(sequent).collect { case l: SchematicTermLabel[?] => l } + + def schematicPredicatesOfSequent(sequent: SequentBase): Set[SchematicPredicateLabel[?]] = + predicatesOfSequent(sequent).collect { case l: SchematicPredicateLabel[?] => l } + + def schematicConnectorsOfSequent(sequent: SequentBase): Set[SchematicConnectorLabel[?]] = + sequent.formulas.flatMap(schematicConnectorsOf).toSet + + def freeVariablesOfSequent(sequent: SequentBase): Set[VariableLabel] = sequent.formulas.flatMap(freeVariablesOf).toSet + + def declaredBoundVariablesOfSequent(sequent: SequentBase): Set[VariableLabel] = + sequent.formulas.flatMap(declaredBoundVariablesOf).toSet + + def isSequentWellFormed(sequent: SequentBase): Boolean = + sequent.formulas.forall(isWellFormed) + + // Only full sequents should be converted to the kernel + def sequentToKernel(sequent: Sequent): lisa.kernel.proof.SequentCalculus.Sequent = + lisa.kernel.proof.SequentCalculus.Sequent( + sequent.left.map(toKernel).toSet, + sequent.right.map(toKernel).toSet + ) + + given Conversion[Sequent, lisa.kernel.proof.SequentCalculus.Sequent] = sequentToKernel + + def isSameSequent(s1: Sequent, s2: Sequent): Boolean = + lisa.kernel.proof.SequentCalculus.isSameSequent(s1, s2) + + def instantiateSequentSchemas( + sequent: Sequent, + functions: Seq[AssignedFunction] = Seq.empty, + predicates: Seq[AssignedPredicate] = Seq.empty, + connectors: Seq[AssignedConnector] = Seq.empty + ): Sequent = { + def instantiate(formulas: IndexedSeq[Formula]): IndexedSeq[Formula] = + formulas.map(instantiateFormulaSchemas(_, functions, predicates, connectors)) + Sequent(instantiate(sequent.left), instantiate(sequent.right)) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/sequent/SequentOps.scala b/lisa-front/src/main/scala/lisa/front/proof/sequent/SequentOps.scala new file mode 100644 index 00000000..c6287002 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/sequent/SequentOps.scala @@ -0,0 +1,64 @@ +package lisa.front.proof.sequent + +import lisa.front.fol.FOL.* +import lisa.front.proof.sequent.SequentDefinitions + +trait SequentOps extends SequentDefinitions { + + protected trait IndexedSeqConverter[S, T] { + def apply(t: T): IndexedSeq[S] + } + + given [S]: IndexedSeqConverter[S, Unit] with { + override def apply(u: Unit): IndexedSeq[S] = IndexedSeq.empty + } + given [S]: IndexedSeqConverter[S, EmptyTuple] with { + override def apply(t: EmptyTuple): IndexedSeq[S] = IndexedSeq.empty + } + given [S, H <: S, T <: Tuple](using converter: IndexedSeqConverter[S, T]): IndexedSeqConverter[S, H *: T] with { + override def apply(t: H *: T): IndexedSeq[S] = t.head +: converter(t.tail) + } + given givenTupleValueConversion[S, H, T <: Tuple](using tupleConverter: IndexedSeqConverter[S, T], valueConverter: Conversion[H, S]): IndexedSeqConverter[S, H *: T] with { + override def apply(t: H *: T): IndexedSeq[S] = valueConverter(t.head) +: tupleConverter(t.tail) + } + given [S, T <: S]: IndexedSeqConverter[S, T] with { + override def apply(f: T): IndexedSeq[S] = IndexedSeq(f) + } + given givenValueConversion[S, T](using converter: Conversion[T, S]): IndexedSeqConverter[S, T] with { + override def apply(f: T): IndexedSeq[S] = IndexedSeq(f: S) + } + given [S, I <: Iterable[S]]: IndexedSeqConverter[S, I] with { + override def apply(s: I): IndexedSeq[S] = s.toIndexedSeq + } + + protected def any2seq[S, A, T <: A](any: T)(using converter: IndexedSeqConverter[S, T]): IndexedSeq[S] = converter(any) + + extension [T1](left: T1)(using IndexedSeqConverter[Formula, T1]) { + infix def |-[T2](right: T2)(using IndexedSeqConverter[Formula, T2]): Sequent = Sequent(any2seq(left), any2seq(right)) + } + + object |- { + def apply[T](right: T)(using IndexedSeqConverter[Formula, T]): Sequent = Sequent(IndexedSeq.empty, any2seq(right)) + infix def unapply(sequent: Sequent): Some[(IndexedSeq[Formula], IndexedSeq[Formula])] = + Some((sequent.left, sequent.right)) + } + + extension [T1](left: T1)(using IndexedSeqConverter[Formula, T1]) { + infix def ||-[T2](right: T2)(using IndexedSeqConverter[Formula, T2]): PartialSequent = PartialSequent(any2seq(left), any2seq(right)) + } + + object ||- { + def apply[T](right: T)(using IndexedSeqConverter[Formula, T]): PartialSequent = PartialSequent(IndexedSeq.empty, any2seq(right)) + infix def unapply(sequent: PartialSequent): Some[(IndexedSeq[Formula], IndexedSeq[Formula])] = + Some((sequent.left, sequent.right)) + } + + type KernelSequent = lisa.kernel.proof.SequentCalculus.Sequent + extension (s: KernelSequent) { + infix def +<(f: Formula): KernelSequent = s.copy(left = s.left + f) + infix def -<(f: Formula): KernelSequent = s.copy(left = s.left - f) + infix def +>(f: Formula): KernelSequent = s.copy(right = s.right + f) + infix def ->(f: Formula): KernelSequent = s.copy(right = s.right - f) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/state/ProofEnvironmentDefinitions.scala b/lisa-front/src/main/scala/lisa/front/proof/state/ProofEnvironmentDefinitions.scala new file mode 100644 index 00000000..62c774bf --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/state/ProofEnvironmentDefinitions.scala @@ -0,0 +1,224 @@ +package lisa.front.proof.state + +import lisa.front.fol.FOL.* +import lisa.kernel.proof.RunningTheory +import lisa.kernel.proof.RunningTheoryJudgement.* +import lisa.kernel.proof.SCProof +import lisa.kernel.proof.SCProofChecker +import lisa.kernel.proof.SequentCalculus.SCSubproof +import lisa.kernel.proof.SequentCalculus.sequentToFormula +import lisa.utils.Printer +import lisa.utils.ProofsShrink + +trait ProofEnvironmentDefinitions extends ProofStateDefinitions { + + import scala.collection.mutable + + /** + * The proof environment represents a mutable context with axioms and theorems. + * It is analogous to the kernel's [[RunningTheory]], but adapted to the front and with additional safety guarantees and utilities. + * @param runningTheory the kernel's theory + */ + final class ProofEnvironment( + val runningTheory: RunningTheory // For now, doesn't need to be generically typed + ) extends ReadableProofEnvironment { + private[ProofEnvironmentDefinitions] val proven: mutable.Map[Sequent, Seq[(Justified, runningTheory.Justification)]] = mutable.Map.empty + + private def addOne(sequent: Sequent, justified: Justified, kernelJustification: runningTheory.Justification): Unit = { + if (!proven.contains(sequent)) { + proven.addOne(sequent, Seq.empty) + } + proven.addOne(sequent, proven(sequent) :+ (justified, kernelJustification)) + } + + // Lift the initial axioms + runningTheory.axiomsList.foreach { kernelAxiom => + val frontAxiom = Axiom(this, fromKernel(kernelAxiom.ax)) + addOne(frontAxiom.sequent, frontAxiom, kernelAxiom) + } + + override def contains(sequent: Sequent): Boolean = proven.contains(sequent) + + def belongsToTheory(label: ConstantFunctionLabel[?]): Boolean = runningTheory.isSymbol(toKernel(label)) + def belongsToTheory(label: ConstantPredicateLabel[?]): Boolean = runningTheory.isSymbol(toKernel(label)) + def belongsToTheory(term: Term): Boolean = + termLabelsOf(term).collect { case f: ConstantFunctionLabel[?] => f }.forall(belongsToTheory) + def belongsToTheory(formula: Formula): Boolean = + termLabelsOf(formula).collect { case f: ConstantFunctionLabel[?] => f }.forall(belongsToTheory) && + predicatesOf(formula).collect { case p: ConstantPredicateLabel[?] => p }.forall(belongsToTheory) + def belongsToTheory(sequent: SequentBase): Boolean = + sequent.left.forall(belongsToTheory) && sequent.right.forall(belongsToTheory) + + private def addSequentToEnvironment(sequent: Sequent, scProof: SCProof, justifiedImports: Map[Int, Sequent]): Theorem = { + require(scProof.imports.size == justifiedImports.size && scProof.imports.indices.forall(justifiedImports.contains), "All imports must be justified") + require(isAcceptedSequent(sequent)(this), "Invalid conclusion") + require( + lisa.kernel.proof.SequentCalculus.isSameSequent(sequentToKernel(sequent), scProof.conclusion), + "Error: the proof conclusion does not match the provided sequent" + ) + val judgement = SCProofChecker.checkSCProof(scProof) + if (!judgement.isValid) { + throw new AssertionError( + Seq( + "Error: the theorem was found to produce an invalid proof; this could indicate a problem with a tactic or a bug in the implementation", + "The produced proof is shown below for reference:", + Printer.prettySCProof(judgement) + ).mkString("\n") + ) + } + + val justificationPairs = scProof.imports.indices.map(justifiedImports).map(proven).map(_.head) + val justifications = justificationPairs.map { case (justification, _) => justification } + + val kernelJustifications = justificationPairs.map { case (_, kernelJustification) => kernelJustification } + val kernelTheorem = runningTheory.makeTheorem(s"t${proven.size}", scProof.conclusion, scProof, kernelJustifications) match { + case ValidJustification(result) => result + case InvalidJustification(_, _) => throw new Error // Should have been caught before + } + + val theorem = Theorem(this, sequent, scProof, justifications) + addOne(sequent, theorem, kernelTheorem) // TODO should we salvage existing theorems instead of creating new ones? + + theorem + } + def mkTheorem(proof: Proof): Theorem = { + require(proof.initialState.goals.sizeIs == 1, "The proof must start with exactly one goal") + val sequent = proof.initialState.goals.head + evaluateProof(proof)(this) match { + case Some(proofModeState) => + val (scProof, theoremImports) = reconstructSCProof(proofModeState) + addSequentToEnvironment(sequent, scProof, theoremImports) + case None => throw new Exception // Failure in evaluating the proof + } + } + def mkAxiom(formula: Formula): Axiom = { + require(runningTheory.isAxiom(formula)) + Axiom(this, formula) + } + // def mkDefinition() // TODO + def mkTheorem(sequent: Sequent, scProof: SCProof, theorems: IndexedSeq[Justified]): Theorem = + addSequentToEnvironment(sequent, scProof, theorems.map(_.sequent).zipWithIndex.map(_.swap).toMap) + // override def toString: String = proven.keySet.toSeq.map(Theorem(this, _)).map(_.toString).mkString("\n") + } + + def newEmptyEnvironment(): ProofEnvironment = new ProofEnvironment(new RunningTheory) + + /** + * A justified statement with respect to a theory is a sequent that is accepted by this theory. + */ + sealed abstract class Justified extends ReadableJustified { + private[proof] val environment: ProofEnvironment + def sequent: Sequent + final def sequentAsKernel: lisa.kernel.proof.SequentCalculus.Sequent = sequentToKernel(sequent) + } + + /** + * An axiom is a justified statement that is admitted without a proof. + * It is guaranteed that this sequent has exactly one conclusion and no assumptions. + * @param formula the original formula + */ + case class Axiom private[ProofEnvironmentDefinitions] (environment: ProofEnvironment, formula: Formula) extends Justified { + override def sequent: Sequent = () |- formula + override def toString: String = s"Axiom: $sequent" + } + + /** + * A theorem is a justified statement which has an associated proof depending on other justified statements. + * @param proof the proof of this theorem + * @param justifications the dependencies of this theorem (= assumptions) + */ + case class Theorem private[ProofEnvironmentDefinitions] (environment: ProofEnvironment, sequent: Sequent, proof: SCProof, justifications: IndexedSeq[Justified]) extends Justified { + override def toString: String = s"Theorem: $sequent" + } + + // Borrowed from past work: https://github.com/FlorianCassayre/competitive-scala + private def topologicalSort[U](start: U, adjacency: Map[U, Set[U]]): Seq[U] = { + def dfs(stack: Seq[(U, Set[U])], marks: Map[U, Boolean], sorted: Seq[U]): (Map[U, Boolean], Seq[U]) = { + stack match { + case (u, adjacent) +: tail => + adjacent.headOption match { + case Some(v) => + marks.get(v) match { + case Some(false) => throw new Exception // Cycle + case Some(true) => dfs((u, adjacent.tail) +: tail, marks, sorted) + case None => dfs((v, adjacency.getOrElse(v, Set.empty[U])) +: (u, adjacent.tail) +: tail, marks + (v -> false), sorted) + } + case None => dfs(tail, marks + (u -> true), u +: sorted) + } + case _ => (marks, sorted) + } + } + val (_, sorted) = dfs(Seq((start, adjacency.getOrElse(start, Set.empty[U]))), Map(start -> false), Seq.empty) + sorted + } + + /** + * Converts a theorem into a kernel proof where the imports are the assumption of that theorem. + * @param theorem the theorem to convert + * @return a kernel proof + */ + def reconstructPartialSCProofForTheorem(theorem: Theorem): SCProof = theorem.proof // (that's it) + + /** + * Converts a theorem into a kernel proof where the imports are all axioms of that theory. + * Essentially inlines all dependent theorems recursively into a single, fat proof. + * @param theorem the theorem to convert + * @return a kernel proof + */ + def reconstructSCProofForTheorem(theorem: Theorem): SCProof = { + // Inefficient, no need to traverse/reconstruct the whole graph + val environment = theorem.environment + val theorems = environment.proven.values + .flatMap(_.collect { case (theorem: Theorem, _) => + theorem + }) + .toSeq + val sortedTheorems = topologicalSort( + theorem, + theorems + .map(theorem => (theorem, theorem.justifications.collect { case other: Theorem => other }.toSet) // This will have to be updated for definitions + ) + .toMap + .withDefaultValue(Set.empty) + ).reverse + val sortedAxioms = sortedTheorems + .flatMap(_.justifications.collect { case ax: Axiom => ax }) + .toSet + .map(_.sequent) + .toIndexedSeq + .sortBy(_.toString) + val sequentToImport = sortedAxioms.zipWithIndex.toMap.view.mapValues(i => -(i + 1)).toMap + + assert(sortedTheorems.lastOption.contains(theorem)) + val sequentToIndex = sortedTheorems + .map(_.sequent) + .zipWithIndex + .reverse // This step is important: in case of duplicate nodes, this ensures we have no forward references + .toMap ++ sequentToImport + + assert(sortedTheorems.zipWithIndex.forall { case (thm, i) => thm.justifications.map(_.sequent).forall(s => sequentToIndex.get(s).exists(_ < i)) }) + + val scProof = SCProof( + sortedTheorems.map(theorem => SCSubproof(theorem.proof, theorem.justifications.map(_.sequent).map(sequentToIndex))).toIndexedSeq, + sortedAxioms.map(sequentToKernel) + ) + + assert(scProof.conclusion == sequentToKernel(theorem.sequent)) + + val judgement = SCProofChecker.checkSCProof(scProof) + if (!judgement.isValid) { + throw new AssertionError( + Seq( + "Error: the reconstructed proof was found to be invalid; this could indicate a bug in the implementation of this very method", + "The reconstructed proof is shown below for reference:", + Printer.prettySCProof(judgement) + ).mkString("\n") + ) + } + + val optimized = ProofsShrink.optimizeProofIteratively(scProof) + assert(SCProofChecker.checkSCProof(optimized).isValid) // Assertion failure means a bug in `SCUtils` + optimized + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/state/ProofInterfaceDefinitions.scala b/lisa-front/src/main/scala/lisa/front/proof/state/ProofInterfaceDefinitions.scala new file mode 100644 index 00000000..f936a395 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/state/ProofInterfaceDefinitions.scala @@ -0,0 +1,76 @@ +package lisa.front.proof.state + +import lisa.front.printer.FrontPositionedPrinter + +trait ProofInterfaceDefinitions extends ProofEnvironmentDefinitions { + + private def prettyFrame(string: String, verticalPadding: Int = 0, horizontalPadding: Int = 2): String = { + val (space, vertical, horizontal, corner) = (' ', '|', '-', '+') + val lines = string.split("\n") + val maxLength = lines.map(_.length).max + val bottomAndTop = (corner +: Seq.fill(maxLength + 2 * horizontalPadding)(horizontal) :+ corner).mkString + val bottomAndTopMargin = (vertical +: Seq.fill(maxLength + 2 * horizontalPadding)(space) :+ vertical).mkString + val linesMargin = + lines.map(line => Seq(vertical) ++ Seq.fill(horizontalPadding)(space) ++ line.toCharArray ++ Seq.fill(maxLength - line.length + horizontalPadding)(space) ++ Seq(vertical)).map(_.mkString) + (Seq(bottomAndTop) ++ Seq.fill(verticalPadding)(bottomAndTopMargin) ++ linesMargin ++ Seq.fill(verticalPadding)(bottomAndTopMargin) ++ Seq(bottomAndTop)).mkString("\n") + } + + /** + * The proof mode represents an interface for [[ProofModeState]]. + * It is stateful, and as such should be mutated using the commands available, e.g. [[apply]]. + * It is interactive, in the sense that the command applications print information in the standard output. + * When no proof goal remains, a theorem can be obtained with [[asTheorem]]. + */ + case class ProofMode private (private var currentState: ProofModeState) { + def state: ProofState = currentState.state + def proving: ProofState = currentState.proving + def apply(mutator: ProofModeStateMutator): Boolean = { + print(s"Trying to apply '${mutator.getClass.getSimpleName}'...") + val result = mutator.applyMutator(currentState) match { + case Some(newState) => + println(" [ok]") + currentState = newState + true + case None => + println(" [!!! failure !!!]") + false + } + println() + println(prettyFrame(currentState.state.toString)) + println() + result + } + def focus(goal: Int): Boolean = apply(TacticFocusGoal(goal)) + def back(): Boolean = apply(CancelPreviousTactic) + def repeat(tactic: Tactic): Unit = apply(TacticRepeat(tactic)) + def applyOne(tactics: Tactic*): Boolean = apply(TacticFallback(tactics)) + def reset(): Unit = apply(CancelPreviousTactic) + def asTheorem(): Theorem = { + require(state.goals.isEmpty, "The proof is incomplete and thus cannot be converted into a theorem") + val env = currentState.environment + val theorem = env.mkTheorem(Proof(proving.goals*)(currentState.tactics*)) + theorem.display() + } + override def toString: String = + (Seq("subgoals:", currentState.state.toString) ++ Seq("proving:", currentState.proving.toString)).mkString("\n") + } + object ProofMode { + def apply(goals: Sequent*)(using environment: ProofEnvironment): ProofMode = { + val initial = ProofMode(initialProofModeState(goals*)(environment)) + println("Entering proof mode") + println() + println(prettyFrame(initial.state.toString)) + println() + initial + } + } + + extension [T <: Justified](justified: T) { + def display(): T = { + println(justified) + println() + justified + } + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/state/ProofStateDefinitions.scala b/lisa-front/src/main/scala/lisa/front/proof/state/ProofStateDefinitions.scala new file mode 100644 index 00000000..ce1a6f48 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/state/ProofStateDefinitions.scala @@ -0,0 +1,364 @@ +package lisa.front.proof.state + +import lisa.front.fol.FOL.* +import lisa.front.proof.sequent.SequentDefinitions +import lisa.front.proof.sequent.SequentOps +import lisa.kernel.proof.SCProof +import lisa.kernel.proof.SequentCalculus.Rewrite +import lisa.kernel.proof.SequentCalculus.SCProofStep +import lisa.kernel.proof.SequentCalculus.SCSubproof +import lisa.utils.ProofsShrink + +trait ProofStateDefinitions extends SequentDefinitions with SequentOps { + + /** + * A proof in the front. + * It corresponds to an initial state represented a sequence of goals, and a sequence of tactics to be applied. + * Note that the tactics do not necessarily eliminate all goals. + * @param initialState the initial state + * @param steps the tactics + */ + case class Proof(initialState: ProofState, steps: Seq[Tactic]) + object Proof { + def apply(goals: Sequent*)(steps: Tactic*): Proof = Proof(ProofState(goals.toIndexedSeq), steps) + } + + /** + * The proof state is a sequence of proof goals, which are themselves sequents. + * @param goals the goals in this state + */ + final case class ProofState(goals: IndexedSeq[Sequent]) { + override def toString: String = + ((if (goals.nonEmpty) s"${goals.size} goal${if (goals.sizeIs > 1) "s" else ""}" else "[zero goals]") +: + goals.map(_.toString).map(s => s"- $s")).mkString("\n") + } + object ProofState { + def apply(goals: Sequent*): ProofState = ProofState(goals.toIndexedSeq) + } + + type MutatorResult = Option[ProofModeState] + private[ProofStateDefinitions] type TacticResult = Option[Seq[(AppliedTactic, ProofStateSnapshot)]] + + /** + * A general class that describes a mutation on the proof mode state. + */ + sealed abstract class ProofModeStateMutator { + def applyMutator(proofModeState: ProofModeState): MutatorResult + } + case object CancelPreviousTactic extends ProofModeStateMutator { + override def applyMutator(proofModeState: ProofModeState): MutatorResult = + proofModeState.steps match { + case _ +: previousSteps => + Some(proofModeState.copy(steps = previousSteps)) + case _ => None + } + } + case object ResetProofMode extends ProofModeStateMutator { + override def applyMutator(proofModeState: ProofModeState): MutatorResult = + Some(proofModeState.copy(steps = Seq.empty)) + } + + /** + * A tactic is a function that can transform a proof state into a new proof state. + * When applied it returns an [[AppliedTactic]] object along with the new state. + */ + sealed abstract class Tactic extends ProofModeStateMutator { + override final def applyMutator(proofModeState: ProofModeState): MutatorResult = + applySnapshot(proofModeState.lastSnapshot, proofModeState.environment).map(steps => proofModeState.withNewSteps(steps)) + private[ProofStateDefinitions] def applySnapshot(snapshot: ProofStateSnapshot, env: ProofEnvironment): TacticResult + } + case class TacticFocusGoal(goal: Int) extends Tactic { + override private[ProofStateDefinitions] def applySnapshot(snapshot: ProofStateSnapshot, env: ProofEnvironment): TacticResult = { + if (snapshot.proofState.goals.indices.contains(goal)) { + // This function moves the element of index `goal` to the front + def bringToFront[T](goals: IndexedSeq[T]): IndexedSeq[T] = + goals(goal) +: (goals.take(goal) ++ goals.drop(goal + 1)) + val newProofState = ProofState(bringToFront(snapshot.proofState.goals)) + val newShadowProofState = bringToFront(snapshot.shadowProofState) + Some( + Seq( + ( + AppliedTactic(-1, this, () => IndexedSeq.empty, false, Map.empty), + ProofStateSnapshot(newProofState, newShadowProofState, snapshot.nextId) + ) + ) + ) + } else { + None + } + } + } + case class TacticRepeat(tactic: Tactic) extends Tactic { + override private[ProofStateDefinitions] def applySnapshot(snapshot: ProofStateSnapshot, env: ProofEnvironment): TacticResult = { + def repeat(currentSnapshot: ProofStateSnapshot, acc: Seq[(AppliedTactic, ProofStateSnapshot)], executed: Boolean): TacticResult = { + tactic.applySnapshot(currentSnapshot, env) match { + case Some(seq) if seq.nonEmpty => + val reversed = seq.reverse + repeat(reversed.head._2, reversed ++ acc, true) + case _ => if (executed) Some(acc.reverse) else None + } + } + repeat(snapshot, Seq.empty, true) + } + } + case class TacticFallback(tactics: Seq[Tactic]) extends Tactic { + override private[ProofStateDefinitions] def applySnapshot(snapshot: ProofStateSnapshot, env: ProofEnvironment): TacticResult = { + def iteratedTry(remaining: Seq[Tactic]): TacticResult = remaining match { + case tactic +: tail => + tactic.applySnapshot(snapshot, env) match { + case Some(result) => Some(result) + case None => iteratedTry(tail) + } + case _ => None + } + iteratedTry(tactics) + } + } + case class TacticCombine(tactics: Seq[Tactic]) extends Tactic { + override private[ProofStateDefinitions] def applySnapshot(snapshot: ProofStateSnapshot, env: ProofEnvironment): TacticResult = { + def iterated(remaining: Seq[Tactic], currentSnapshot: ProofStateSnapshot, acc: Seq[(AppliedTactic, ProofStateSnapshot)]): TacticResult = remaining match { + case tactic +: tail => + tactic.applySnapshot(currentSnapshot, env) match { + case Some(result) => + val reversed = result.reverse + val newSnapshot = reversed.headOption match { + case Some((_, head)) => head + case None => currentSnapshot + } + iterated(tail, newSnapshot, reversed ++ acc) + case None => None + } + case _ => Some(acc.reverse) + } + iterated(tactics, snapshot, Seq.empty) + } + } + + /** + * A particular case of tactic that works on a single goal. + */ + sealed abstract class TacticGoal extends Tactic { + override private[ProofStateDefinitions] def applySnapshot(snapshot: ProofStateSnapshot, env: ProofEnvironment): TacticResult = { + (snapshot.proofState.goals, snapshot.shadowProofState) match { + case (proofGoal +: tailGoals, id +: tailShadowProofState) => + applyGoal(proofGoal, env) match { + case Some(opt) => + val (newGoalsOrJustifications, reconstruct) = opt.getOrElse((IndexedSeq.empty, () => IndexedSeq.empty)) + val newGoals = newGoalsOrJustifications.map { + case Left(sequent) => sequent + case Right(justified) => justified.sequent + } + val newGoalsShown = newGoalsOrJustifications.collect { case Left(sequent) => + sequent + } + // We prepend the newly created goals + val newProofState = ProofState(newGoalsShown ++ tailGoals) + // Number of goals that have been created (or updated), possibly zero + // This corresponds to the number of premises in that rule + val nReplacedGoals = newGoals.size + val newShadowGoals = snapshot.nextId until (snapshot.nextId + nReplacedGoals) + val newShadowGoalsShown = newShadowGoals.zip(newGoalsOrJustifications).collect { case (i, Left(_)) => i } + // Updated shadow proof state (= ids for the new proof state) + val newShadowProofState = newShadowGoalsShown ++ tailShadowProofState + // Since we created n new goals, we must increment the counter by n + val newNextId = snapshot.nextId + nReplacedGoals + + val justifications = newShadowGoals.zip(newGoalsOrJustifications).collect { case (i, Right(justified)) => (i, justified) }.toMap + + Some(Seq((AppliedTactic(id, this, reconstruct, opt.isEmpty, justifications), ProofStateSnapshot(newProofState, newShadowProofState, newNextId)))) + case None => None + } + case _ => None + } + } + def applyGoal(proofGoal: Sequent, env: ProofEnvironment): Option[Option[(IndexedSeq[Either[Sequent, Justified]], ReconstructSteps)]] + } + case class TacticApplyJustification(justified: Justified) extends TacticGoal { + override def applyGoal(proofGoal: Sequent, env: ProofEnvironment): Option[Option[(IndexedSeq[Either[Sequent, Justified]], ReconstructSteps)]] = { + if (justified.environment == env && justified.sequent == proofGoal && env.contains(proofGoal)) { + Some(None) + } else { + None + } + } + } + + type ReconstructSteps = () => IndexedSeq[SCProofStep] + + // The premises indexing is implicit: + // * 0, 1, 2 will reference respectively the first, second and third steps in that array + + abstract class TacticGoalFunctionalPruning extends TacticGoal { + override def applyGoal(proofGoal: Sequent, env: ProofEnvironment): Option[Option[(IndexedSeq[Either[Sequent, Justified]], ReconstructSteps)]] = + apply(proofGoal).map(result => Some(result)) + def apply(proofGoal: Sequent): Option[(IndexedSeq[Either[Sequent, Justified]], ReconstructSteps)] + } + + abstract class TacticGoalFunctional extends TacticGoal { + override def applyGoal(proofGoal: Sequent, env: ProofEnvironment): Option[Option[(IndexedSeq[Either[Sequent, Justified]], ReconstructSteps)]] = + apply(proofGoal).map { case (sequent, reconstruct) => Some((sequent.map(Left.apply), reconstruct)) } + def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] + } + + trait ReadableProofEnvironment { + def contains(sequent: Sequent): Boolean + def belongsToTheory(sequent: SequentBase): Boolean + } + + type ProofEnvironment <: ReadableProofEnvironment + + trait ReadableJustified { + private[proof] def environment: ProofEnvironment + def sequent: Sequent + } + type Justified <: ReadableJustified + + private[ProofStateDefinitions] case class ProofStateSnapshot( + proofState: ProofState, + shadowProofState: IndexedSeq[Int], + nextId: Int + ) + + private[ProofStateDefinitions] case class AppliedTactic(id: Int, tactic: Tactic, reconstruct: ReconstructSteps, isTheorem: Boolean, toClose: Map[Int, Justified]) + + /** + * The proof mode state represents a backward proof builder. + * It is initialized by specifying a sequent (the starting goal). + * Applied tactics may be appended using the method [[withNewSteps]]. + * See [[reconstructSCProof]] for the conversion of this object into a kernel proof. + * @param initialSnapshot + * @param steps + * @param environment + */ + case class ProofModeState private[ProofStateDefinitions] ( + private[ProofStateDefinitions] val initialSnapshot: ProofStateSnapshot, + private[ProofStateDefinitions] val steps: Seq[Seq[(AppliedTactic, ProofStateSnapshot)]], // Steps are in reverse direction (the first element is the latest) + environment: ProofEnvironment + ) { + private[ProofStateDefinitions] def lastSnapshot: ProofStateSnapshot = + steps.view.flatMap(_.lastOption).headOption.map { case (_, snapshot) => snapshot }.getOrElse(initialSnapshot) + private[ProofStateDefinitions] def zippedSteps: Seq[(ProofStateSnapshot, AppliedTactic, ProofStateSnapshot)] = { + val flatSteps = steps.flatMap(_.reverse) + val snapshots = flatSteps.map { case (_, snapshot) => snapshot } :+ initialSnapshot + snapshots.zip(snapshots.tail).zip(flatSteps.map { case (applied, _) => applied }).map { case ((snapshotAfter, snapshotBefore), applied) => + (snapshotBefore, applied, snapshotAfter) + } + } + private[ProofStateDefinitions] def withNewSteps(step: Seq[(AppliedTactic, ProofStateSnapshot)]): ProofModeState = + copy(steps = step +: steps) + + def state: ProofState = lastSnapshot.proofState + def proving: ProofState = initialSnapshot.proofState + def tactics: Seq[Tactic] = steps.reverse.flatten.map { case (AppliedTactic(_, tactic, _, _, _), _) => tactic } + } + + /** + * Evaluates a proof by converting tactics to applied tactics. + * @param proof the proof to evaluate + * @param environment the environment + * @return an optional final proof mode state after applying all the tactics + */ + def evaluateProof(proof: Proof)(environment: ProofEnvironment): Option[ProofModeState] = { + def applyTactics(tactics: Seq[Tactic], proofModeState: ProofModeState): Option[ProofModeState] = tactics match { + case tactic +: rest => + tactic.applyMutator(proofModeState) match { + case Some(newProofModeState) => applyTactics(rest, newProofModeState) + case None => None + } + case _ => Some(proofModeState) + } + applyTactics(proof.steps, initialProofModeState(proof.initialState.goals*)(environment)) + } + + /** + * Reconstructs a kernel proof from an instance of a [[ProofModeState]]. + * The passed mode can still contain goals, in that case they be included as imports. + * @param proofModeState the proof mode to use + * @return the final proof, and a mapping from imports to the theorems used + */ + def reconstructSCProof(proofModeState: ProofModeState): (SCProof, Map[Int, Sequent]) = { + val proofEnvironment = proofModeState.environment + // Each proof goal that is created (or updated) will be given a unique id + // Then we use these ids to refer to them as a proof step in the SC proof + + // For a complete proof the proof state should be empty + // However for testing purposes we may still allow incomplete proofs to exist, + // and for that we should convert unclosed branches into imports + val imports = proofModeState.lastSnapshot.proofState.goals.map(sequentToKernel) + val initialTranslation = proofModeState.lastSnapshot.shadowProofState.zipWithIndex.map { case (v, i) => v -> -(i + 1) }.toMap + + val (finalProof, _, finalTheorems) = proofModeState.zippedSteps.foldLeft((SCProof(IndexedSeq.empty, imports), initialTranslation, Map.empty[Int, Sequent])) { + case ((proof, translation, theorems), (snapshotBefore, applied, snapshotAfter)) => + val reconstructedSteps = applied.reconstruct() + val isTheorem = applied.isTheorem + val nReplacedGoals = snapshotAfter.nextId - snapshotBefore.nextId // TODO do not rely on the ids for that + val id = applied.id // TODO + val updatedGoal = snapshotBefore.proofState.goals.head + + val sortedClosed = applied.toClose.toSeq.sortBy(_._1) + val newTheorems = theorems ++ sortedClosed.zipWithIndex.map { case ((_, justified), i) => + (proof.imports.size + i) -> justified.sequent + }.toMap + val newTranslation = translation ++ sortedClosed.zipWithIndex.map { case ((id, _), j) => + id -> -(proof.imports.size + j + 1) + } + val newImports = proof.imports ++ sortedClosed.map(_._2.sequent).map(sequentToKernel) + val newProof0 = proof.copy(imports = newImports) + + val premises = (snapshotBefore.nextId until snapshotAfter.nextId).map(newTranslation) + val reconstructedAndRemappedStep = + if (reconstructedSteps.nonEmpty) + Some( + SCSubproof( + SCProof(reconstructedSteps, premises.map(newProof0.getSequent)), + premises + ) + ) + else + None + val newProof = newProof0.withNewSteps(reconstructedAndRemappedStep.toIndexedSeq) + + // We return the expanded proof, along with the information to recover the last (= current) step as a premise + if (isTheorem) { + val importId = newProof.imports.size + val translatedId = -(importId + 1) + ( + newProof.copy(imports = newProof.imports :+ sequentToKernel(updatedGoal)), + newTranslation + (id -> translatedId), + newTheorems + (importId -> updatedGoal) + ) + } else { + val translatedId = newProof.steps.size - 1 + ( + newProof, + newTranslation + (id -> translatedId), + newTheorems + ) + } + } + + (ProofsShrink.flattenProof(finalProof), finalTheorems) + } + + // The final conclusion is given the id 0, although it will never be referenced as a premise + def initialProofModeState(goals: Sequent*)(environment: ProofEnvironment): ProofModeState = { + require(goals.forall(isAcceptedSequent(_)(environment))) + ProofModeState(ProofStateSnapshot(ProofState(goals*), 0 until goals.size, goals.size), Seq.empty, environment) + } + + def isAcceptedSequent(sequent: Sequent)(environment: ProofEnvironment): Boolean = { + isSequentWellFormed(sequent) && schematicConnectorsOfSequent(sequent).isEmpty && environment.belongsToTheory(sequent) // TODO is printable + } + + /** + * A helper module that provides common symbols for usage in rules. + */ + object Notations { + val (a, b, c, d, e) = (SchematicPredicateLabel[0]("a"), SchematicPredicateLabel[0]("b"), SchematicPredicateLabel[0]("c"), SchematicPredicateLabel[0]("d"), SchematicPredicateLabel[0]("e")) + val (s, t, u) = (SchematicTermLabel[0]("s"), SchematicTermLabel[0]("t"), SchematicTermLabel[0]("u")) + val f: SchematicConnectorLabel[1] = SchematicConnectorLabel[1]("f") + val p: SchematicPredicateLabel[1] = SchematicPredicateLabel[1]("p") + val (x, y) = (VariableLabel("x"), VariableLabel("y")) + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/state/RuleDefinitions.scala b/lisa-front/src/main/scala/lisa/front/proof/state/RuleDefinitions.scala new file mode 100644 index 00000000..114edf04 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/state/RuleDefinitions.scala @@ -0,0 +1,168 @@ +package lisa.front.proof.state + +import lisa.front.fol.FOL.* +import lisa.front.proof.unification.UnificationUtils +import lisa.kernel.proof.SequentCalculus.SCProofStep +import lisa.kernel.proof.SequentCalculus.SCSubproof + +import scala.collection.View + +trait RuleDefinitions extends ProofEnvironmentDefinitions with UnificationUtils { + + type ReconstructRule = PartialFunction[(lisa.kernel.proof.SequentCalculus.Sequent, UnificationContext), IndexedSeq[SCProofStep]] + + /** + * The parameters to instantiate a rule into a tactic (see [[RuleTactic]]). + * @param selectors the correspondence between patterns and values, can be partial + * @param functions a partial assignment of functions + * @param predicates a partial assignment of predicates + * @param connectors a partial assignment of connectors + * @param variables a partial assignment of free variables + */ + case class RuleParameters( + selectors: Map[Int, SequentSelector] = Map.empty, + functions: Seq[AssignedFunction] = Seq.empty, + predicates: Seq[AssignedPredicate] = Seq.empty, + connectors: Seq[AssignedConnector] = Seq.empty, + variables: Map[VariableLabel, VariableLabel] = Map.empty + ) { + def withIndices(i: Int)(left: Int*)(right: Int*): RuleParameters = { + val pair = (left.toIndexedSeq, right.toIndexedSeq) + copy(selectors = selectors + (i -> pair)) + } + + def withFunction[N <: Arity]( + label: SchematicTermLabel[N], + f: FillArgs[SchematicTermLabel[0], N] => Term + )(using ValueOf[N]): RuleParameters = + copy(functions = functions :+ AssignedFunction(label, LambdaFunction[N](f))) + def withFunction(label: SchematicTermLabel[0], value: Term): RuleParameters = + withFunction(label, _ => value) + + def withPredicate[N <: Arity]( + label: SchematicPredicateLabel[N], + f: FillArgs[SchematicTermLabel[0], N] => Formula + )(using ValueOf[N]): RuleParameters = copy(predicates = predicates :+ AssignedPredicate(label, LambdaPredicate(f))) + def withPredicate(label: SchematicPredicateLabel[0], value: Formula): RuleParameters = + withPredicate(label, _ => value) + + def withConnector[N <: Arity]( + label: SchematicConnectorLabel[N], + f: FillArgs[SchematicPredicateLabel[0], N] => Formula + )(using ValueOf[N]): RuleParameters = { + require(label.arity > 0, "For consistency, use nullary predicate schemas instead of connectors") + copy(connectors = connectors :+ AssignedConnector(label, LambdaConnector(f))) + } + + def withVariable(label: VariableLabel, value: VariableLabel): RuleParameters = + copy(variables = variables + (label -> value)) + } + object RuleParameters { + def apply(args: (AssignedFunction | AssignedPredicate | AssignedConnector | (VariableLabel, VariableLabel))*): RuleParameters = + args.foldLeft(new RuleParameters())((acc, e) => + e match { + case assigned: AssignedFunction => acc.copy(functions = acc.functions :+ assigned) + case assigned: AssignedPredicate => acc.copy(predicates = acc.predicates :+ assigned) + case assigned: AssignedConnector => acc.copy(connectors = acc.connectors :+ assigned) + case pair @ (_: VariableLabel, _: VariableLabel) => acc.copy(variables = acc.variables + pair) + } + ) + } + + protected def applyRuleInference( + parameters: RuleParameters, + patternsFrom: IndexedSeq[PartialSequent], + patternsTo: IndexedSeq[PartialSequent], + valuesFrom: IndexedSeq[Sequent] + ): Option[(IndexedSeq[Sequent], UnificationContext)] = { + def parametersView: View[IndexedSeq[SequentSelector]] = + if (patternsFrom.size == valuesFrom.size) { + matchIndices(parameters.selectors, patternsFrom, valuesFrom) + } else { + View.empty + } + + parametersView.flatMap { selectors => + val ctx = UnificationContext( + parameters.predicates.map(r => r.schema -> r.lambda).toMap, + parameters.functions.map(r => r.schema -> r.lambda).toMap, + parameters.connectors.map(r => r.schema -> r.lambda).toMap + ) + unifyAndResolve(patternsFrom, valuesFrom, patternsTo, ctx, selectors) + }.headOption + } + + /** + * An instantiated rule. Note that the parameters can be incorrect, in that case the tactic will always fail. + * @param rule the original rule + * @param parameters the parameters used for the instantiation + */ + case class RuleTactic private[RuleDefinitions] (rule: Rule, parameters: RuleParameters) extends TacticGoalFunctional { + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] = { + applyRuleInference(parameters, IndexedSeq(rule.conclusion), rule.hypotheses, IndexedSeq(proofGoal)).flatMap { case (newGoals, ctx) => + val stepsOption = rule.reconstruct.andThen(Some.apply).applyOrElse((proofGoal, ctx), _ => None) + stepsOption.map(steps => (newGoals, () => steps)) + } + } + + override def toString: String = s"${rule.getClass.getSimpleName}(...)" + } + + /** + * An rule is an object specifying a type of transformation on a justified statement or a proof goal. + * It is characterized by a sequence of premises (also known as hypotheses) and a conclusion; all patterns. + * It must also define a reconstruction function, in order to translate it to kernel proof steps. + */ + sealed abstract class Rule { + def hypotheses: IndexedSeq[PartialSequent] + def conclusion: PartialSequent + + def reconstruct: ReconstructRule + + require(isLegalPatterns(hypotheses) && isLegalPatterns(IndexedSeq(conclusion))) + + final def apply(parameters: RuleParameters = RuleParameters()): RuleTactic = + RuleTactic(this, parameters) + + final def apply(justification0: Justified, rest: Justified*): Option[Theorem] = + apply(RuleParameters())((justification0 +: rest)*)(using justification0.environment) + /*final def apply(parameters: RuleParameters)(justification0: Justified, rest: Justified*): Option[Theorem] = { + val env = justification0.environment + val justifications = justification0 +: rest + apply(parameters)(justifications: _*)(using env) + }*/ + final def apply(parameters: RuleParameters)(using env: ProofEnvironment): Option[Theorem] = + apply(parameters)() + + final def apply(parameters: RuleParameters)(justifications: Justified*)(using env: ProofEnvironment): Option[Theorem] = { + val justificationsSeq = justifications.toIndexedSeq + val topSequents = justificationsSeq.map(_.sequent) + applyRuleInference(parameters, hypotheses, IndexedSeq(conclusion), topSequents).flatMap { + case (IndexedSeq(newSequent), ctx) => + reconstruct.andThen(Some.apply).applyOrElse((newSequent, ctx), _ => None).map { scSteps => + val scProof = lisa.kernel.proof.SCProof(scSteps, justificationsSeq.map(_.sequentAsKernel)) + env.mkTheorem(newSequent, scProof, justificationsSeq) + } + case _ => throw new Error + } + } + + override def toString: String = { + val top = hypotheses.map(_.toString).mkString(" " * 6) + val bottom = conclusion.toString + val length = Math.max(top.length, bottom.length) + + def pad(s: String): String = " " * ((length - s.length) / 2) + s + + Seq(pad(top), "=" * length, pad(bottom)).mkString("\n") + } + } + + /** + * A constructor for [[Rule]]. + */ + open class RuleBase(override val hypotheses: IndexedSeq[PartialSequent], override val conclusion: PartialSequent, override val reconstruct: ReconstructRule) extends Rule + + given Conversion[Rule, RuleTactic] = _() + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/unification/UnificationDefinitions.scala b/lisa-front/src/main/scala/lisa/front/proof/unification/UnificationDefinitions.scala new file mode 100644 index 00000000..ffdf7d69 --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/unification/UnificationDefinitions.scala @@ -0,0 +1,51 @@ +package lisa.front.proof.unification + +import lisa.front.fol.FOL.* + +trait UnificationDefinitions { + + /** + * An assignment for a unification problem instance. + * @param predicates the assigned predicates + * @param functions the assigned functions + * @param connectors the assigned connectors + * @param variables the assigned variables + */ + case class UnificationContext( + predicates: Map[SchematicPredicateLabel[?], LambdaPredicate[?]] = Map.empty, + functions: Map[SchematicTermLabel[?], LambdaFunction[?]] = Map.empty, + connectors: Map[SchematicConnectorLabel[?], LambdaConnector[?]] = Map.empty, + variables: Map[VariableLabel, VariableLabel] = Map.empty + ) { + infix def +(predicate: AssignedPredicate): UnificationContext = copy(predicates = predicates + (predicate.schema -> predicate.lambda)) + infix def +(function: AssignedFunction): UnificationContext = copy(functions = functions + (function.schema -> function.lambda)) + infix def +(connector: AssignedConnector): UnificationContext = copy(connectors = connectors + (connector.schema -> connector.lambda)) + infix def +(pair: (VariableLabel, VariableLabel)): UnificationContext = copy(functions = functions + (pair._1 -> pair._2)) + + def apply[N <: Arity](predicate: SchematicPredicateLabel[N]): LambdaPredicate[N] = predicates(predicate).asInstanceOf[LambdaPredicate[N]] + def apply[N <: Arity](function: SchematicTermLabel[N]): LambdaFunction[N] = functions(function).asInstanceOf[LambdaFunction[N]] + def apply[N <: Arity](connector: SchematicConnectorLabel[N]): LambdaConnector[N] = connectors(connector).asInstanceOf[LambdaConnector[N]] + + def apply(predicate: SchematicPredicateLabel[0]): Formula = predicates(predicate).body + def apply(function: SchematicTermLabel[0]): Term = functions(function).body + + def assignedPredicates: Seq[AssignedPredicate] = predicates.map { case (k, v) => AssignedPredicate.unsafe(k, v) }.toSeq + def assignedFunctions: Seq[AssignedFunction] = functions.map { case (k, v) => AssignedFunction.unsafe(k, v) }.toSeq + def assignedConnectors: Seq[AssignedConnector] = connectors.map { case (k, v) => AssignedConnector.unsafe(k, v) }.toSeq + } + + /** + * A helper object that represents a renaming. + * @param predicates the renamed predicates + * @param functions the renamed functions + * @param connectors the renamed connectors + * @param variables the renamed free variables + */ + case class RenamingContext( + predicates: Seq[RenamedPredicateSchema] = Seq.empty, + functions: Seq[RenamedFunctionSchema] = Seq.empty, + connectors: Seq[RenamedConnectorSchema] = Seq.empty, + variables: Map[VariableLabel, VariableLabel] = Map.empty + ) + +} diff --git a/lisa-front/src/main/scala/lisa/front/proof/unification/UnificationUtils.scala b/lisa-front/src/main/scala/lisa/front/proof/unification/UnificationUtils.scala new file mode 100644 index 00000000..fd14e4aa --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/proof/unification/UnificationUtils.scala @@ -0,0 +1,590 @@ +package lisa.front.proof.unification + +import lisa.front.fol.FOL.* +import lisa.front.proof.sequent.SequentDefinitions + +import scala.collection.View + +trait UnificationUtils extends UnificationDefinitions with SequentDefinitions { + + /** + * Whether a collection of patterns is legal (e.g. no malformed formulas, no clashing variables, ...) + * @param patterns the patterns to check + * @return whether the patterns are legal or not + */ + def isLegalPatterns(patterns: IndexedSeq[PartialSequent]): Boolean = { + lazy val boundVariables = patterns.flatMap(declaredBoundVariablesOfSequent) + + // Applications match arity, no clashing bound variable patterns + lazy val noMalformedFormulas = patterns.forall(isSequentWellFormed) + // Declared variable patterns must have a globally unique name + lazy val noClashingBoundVariablePatterns = boundVariables.distinct.size == boundVariables.size + // Free variables should not reuse a name of a bound variable + lazy val noConflictingBoundFreeVariables = boundVariables.intersect(patterns.flatMap(freeVariablesOfSequent)).isEmpty + + noMalformedFormulas && noClashingBoundVariablePatterns && noConflictingBoundFreeVariables + } + + /** + * Inflates patterns using a context. + * @param patternsTo the patterns to inflate + * @param valuesFrom the values on the other side + * @param ctx the assignment + * @param indices the indices of the formulas that have been matched in the values + * @return the inflated values + */ + private def inflateValues( + patternsTo: IndexedSeq[PartialSequent], + valuesFrom: IndexedSeq[Sequent], + ctx: UnificationContext, + indices: IndexedSeq[(IndexedSeq[Int], IndexedSeq[Int])] + ): IndexedSeq[Sequent] = { + def removeIndices[T](array: IndexedSeq[T], indices: Seq[Int]): IndexedSeq[T] = { + val set = indices.toSet + for { + (v, i) <- array.zipWithIndex + if !set.contains(i) + } yield v + } + + def instantiate(formulas: IndexedSeq[Formula]): IndexedSeq[Formula] = + formulas.map(formula => + instantiateFormulaSchemas(unsafeRenameVariables(formula, ctx.variables), functions = ctx.assignedFunctions, predicates = ctx.assignedPredicates, connectors = ctx.assignedConnectors) + ) + + def createValueTo(common: IndexedSeq[Formula], pattern: IndexedSeq[Formula], partial: Boolean): IndexedSeq[Formula] = { + val instantiated = instantiate(pattern) + if (partial) common ++ instantiated else instantiated + } + + val (commonLeft, commonRight) = { + indices + .zip(valuesFrom) + .map { case ((indicesLeft, indicesRight), Sequent(valueLeft, valueRight)) => // Union all + (removeIndices(valueLeft, indicesLeft), removeIndices(valueRight, indicesRight)) + } + .foldLeft((IndexedSeq.empty[Formula], IndexedSeq.empty[Formula])) { case ((accL, accR), ((ls, rs))) => + (accL ++ ls.diff(accL), accR ++ rs.diff(accR)) + } + } + + val newValues = patternsTo.map(patternTo => + Sequent( + createValueTo(commonLeft, patternTo.left, patternTo.partialLeft), + createValueTo(commonRight, patternTo.right, patternTo.partialRight) + ) + ) + + newValues + } + + private type Constraints = Seq[Constraint] + private type ConstraintsResult = Option[Constraints] + + private type Context = Set[(VariableLabel, VariableLabel)] + + /** + * A constraint represents an equation between a label and a pattern. + * The constraint can be resolved as soon as the pattern can be fully instantiated to a value. + */ + private enum Constraint { + case SchematicFunction(label: SchematicTermLabel[?], args: Seq[Term], value: Term, ctx: Context) + case SchematicPredicate(label: SchematicPredicateLabel[?], args: Seq[Term], value: Formula, ctx: Context) + case SchematicConnector(label: SchematicConnectorLabel[?], args: Seq[Formula], value: Formula, ctx: Context) + case Variable(pattern: VariableLabel, value: VariableLabel) + } + import Constraint.* + + private val empty: ConstraintsResult = Some(Seq.empty) + private def merge(o1: ConstraintsResult, o2: ConstraintsResult): ConstraintsResult = + for { + c1 <- o1 + c2 <- o2 + } yield c1 ++ c2 + private def collectRecursiveTerm( + pattern: Term, + value: Term, + valuesFunctions: Set[SchematicTermLabel[?]], + valuesVariables: Set[VariableLabel] + )(using ctx: Context): ConstraintsResult = (pattern, value) match { + case (Term(labelPattern: TermLabel[?], argsPattern), Term(labelValue: TermLabel[?], argsValue)) if labelPattern == labelValue => + argsPattern.zip(argsValue).map { case (argPattern, argValue) => collectRecursiveTerm(argPattern, argValue, valuesFunctions, valuesVariables) }.fold(empty)(merge) + case (Term(labelPattern: SchematicTermLabel[?], argsPattern), _) if !valuesFunctions.contains(labelPattern) => + Some(Seq(SchematicFunction(labelPattern, argsPattern, value, ctx))) + case (VariableTerm(labelPattern), VariableTerm(labelValue)) => + if (valuesVariables.contains(labelPattern)) { + if (labelPattern == labelValue) { + Some(Seq.empty) + } else { + None + } + } else if (ctx.contains((labelPattern, labelValue))) { // Bound variable + Some(Seq(Variable(labelPattern, labelValue))) + } else if (ctx.forall { case (p, v) => p != labelPattern && v != labelValue }) { // Free variable + Some(Seq(Variable(labelPattern, labelValue))) // TODO merge these branches + } else { + None + } + case _ => None + } + private def collectRecursiveFormula( + pattern: Formula, + value: Formula, + valuesFunctions: Set[SchematicTermLabel[?]], + valuesPredicates: Set[SchematicPredicateLabel[?]], + valuesConnectors: Set[SchematicConnectorLabel[?]], + valuesVariables: Set[VariableLabel] + )(using ctx: Set[(VariableLabel, VariableLabel)]): ConstraintsResult = (pattern, value) match { + case (PredicateFormula(labelPattern: PredicateLabel[?], argsPattern), PredicateFormula(labelValue: PredicateLabel[?], argsValue)) if labelPattern == labelValue => + argsPattern.zip(argsValue).map { case (argPattern, argValue) => collectRecursiveTerm(argPattern, argValue, valuesFunctions, valuesVariables) }.fold(empty)(merge) + case (PredicateFormula(labelPattern: SchematicPredicateLabel[?], argsPattern), _) if !valuesPredicates.contains(labelPattern) => + Some(Seq(SchematicPredicate(labelPattern, argsPattern, value, ctx))) + case (ConnectorFormula(labelPattern: ConnectorLabel[?], argsPattern), ConnectorFormula(labelValue: ConnectorLabel[?], argsValue)) if labelPattern == labelValue => + argsPattern + .zip(argsValue) + .map { case (argPattern, argValue) => collectRecursiveFormula(argPattern, argValue, valuesFunctions, valuesPredicates, valuesConnectors, valuesVariables) } + .fold(empty)(merge) + case (ConnectorFormula(labelPattern: SchematicConnectorLabel[?], argsPattern), _) if !valuesConnectors.contains(labelPattern) => + Some(Seq(SchematicConnector(labelPattern, argsPattern, value, ctx))) + case (BinderFormula(labelPattern, boundPattern, innerPattern), BinderFormula(labelValue, boundValue, innerValue)) if labelPattern == labelValue => + collectRecursiveFormula(innerPattern, innerValue, valuesFunctions, valuesPredicates, valuesConnectors, valuesVariables)(using ctx + ((boundPattern, boundValue))) + .map(Variable(boundPattern, boundValue) +: _) + case _ => None + } + + private def collect( + patternsAndValues: IndexedSeq[(Formula, Formula)], + valuesFunctions: Set[SchematicTermLabel[?]], + valuesPredicates: Set[SchematicPredicateLabel[?]], + valuesConnectors: Set[SchematicConnectorLabel[?]], + valuesVariables: Set[VariableLabel] + ): ConstraintsResult = + patternsAndValues.map { case (pattern, value) => collectRecursiveFormula(pattern, value, valuesFunctions, valuesPredicates, valuesConnectors, valuesVariables)(using Set.empty) }.fold(empty)(merge) + + private def unifyFromConstraints( + constraints: Constraints, + partialAssignment: UnificationContext, + valueFunctions: Set[SchematicTermLabel[?]], + valuePredicates: Set[SchematicPredicateLabel[?]], + valueConnectors: Set[SchematicConnectorLabel[?]], + valueVariables: Set[VariableLabel] + ): Option[UnificationContext] = { + if (constraints.nonEmpty) { + def isSolvableTerm(pattern: Term)(using ctx: Set[VariableLabel]): Boolean = pattern match { + case VariableTerm(label) => valueVariables.contains(label) || partialAssignment.variables.contains(label) + case Term(_: ConstantFunctionLabel[?], args) => args.forall(isSolvableTerm) + case Term(schematic: SchematicTermLabel[?], args) => (valueFunctions.contains(schematic) || partialAssignment.functions.contains(schematic)) && args.forall(isSolvableTerm) + case _ => false + } + def isSolvableFormula(pattern: Formula)(using ctx: Set[VariableLabel]): Boolean = pattern match { + case PredicateFormula(_: ConstantPredicateLabel[?], args) => args.forall(isSolvableTerm) + case PredicateFormula(schematic: SchematicPredicateLabel[?], args) => (valuePredicates.contains(schematic) || partialAssignment.predicates.contains(schematic)) && args.forall(isSolvableTerm) + case ConnectorFormula(_: ConstantConnectorLabel[?], args) => args.forall(isSolvableFormula) + case ConnectorFormula(schematic: SchematicConnectorLabel[?], args) => + (valueConnectors.contains(schematic) || partialAssignment.connectors.contains(schematic)) && args.forall(isSolvableFormula) + case BinderFormula(_, bound, inner) => valueVariables.contains(bound) && isSolvableFormula(inner)(using ctx + bound) + case _ => false + } + + // This function tries to factor out all occurrences of `args._2` into `args._1` within `term`, and will store the result in `assignment` + def greedyFactoringFunction(term: Term, args: IndexedSeq[(SchematicTermLabel[0], Term)], assignment: Map[SchematicTermLabel[0], Term]): (Term, Map[SchematicTermLabel[0], Term]) = { + args.find { case (_, t) => isSame(term, instantiateTermPartial(t)) } match { + case Some((variable, value)) => (variable, if (!assignment.contains(variable)) assignment + (variable -> value) else assignment) + case None => + term match { + case Term(label, fArgs) => + val (finalArgs, finalAssignment) = fArgs.foldLeft((Seq.empty[Term], assignment)) { case ((argsAcc, currentAssignment), arg) => + val (newTerm, newAssignment) = greedyFactoringFunction(arg, args, currentAssignment) + (argsAcc :+ newTerm, newAssignment) + } + (Term.unsafe(label, finalArgs), finalAssignment) + } + } + } + + def greedyFactoringPredicate(formula: Formula, args: IndexedSeq[(SchematicTermLabel[0], Term)], assignment: Map[SchematicTermLabel[0], Term]): (Formula, Map[SchematicTermLabel[0], Term]) = { + formula match { + case PredicateFormula(label, fArgs) => + val (finalAssignment, finalFArgs) = fArgs.foldLeft((assignment, Seq.empty[Term])) { case ((currentAssignment, currentFArgs), a) => + val (newA, newAssignment) = greedyFactoringFunction(a, args, currentAssignment) + (newAssignment, currentFArgs :+ newA) + } + (PredicateFormula.unsafe(label, finalFArgs), finalAssignment) + case ConnectorFormula(label, fArgs) => + val (finalArgs, finalAssignment) = fArgs.foldLeft((Seq.empty[Formula], assignment)) { case ((argsAcc, currentAssignment), arg) => + val (newFormula, newAssignment) = greedyFactoringPredicate(arg, args, currentAssignment) + (argsAcc :+ newFormula, newAssignment) + } + (ConnectorFormula.unsafe(label, finalArgs), finalAssignment) + case BinderFormula(label, bound, inner) => + val (factoredInner, finalAssignment) = greedyFactoringPredicate(inner, args, assignment) + (BinderFormula(label, bound, factoredInner), finalAssignment) + } + } + + def greedyFactoringConnector( + formula: Formula, + args: IndexedSeq[(SchematicPredicateLabel[0], Formula)], + assignment: Map[SchematicPredicateLabel[0], Formula] + ): (Formula, Map[SchematicPredicateLabel[0], Formula]) = { + args.find { case (_, f) => isSame(formula, instantiateFormulaPartial(f)) } match { + case Some((variable, value)) => (variable, if (!assignment.contains(variable)) assignment + (variable -> value) else assignment) + case None => + formula match { + case _: PredicateFormula => (formula, assignment) // Identity + case ConnectorFormula(label, fArgs) => + val (finalArgs, finalAssignment) = fArgs.foldLeft((Seq.empty[Formula], assignment)) { case ((argsAcc, currentAssignment), arg) => + val (newFormula, newAssignment) = greedyFactoringConnector(arg, args, currentAssignment) + (argsAcc :+ newFormula, newAssignment) + } + (ConnectorFormula.unsafe(label, finalArgs), finalAssignment) + case BinderFormula(label, bound, inner) => + val (factoredInner, finalAssignment) = greedyFactoringConnector(inner, args, assignment) + (BinderFormula(label, bound, factoredInner), finalAssignment) + } + } + } + + def instantiateTermPartial(term: Term): Term = + instantiateTermSchemas(term, partialAssignment.assignedFunctions) + def instantiateFormulaPartial(formula: Formula): Formula = + instantiateFormulaSchemas(formula, partialAssignment.assignedFunctions, partialAssignment.assignedPredicates, partialAssignment.assignedConnectors) + + def isFormulaBodyNoBoundVariables(body: Formula, ctx: Context): Boolean = + ctx.map(_._2).intersect(freeVariablesOf(body)).isEmpty + def isTermBodyNoBoundVariables(body: Term, ctx: Context): Boolean = + ctx.map(_._2).intersect(freeVariablesOf(body)).isEmpty + + // The method tries to resolve a constraint and returns two nested options: + // * None => the constraint is unsolvable (e.g. too many degrees of freedom) + // * Some(None) => there is a contradiction + def handler(constraint: Constraint): Option[Option[(Constraints, UnificationContext)]] = constraint match { + case SchematicFunction(label, args, value, ctx) if partialAssignment.functions.contains(label) => + val lambda = partialAssignment.functions(label) + if (!isTermBodyNoBoundVariables(lambda.body, ctx)) { + // All the bound variables must appear in a way or another as arguments of this lambda + Some(None) + } else if (isSame(value, lambda.unsafe(args.map(instantiateTermPartial)))) { + Some(Some((IndexedSeq.empty, partialAssignment))) + } else { + collectRecursiveTerm(lambda.unsafe(args), value, valueFunctions, valueVariables)(using ctx) match { + case Some(addedConstraints) => Some(Some(addedConstraints, partialAssignment)) + case None => Some(None) + } + } + case SchematicFunction(label, args, value, ctx) if args.forall(isSolvableTerm(_)(using ctx.map(_._1))) => + // TODO are all bound variables already instantiated? + val valueArgs = args.map(unsafeRenameVariables(_, ctx.toMap)) + val freshArguments = freshIds(schematicTermLabelsOf(value).map(_.id), valueArgs.size).map(SchematicTermLabel.apply[0]) + // We drop the resulting arguments map (because it is not needed anymore) + val (fBody, _) = greedyFactoringFunction(value, freshArguments.zip(valueArgs).toIndexedSeq, Map.empty) + if (isTermBodyNoBoundVariables(fBody, ctx)) { + Some(Some((IndexedSeq.empty, partialAssignment + AssignedFunction.unsafe(label, LambdaFunction.unsafe(freshArguments, fBody))))) + } else { + Some(None) + } + case SchematicPredicate(label, args, value, ctx) if partialAssignment.predicates.contains(label) => + val lambda = partialAssignment.predicates(label) + if (!isFormulaBodyNoBoundVariables(lambda.body, ctx)) { + Some(None) + } else if (isSame(value, lambda.unsafe(args.map(instantiateTermPartial)))) { + Some(Some((IndexedSeq.empty, partialAssignment))) + } else { + collectRecursiveFormula(lambda.unsafe(args), value, valueFunctions, valuePredicates, valueConnectors, valueVariables)(using ctx) match { + case Some(addedConstraints) => Some(Some(addedConstraints, partialAssignment)) + case None => Some(None) + } + } + case SchematicPredicate(label, args, value, ctx) if args.forall(isSolvableTerm(_)(using ctx.map(_._1))) => + // Analogous to the above + val valueArgs = args.map(unsafeRenameVariables(_, ctx.toMap)) + val freshArguments = freshIds(schematicTermLabelsOf(value).map(_.id), valueArgs.size).map(SchematicTermLabel.apply[0]) + val (fBody, _) = greedyFactoringPredicate(value, freshArguments.zip(valueArgs).toIndexedSeq, Map.empty) + if (isFormulaBodyNoBoundVariables(fBody, ctx)) { + Some(Some((IndexedSeq.empty, partialAssignment + AssignedPredicate.unsafe(label, LambdaPredicate.unsafe(freshArguments, fBody))))) + } else { + Some(None) + } + case SchematicConnector(label, args, value, ctx) if partialAssignment.connectors.contains(label) => + val lambda = partialAssignment.connectors(label) + if (!isFormulaBodyNoBoundVariables(lambda.body, ctx)) { + Some(None) + } else if (isSame(value, lambda.unsafe(args.map(instantiateFormulaPartial)))) { + Some(Some((IndexedSeq.empty, partialAssignment))) + } else { + collectRecursiveFormula(lambda.unsafe(args), value, valueFunctions, valuePredicates, valueConnectors, valueVariables)(using ctx) match { + case Some(addedConstraints) => Some(Some(addedConstraints, partialAssignment)) + case None => Some(None) + } + } + case SchematicConnector(label, args, value, ctx) if args.forall(isSolvableFormula(_)(using ctx.map(_._1))) => + val valueArgs = args.map(unsafeRenameVariables(_, ctx.toMap)) + val freshArguments = freshIds(schematicPredicatesOf(value).map(_.id), valueArgs.size).map(SchematicPredicateLabel.apply[0]) + val (fBody, _) = greedyFactoringConnector(value, freshArguments.zip(valueArgs).toIndexedSeq, Map.empty) + if (isFormulaBodyNoBoundVariables(fBody, ctx)) { + Some(Some((IndexedSeq.empty, partialAssignment + AssignedConnector.unsafe(label, LambdaConnector.unsafe(freshArguments, fBody))))) + } else { + Some(None) + } + case Variable(pattern, value) => + if (valueVariables.contains(pattern)) { + if (pattern == value) { + Some(Some((IndexedSeq.empty, partialAssignment))) + } else { + Some(None) + } + } else if (partialAssignment.variables.forall { case (l, r) => l != pattern || r == value }) { + Some(Some((IndexedSeq.empty, partialAssignment + (pattern -> value)))) + } else { + Some(None) // Contradiction + } + case _ => None + } + constraints.view.zipWithIndex.flatMap { case (constraint, i) => + handler(constraint).map(_.map { case (newConstraints, newContext) => (newConstraints, newContext, i) }) + }.headOption match { + case Some(option) => + option match { + case Some((newConstraints, newContext, i)) => + unifyFromConstraints(constraints.take(i) ++ newConstraints ++ constraints.drop(i + 1), newContext, valueFunctions, valuePredicates, valueConnectors, valueVariables) + case None => None // Explicit error + } + case None => None // No available reduction + } + } else { + Some(partialAssignment) + } + } + + /** + * Solves a matching (one-sided unification) problem. + * The result if any is an assignment for the patterns such that they become equivalent to the values. + * An optional partial assignment can be provided to help or constraint the matching. + * Additionally, it is possible to provide other patterns. In that case, the resulting sequents + * will also include all the unmatched formulas. + * @param patterns the patterns + * @param values the value + * @param otherPatterns other patterns + * @param partialAssignment the partial assignment (or empty if none) + * @param formulaIndices the correspondence between patterns and values in the sequents + * @return an option containing the inflated other patterns and the assignment + */ + def unifyAndResolve( + patterns: IndexedSeq[PartialSequent], + values: IndexedSeq[Sequent], + otherPatterns: IndexedSeq[PartialSequent], + partialAssignment: UnificationContext, + formulaIndices: IndexedSeq[(IndexedSeq[Int], IndexedSeq[Int])] + ): Option[(IndexedSeq[Sequent], UnificationContext)] = { + + def schemasOf(sequents: IndexedSeq[SequentBase]): (Set[SchematicTermLabel[?]], Set[SchematicPredicateLabel[?]], Set[SchematicConnectorLabel[?]], Set[VariableLabel], Set[VariableLabel]) = + ( + sequents.flatMap(schematicFunctionsOfSequent).toSet, + sequents.flatMap(schematicPredicatesOfSequent).toSet, + sequents.flatMap(schematicConnectorsOfSequent).toSet, + sequents.flatMap(freeVariablesOfSequent).toSet, + sequents.flatMap(declaredBoundVariablesOfSequent).toSet + ) + + val (patternsFunctions, patternsPredicates, patternsConnectors, patternsFreeVariables, patternsBoundVariables) = + schemasOf(patterns) + val (valuesFunctions, valuesPredicates, valuesConnectors, valuesFreeVariables, valuesBoundVariables) = + schemasOf(values) + val (otherPatternsFunctions, otherPatternsPredicates, otherPatternsConnectors, otherPatternsFreeVariables, otherPatternsBoundVariables) = + schemasOf(otherPatterns) + val (partialAssignedFunctions, partialAssignedPredicates, partialAssignedConnectors) = + (partialAssignment.functions.keySet, partialAssignment.predicates.keySet, partialAssignment.connectors.keySet) + val (allPatternsFunctions, allPatternsPredicates, allPatternsConnectors, allPatternsFreeVariables, allPatternsBoundVariables) = + ( + patternsFunctions ++ otherPatternsFunctions, + patternsPredicates ++ otherPatternsPredicates, + patternsConnectors ++ otherPatternsConnectors, + patternsFreeVariables ++ otherPatternsFreeVariables, + patternsBoundVariables ++ otherPatternsBoundVariables + ) + val valuesVariables = valuesFreeVariables ++ valuesBoundVariables + val allPatternsVariables = allPatternsFreeVariables ++ allPatternsBoundVariables + + // TODO: do we need to exclude the arguments from these sets? + val allValuesFunctions = valuesFunctions ++ partialAssignment.functions.values.flatMap { f => schematicTermLabelsOf(f.body) } ++ + (partialAssignment.predicates.values ++ partialAssignment.connectors.values).flatMap { f => schematicTermLabelsOf(f.body) } + val allValuesPredicates = valuesPredicates ++ + (partialAssignment.predicates.values ++ partialAssignment.connectors.values).flatMap { f => schematicPredicatesOf(f.body) } + val allValuesConnectors = valuesConnectors ++ + (partialAssignment.predicates.values ++ partialAssignment.connectors.values).flatMap { f => schematicConnectorsOf(f.body) } + val allValuesVariables = valuesVariables ++ partialAssignment.functions.values.flatMap { f => freeVariablesOf(f.body) } ++ + (partialAssignment.predicates.values ++ partialAssignment.connectors.values).flatMap { f => freeVariablesOf(f.body) ++ declaredBoundVariablesOf(f.body) } + + val (nonUnifiableFunctions, nonUnifiablePredicates, nonUnifiableConnectors) = + (otherPatternsFunctions.diff(patternsFunctions), otherPatternsPredicates.diff(patternsPredicates), otherPatternsConnectors.diff(patternsConnectors)) + + lazy val noInvalidSizeRange = patterns.size == values.size && patterns.size == formulaIndices.size && patterns.zip(formulaIndices).zip(values).forall { + case ((PartialSequent(leftPattern, rightPattern, _, _), (leftIndices, rightIndices)), Sequent(leftValue, rightValue)) => + def check(pattern: IndexedSeq[Formula], indices: IndexedSeq[Int], value: IndexedSeq[Formula]): Boolean = + pattern.size == indices.size && indices.forall(value.indices.contains) + check(leftPattern, leftIndices, leftValue) && check(rightPattern, rightIndices, rightValue) + } + lazy val noMalformedValues = values.forall(isSequentWellFormed) + lazy val noSchematicConnectorsValues = values.flatMap(schematicConnectorsOfSequent).isEmpty + lazy val noMalformedAssignment = // TODO some of these should be a contract in `UnificationContext` + partialAssignment.functions.values.forall(lambda => isWellFormed(lambda.body)) && + partialAssignment.predicates.values.forall(lambda => isWellFormed(lambda.body) && schematicConnectorsOf(lambda.body).isEmpty) && + partialAssignment.connectors.values.forall(lambda => isWellFormed(lambda.body) && schematicConnectorsOf(lambda.body).isEmpty) + lazy val noDeclaredUnknown = + partialAssignedFunctions.subsetOf(allPatternsFunctions) && + partialAssignedPredicates.subsetOf(allPatternsPredicates) && + partialAssignedConnectors.subsetOf(allPatternsConnectors) + lazy val noUndeclaredNonUnifiable = + nonUnifiableFunctions.subsetOf(partialAssignedFunctions) && + nonUnifiablePredicates.subsetOf(partialAssignedPredicates) && + nonUnifiableConnectors.subsetOf(partialAssignedConnectors) + + val allRequirements = + isLegalPatterns(patterns) && isLegalPatterns(otherPatterns) && + noInvalidSizeRange && noMalformedValues && noSchematicConnectorsValues && noMalformedAssignment && + noDeclaredUnknown && noUndeclaredNonUnifiable + + if (allRequirements) { + // All requirements are satisfied, we can proceed + // We must rename the symbols in the pattern such that they are distinct from the ones in the values + + // All the names that are already taken (for simplicity we rename everything) + val initialTakenFunctions: Set[SchematicTermLabel[?]] = + patternsFunctions ++ otherPatternsFunctions ++ allValuesFunctions + val initialTakenPredicates: Set[SchematicPredicateLabel[?]] = + patternsPredicates ++ otherPatternsPredicates ++ allValuesPredicates + val initialTakenConnectors: Set[SchematicConnectorLabel[?]] = + patternsConnectors ++ otherPatternsConnectors ++ allValuesConnectors + val initialTakenVariables: Set[VariableLabel] = // Free and bound + patternsFreeVariables ++ patternsBoundVariables ++ otherPatternsFreeVariables ++ otherPatternsBoundVariables ++ allValuesVariables + + def freshMapping[T <: LabelType](taken: Set[T], toRename: Set[T], constructor: (T, String) => T): Map[T, T] = { + val (finalMap, _) = toRename.foldLeft((Map.empty[T, T], taken.map(_.id))) { case ((map, currentTaken), oldSymbol) => + val newName = freshId(currentTaken, oldSymbol.id) + val newSymbol = constructor(oldSymbol, newName) + (map + (oldSymbol -> newSymbol), currentTaken + newName) + } + finalMap + } + + // TODO rename variables args + + val functionsFreshMapping = freshMapping(initialTakenFunctions, allPatternsFunctions, (label, newName) => SchematicTermLabel.unsafe(newName, label.arity)) + val predicatesFreshMapping = freshMapping(initialTakenPredicates, allPatternsPredicates, (label, newName) => SchematicPredicateLabel.unsafe(newName, label.arity)) + val connectorsFreshMapping = freshMapping(initialTakenConnectors, allPatternsConnectors, (label, newName) => SchematicConnectorLabel.unsafe(newName, label.arity)) + val variablesFreshMapping = freshMapping(initialTakenVariables, allPatternsFreeVariables ++ allPatternsBoundVariables, (_, newName) => VariableLabel(newName)) + + val (functionsInverseMapping, predicatesInverseMapping, connectorsInverseMapping, variablesInverseMapping) = + (functionsFreshMapping.map(_.swap), predicatesFreshMapping.map(_.swap), connectorsFreshMapping.map(_.swap), variablesFreshMapping.map(_.swap)) + + val renamedPartialAssignment = UnificationContext( + partialAssignment.predicates.map { case (k, v) => predicatesFreshMapping.getOrElse(k, k) -> v }, + partialAssignment.functions.map { case (k, v) => functionsFreshMapping.getOrElse(k, k) -> v }, + partialAssignment.connectors.map { case (k, v) => connectorsFreshMapping.getOrElse(k, k) -> v }, + partialAssignment.variables.map { case (k, v) => variablesFreshMapping.getOrElse(k, k) -> v } + ) + + def rename(patterns: IndexedSeq[PartialSequent]): IndexedSeq[PartialSequent] = { + def renameFormula(formula: Formula): Formula = + instantiateFormulaSchemas( + unsafeRenameVariables(formula, variablesFreshMapping), + functions = functionsFreshMapping.map { case (k, v) => RenamedLabel.unsafe(k, v).toAssignment }.toSeq, + predicates = predicatesFreshMapping.map { case (k, v) => RenamedLabel.unsafe(k, v).toAssignment }.toSeq, + connectors = connectorsFreshMapping.map { case (k, v) => RenamedLabel.unsafe(k, v).toAssignment }.toSeq + ) + def renameFormulas(formulas: IndexedSeq[Formula]): IndexedSeq[Formula] = formulas.map(renameFormula) + patterns.map(p => p.copy(left = renameFormulas(p.left), right = renameFormulas(p.right))) + } + + val (renamedPatterns, renamedOtherPatterns) = (rename(patterns), rename(otherPatterns)) + + val orderedValues = values.zip(formulaIndices).flatMap { case (value, (indicesLeft, indicesRight)) => + indicesLeft.map(value.left) ++ indicesRight.map(value.right) + } + + val constraints = collect(renamedPatterns.flatMap(_.formulas).zip(orderedValues), valuesFunctions, valuesPredicates, valuesConnectors, valuesVariables) + + val unified = constraints + .flatMap(unifyFromConstraints(_, renamedPartialAssignment, allValuesFunctions, allValuesPredicates, allValuesConnectors, allValuesVariables)) + .filter(assignment => // Check if the assignment is full (should this be an assertion?) + assignment.functions.keySet.map(functionsInverseMapping) == allPatternsFunctions && + assignment.predicates.keySet.map(predicatesInverseMapping) == allPatternsPredicates && + assignment.connectors.keySet.map(connectorsInverseMapping) == allPatternsConnectors && + assignment.variables.keySet.map(variablesInverseMapping) == allPatternsVariables + ) + + unified.map { renamedAssignment => + val assignment = UnificationContext( + renamedAssignment.predicates.map { case (k, v) => predicatesInverseMapping.getOrElse(k, k) -> v }, + renamedAssignment.functions.map { case (k, v) => functionsInverseMapping.getOrElse(k, k) -> v }, + renamedAssignment.connectors.map { case (k, v) => connectorsInverseMapping.getOrElse(k, k) -> v }, + renamedAssignment.variables.map { case (k, v) => variablesInverseMapping.getOrElse(k, k) -> v } + ) + + // Union all + val otherValues = inflateValues(renamedOtherPatterns, values, renamedAssignment, formulaIndices) + + (otherValues, assignment) + } + } else { + None + } + } + + type SequentSelector = (IndexedSeq[Int], IndexedSeq[Int]) + + /** + * A helper method designed to enumerate all possible correspondences between patterns and values. + * @param map an optional partial correspondence (or empty if none) + * @param patterns the patterns + * @param values the values + * @return a lazy list of correspondences + */ + def matchIndices(map: Map[Int, SequentSelector], patterns: IndexedSeq[PartialSequent], values: IndexedSeq[Sequent]): View[IndexedSeq[SequentSelector]] = { + require(patterns.size == values.size) + // Normally `pattern` shouldn't be empty, but this function works regardless + if (map.keySet.forall(patterns.indices.contains)) { + val selectors = patterns.indices.map(map.getOrElse(_, (IndexedSeq.empty, IndexedSeq.empty))) + selectors + .zip(patterns.zip(values)) + .map { case ((leftSelector, rightSelector), (pattern, value)) => + def enumerate(selectorSide: IndexedSeq[Int], patternSideSize: Int, isPatternPartial: Boolean, valueSide: Range): View[IndexedSeq[Int]] = { + // TODO remove the partial parameter as it is not needed in this direction + if (selectorSide.isEmpty) { // If empty we consider all permutations + // If `valueSide` is empty then it will produce an empty array + valueSide.combinations(patternSideSize).flatMap(_.permutations).toSeq.view + } else { + if (selectorSide.size == patternSideSize) { + if (selectorSide.forall(valueSide.contains)) { + // We return exactly what was selected + View(selectorSide) + } else { + // An index value is out of range + View.empty + } + } else { + // Number of args does not match the pattern's + View.empty + } + } + } + val leftSide = enumerate(leftSelector, pattern.left.size, pattern.partialLeft, value.left.indices) + val rightSide = enumerate(rightSelector, pattern.right.size, pattern.partialRight, value.right.indices) + for { + l <- leftSide + r <- rightSide + } yield IndexedSeq((l, r)) + } + .fold(View(IndexedSeq.empty[(IndexedSeq[Int], IndexedSeq[Int])])) { case (v1, v2) => + for { + first <- v1 + second <- v2 + } yield first ++ second + } + } else { + // Map contains values outside the range + View.empty + } + } + +} diff --git a/lisa-front/src/main/scala/lisa/front/theory/SetTheory.scala b/lisa-front/src/main/scala/lisa/front/theory/SetTheory.scala new file mode 100644 index 00000000..27b1b31d --- /dev/null +++ b/lisa-front/src/main/scala/lisa/front/theory/SetTheory.scala @@ -0,0 +1,52 @@ +package lisa.front.theory + +import lisa.front.fol.FOL.* +import lisa.kernel.proof.RunningTheory +import lisa.settheory.AxiomaticSetTheory + +/** + * The set theory package. See [[lisa.settheory.AxiomaticSetTheory]]. + */ +object SetTheory { + + // The purpose of this file is simply to lift the definitions from the kernel to the front + + /** + * A safe type representing a formula that is considered as an axiom in this theory. + */ + opaque type AxiomaticFormula <: Formula = Formula + + val membership: ConstantPredicateLabel[2] = fromKernel(AxiomaticSetTheory.in).asInstanceOf[ConstantPredicateLabel[2]] + val subset: ConstantPredicateLabel[2] = fromKernel(AxiomaticSetTheory.subset).asInstanceOf[ConstantPredicateLabel[2]] + val sameCardinality: ConstantPredicateLabel[2] = fromKernel(AxiomaticSetTheory.in).asInstanceOf[ConstantPredicateLabel[2]] + + val emptySet: ConstantFunctionLabel[0] = fromKernel(AxiomaticSetTheory.emptySet).asInstanceOf[ConstantFunctionLabel[0]] + val unorderedPairSet: ConstantFunctionLabel[2] = fromKernel(AxiomaticSetTheory.pair).asInstanceOf[ConstantFunctionLabel[2]] + // val singletonSet: ConstantFunctionLabel[1] = fromKernel(AxiomaticSetTheory.singleton).asInstanceOf[ConstantFunctionLabel[1]] + val powerSet: ConstantFunctionLabel[1] = fromKernel(AxiomaticSetTheory.powerSet).asInstanceOf[ConstantFunctionLabel[1]] + val unionSet: ConstantFunctionLabel[1] = fromKernel(AxiomaticSetTheory.union).asInstanceOf[ConstantFunctionLabel[1]] + val universeSet: ConstantFunctionLabel[1] = fromKernel(AxiomaticSetTheory.universe).asInstanceOf[ConstantFunctionLabel[1]] + + val axiomEmpty: AxiomaticFormula = fromKernel(AxiomaticSetTheory.emptySetAxiom) + val axiomExtensionality: AxiomaticFormula = fromKernel(AxiomaticSetTheory.extensionalityAxiom) + val axiomPair: AxiomaticFormula = fromKernel(AxiomaticSetTheory.pairAxiom) + val axiomUnion: AxiomaticFormula = fromKernel(AxiomaticSetTheory.unionAxiom) + val axiomPower: AxiomaticFormula = fromKernel(AxiomaticSetTheory.powerAxiom) + val axiomFoundation: AxiomaticFormula = fromKernel(AxiomaticSetTheory.foundationAxiom) + + val axiomSchemaReplacement: AxiomaticFormula = fromKernel(AxiomaticSetTheory.replacementSchema) + + val axiomTarski: AxiomaticFormula = fromKernel(AxiomaticSetTheory.tarskiAxiom) + + val definitionSubset: AxiomaticFormula = { + val (x, y, z) = (VariableLabel("x"), VariableLabel("y"), VariableLabel("z")) + forall(x, forall(y, subset(x, y) <=> forall(z, (z in x) ==> (z in y)))) + } + + extension (term: Term) { + infix def in(other: Term): Formula = membership(term, other) + def subsetOf(other: Term): Formula = subset(term, other) + infix def ~(other: Term): Formula = sameCardinality(term, other) + } + +} diff --git a/lisa-front/src/test/scala/lisa/front/FrontMacroTests.scala b/lisa-front/src/test/scala/lisa/front/FrontMacroTests.scala new file mode 100644 index 00000000..0c50fac4 --- /dev/null +++ b/lisa-front/src/test/scala/lisa/front/FrontMacroTests.scala @@ -0,0 +1,32 @@ +package lisa.front + +import lisa.front.{_, given} +import org.scalatest.funsuite.AnyFunSuite + +import scala.language.adhocExtensions + +class FrontMacroTests extends AnyFunSuite { + // TODO Front macros are not working du to changes to variables. + /* + test("string interpolation macros") { + term"g(x, y)" + formula"a /\ b \/ c => d" + sequent"a; b |- c" + partial"a |- b; ..." + + val p = ConstantPredicateLabel[2]("p") + assert(formula"$p(x, y)".toString == "p(x, y)") + + val f = SchematicTermLabel[2]("f") + val y0:Term = SchematicTermLabel[0]("y")() + term"$y0" + assert(term"{$f(x, $y0)}".toString == "{?f(x, ?y)}") + assert(formula"{} = {$f(x, $y0)}".toString == "? = {?f(x, ?y)}") + + val p0 = ConstantPredicateLabel[0]("p") + val v = VariableLabel("x") + assert(sequent" |- $p0".toString == "? p") + assert(partial"\ $v. $v = {}; f($y0) |- $p0 /\ b; ...".toString == raw"\x. f(?y); x = ? ? p ? b; ?") + } + */ +} diff --git a/lisa-front/src/test/scala/lisa/front/ParserPrinterTests.scala b/lisa-front/src/test/scala/lisa/front/ParserPrinterTests.scala new file mode 100644 index 00000000..f915ebc0 --- /dev/null +++ b/lisa-front/src/test/scala/lisa/front/ParserPrinterTests.scala @@ -0,0 +1,68 @@ +package lisa.front + +import lisa.front.parser.FrontReader +import lisa.front.printer.FrontPositionedPrinter +import lisa.front.printer.FrontPrintStyle +import org.scalatest.funsuite.AnyFunSuite + +import scala.language.adhocExtensions + +class ParserPrinterTests extends AnyFunSuite { + test("formula parsing and printing (ASCII)") { + Seq[String]( + "a", + raw"a /\ b", + raw"a /\ b \/ c => d <=> e", + "a => b => c => d", + "((a => b) => c) => d", + "forall x. ?x = ?z", + "f(a, b)", + raw"(a \/ b) /\ c", + raw"forall x, y. (?x = ?z) /\ (?x = ?y)", + raw"??f(g({?x, {{}, ?y}}, {}), a, a /\ b)", + "?s", + "?f(?s, ?g(?x), t) = ?u", + "exists x. forall y. ?x = ?y" + ).foreach { s => + val formula = FrontReader.readFormula(s) + val printed = FrontPositionedPrinter.prettyFormula(formula, symbols = FrontPrintStyle.Ascii) + println(printed) + assert(printed == s) // actual == expected + } + } + + test("sequent parsing and printing (ASCII)") { + Seq[String]( + "|-", + "|- a", + "a |- b", + "a; b |- c; d", + raw"a /\ b; c \/ d |- e; f => g; h" + ).foreach { s => + val sequent = FrontReader.readSequent(s) + val printed = FrontPositionedPrinter.prettySequent(sequent, symbols = FrontPrintStyle.Ascii) + // println(printed) + assert(printed == s) + } + } + + test("partial sequent parsing and printing (ASCII)") { + Seq[String]( + "|-", + "|- a", + "a |- b", + "a; b |- c; d", + "... |- a; b", + "... |- ...", + "a |- b; ...", + "...; a |- b", + "...; a |- b; ...", + "...; a; b |- b; c; ..." + ).foreach { s => + val sequent = FrontReader.readPartialSequent(s) + val printed = FrontPositionedPrinter.prettyPartialSequent(sequent, symbols = FrontPrintStyle.Ascii) + // println(printed) + assert(printed == s) + } + } +} diff --git a/lisa-front/src/test/scala/lisa/front/UnificationTests.scala b/lisa-front/src/test/scala/lisa/front/UnificationTests.scala new file mode 100644 index 00000000..26103982 --- /dev/null +++ b/lisa-front/src/test/scala/lisa/front/UnificationTests.scala @@ -0,0 +1,256 @@ +package lisa.front + +import lisa.front.fol.FOL.LabelType +import lisa.front.fol.FOL.WithArityType +import lisa.front.printer.FrontPositionedPrinter +import lisa.front.printer.FrontPrintStyle +import lisa.front.{_, given} +import org.scalatest.Ignore +import org.scalatest.funsuite.AnyFunSuite + +import scala.language.adhocExtensions + +@Ignore +class UnificationTests extends AnyFunSuite { + + val (sa, sb, sc) = (SchematicPredicateLabel[0]("a"), SchematicPredicateLabel[0]("b"), SchematicPredicateLabel[0]("c")) + val (a, b, c) = (ConstantPredicateLabel[0]("a"), ConstantPredicateLabel[0]("b"), ConstantPredicateLabel[0]("c")) + + val (st, su, sv) = (SchematicTermLabel[0]("t"), SchematicTermLabel[0]("u"), SchematicTermLabel[0]("v")) + val (t, u, v) = (ConstantFunctionLabel[0]("t"), ConstantFunctionLabel[0]("u"), ConstantFunctionLabel[0]("v")) + + val (sf1, f1) = (SchematicTermLabel[1]("f1"), ConstantFunctionLabel[1]("f1")) + val (sg1) = (SchematicConnectorLabel[1]("g1")) + val (sp1, p1) = (SchematicPredicateLabel[1]("p1"), ConstantPredicateLabel[1]("p1")) + + val (x, y, z) = (VariableLabel("x"), VariableLabel("y"), VariableLabel("z")) + + def unify(pattern: Formula, target: Formula, partial: UnificationContext = emptyContext): Option[(IndexedSeq[Sequent], UnificationContext)] = + unifyAndResolve( + IndexedSeq(PartialSequent(IndexedSeq(pattern), IndexedSeq.empty)), + IndexedSeq(Sequent(IndexedSeq(target), IndexedSeq.empty)), + IndexedSeq.empty, + partial, + IndexedSeq((IndexedSeq(0), IndexedSeq.empty)) + ) + + @deprecated + def checkUnifies(pattern: Formula, target: Formula, partial: UnificationContext = emptyContext): Unit = { + assert( + unify(pattern, target, partial).nonEmpty, + s"Pattern ${FrontPositionedPrinter.prettyFormula(pattern, symbols = FrontPrintStyle.Unicode)} and " + + s"target ${FrontPositionedPrinter.prettyFormula(target, symbols = FrontPrintStyle.Unicode)} did not unify" + ) + } + + def checkDoesNotUnify(pattern: Formula, target: Formula, partial: UnificationContext = emptyContext): Unit = { + assert( + unify(pattern, target, partial).isEmpty, + s"Pattern ${FrontPositionedPrinter.prettyFormula(pattern, symbols = FrontPrintStyle.Unicode)} and " + + s"target ${FrontPositionedPrinter.prettyFormula(target, symbols = FrontPrintStyle.Unicode)} did unify" + ) + } + + def contextsEqual(ctx1: UnificationContext, ctx2: UnificationContext): Boolean = { + def names(lambda: WithArityType[?]): Seq[String] = (0 until lambda.arity).map(i => s"unique_name_$i") + def normalizeFunction[N <: Arity](lambda: LambdaFunction[N]) = { + val newParams = names(lambda).map(SchematicTermLabel.apply[0]) + LambdaFunction.unsafe( + newParams, + instantiateTermSchemas(lambda.body, lambda.parameters.zip(newParams).map { case (l1, l2) => RenamedLabel.unsafe(l1, l2).toAssignment }) + ) + } + // val x:Nothing = normalizeFunction + def normalizePredicate(lambda: LambdaPredicate[?]): LambdaPredicate[?] = { + val newParams = names(lambda).map(SchematicTermLabel.apply[0]) + LambdaPredicate.unsafe( + newParams, + instantiateFormulaSchemas(lambda.body, lambda.parameters.zip(newParams).map { case (l1, l2) => RenamedLabel.unsafe(l1, l2).toAssignment }, Seq.empty, Seq.empty) + ) + } + def normalizeConnector(lambda: LambdaConnector[?]): LambdaConnector[?] = { + val newParams = names(lambda).map(SchematicPredicateLabel.apply[0]) + LambdaConnector.unsafe( + newParams, + instantiateFormulaSchemas(lambda.body, Seq.empty, lambda.parameters.zip(newParams).map { case (l1, l2) => RenamedLabel.unsafe(l1, l2).toAssignment }, Seq.empty) + ) + } + def normalizeContext(ctx: UnificationContext): UnificationContext = + UnificationContext( + ctx.predicates.view.mapValues(normalizePredicate).toMap, + ctx.functions.view.mapValues(normalizeFunction).toMap, + ctx.connectors.view.mapValues(normalizeConnector).toMap, + ctx.variables + ) + normalizeContext(ctx1) == normalizeContext(ctx2) + } + + def checkUnifiesAs(pattern: Formula, target: Formula, partial: UnificationContext)(expected: UnificationContext): Unit = { + unify(pattern, target, partial) match { + case Some((_, resultCtx)) => assert(contextsEqual(resultCtx, expected), resultCtx) + case None => + fail( + s"Pattern ${FrontPositionedPrinter.prettyFormula(pattern, symbols = FrontPrintStyle.Unicode)} and " + + s"target ${FrontPositionedPrinter.prettyFormula(target, symbols = FrontPrintStyle.Unicode)} did not unify" + ) + } + } + + def checkUnifiesAs(pattern: Formula, target: Formula)(expected: UnificationContext): Unit = + checkUnifiesAs(pattern, target, emptyContext)(expected) + + val emptyContext: UnificationContext = UnificationContext() + val emptyResult: UnificationContext = UnificationContext() + + test("same boolean constant") { + checkUnifiesAs(a, a)(emptyResult) + } + test("different boolean constants") { + checkDoesNotUnify(a, b) + } + test("boolean and schematic constants") { + checkDoesNotUnify(a, sa) + } + test("boolean constant expression") { + checkUnifiesAs(a /\ b, a /\ b)(emptyResult) + } + + test("schematic boolean constant") { + checkUnifiesAs(sa, a)(emptyResult + AssignedPredicate(sa, a)) + } + test("same schematic boolean constant") { + checkUnifiesAs(sa, sa)(emptyResult + AssignedPredicate(sa, sa)) + } + test("expression with schematic boolean constant") { + checkUnifiesAs(sa /\ b, a /\ b)(emptyResult + AssignedPredicate(sa, a)) + } + test("expression with multiple schematic boolean constants") { + checkUnifiesAs(sa /\ sb, a /\ b)(emptyResult + AssignedPredicate(sa, a) + AssignedPredicate(sb, b)) + } + test("matching two schematic boolean constants") { + checkUnifiesAs(sa /\ b, sb /\ b)(emptyResult + AssignedPredicate(sa, sb)) + } + test("schematic boolean constants match expressions") { + checkUnifiesAs(sa /\ sb, (a ==> b) /\ (c \/ a))(emptyResult + AssignedPredicate(sa, a ==> b) + AssignedPredicate(sb, c \/ a)) + } + test("schematic predicate matches same expressions") { + checkUnifiesAs(sa /\ sa, b /\ b)(emptyResult + AssignedPredicate(sa, b)) + } + test("schematic predicate matches equivalent expressions") { + checkUnifiesAs(sa /\ sa, (a \/ b) /\ (b \/ a))(emptyResult + AssignedPredicate(sa, a \/ b)) + } + test("schematic predicate does not match different constants") { + checkDoesNotUnify(sa /\ sa, a /\ b) + } + test("schematic 0-ary predicate: equivalent expression in the context") { + checkUnifiesAs(sa, a /\ b, emptyContext + AssignedPredicate(sa, b /\ a))( + emptyResult + AssignedPredicate(sa, b /\ a) + ) + } + test("schematic 0-ary predicate with partial mapping") { + checkUnifiesAs(sa, a, emptyContext + AssignedPredicate(sa, a))(emptyResult + AssignedPredicate(sa, a)) + } + test("schematic 0-ary predicate with contradicting partial mapping") { + checkDoesNotUnify(sa, a, emptyContext + AssignedPredicate(sa, b)) + } + test("schematic 0-ary predicate with contradicting partial mapping 2") { + checkDoesNotUnify(sa, a, emptyContext + AssignedPredicate(sb, a)) + } + + test("same predicate") { + checkUnifiesAs(p1(t), p1(t))(emptyResult) + } + test("predicate of schematic variable to predicate of constant") { + checkUnifiesAs(p1(st), p1(u))(emptyResult + AssignedFunction(st, u)) + } + test("schematic to constant predicate") { + checkUnifiesAs(sp1(t), p1(t))(emptyResult + AssignedPredicate(sp1, LambdaPredicate(x => p1(x)))) + } + test("schematic predicate to expression") { + checkUnifiesAs(sp1(t), p1(t) /\ p1(u))(emptyResult + AssignedPredicate(sp1, LambdaPredicate(x => p1(x) /\ p1(u)))) + } + test("schematic connector of boolean constant to boolean constant") { + checkUnifiesAs(sg1(a), b)(emptyResult + AssignedConnector(sg1, LambdaConnector(_ => b))) + } + test("schematic connector of schematic boolean constant does not match boolean constant") { + checkDoesNotUnify(sg1(sa), a) + } + test("schematic connector of schematic boolean constant with partial mapping of connector") { + checkUnifiesAs(sg1(sa), b, emptyContext + AssignedConnector(sg1, LambdaConnector(x => x)))( + emptyResult + + AssignedConnector(sg1, LambdaConnector(x => x)) + AssignedPredicate(sa, b) + ) + } + test("schematic connector of schematic boolean constant with partial mapping of 0-ary predicate") { + checkUnifiesAs(sg1(sa), b, emptyContext + AssignedPredicate(sa, b))( + emptyResult + AssignedPredicate(sa, b) + + AssignedConnector(sg1, LambdaConnector(x => x)) + ) + } + test("schematic connector of schematic boolean constant with partial mapping of connector and 0-ary predicate") { + checkUnifiesAs(sg1(sa), b, emptyContext + AssignedConnector(sg1, LambdaConnector(x => x)) + AssignedPredicate(sa, b))( + emptyContext + AssignedConnector(sg1, LambdaConnector(x => x)) + AssignedPredicate(sa, b) + ) + } + test("schematic connector of schematic boolean constant with partial mapping of connector and different 0-ary predicate") { + checkDoesNotUnify(sg1(sa), b, emptyContext + AssignedConnector(sg1, LambdaConnector(x => x)) + AssignedPredicate(sa, a)) + } + test("schematic connector of schematic boolean constant with partial mapping of 0-ary predicate and different connector") { + checkDoesNotUnify(sg1(sa), b, emptyContext + AssignedConnector(sg1, LambdaConnector(_ => a)) + AssignedPredicate(sa, b)) + } + test("predicate of a variable") { + checkUnifiesAs(p1(x), p1(x))(emptyResult + (x -> x)) + } + test("predicate of a variable to a predicate of a different variable") { + checkUnifiesAs(p1(x), p1(y))(emptyResult + (x -> y)) + } + test("predicate of a variable to a predicate of a different variable with partial mapping of variables") { + checkUnifiesAs(p1(x), p1(y), emptyContext + (x -> y))(emptyResult + (x -> y)) + } + test("predicate of a variable to a predicate of a different variable with incompatible partial mapping of variables") { + checkDoesNotUnify(p1(x), p1(y), emptyContext + (x -> z)) + } + + test("exists constant") { + checkUnifiesAs(exists(x, a), exists(x, a))(emptyResult + (x -> x)) + } + test("exists constant with different bound variables") { + checkUnifiesAs(exists(x, a), exists(y, a))(emptyResult + (x -> y)) + } + test("exists expression with different bound variables") { + checkUnifiesAs(exists(x, p1(x)), exists(y, p1(y)))(emptyResult + (x -> y)) + } + test("exists expression of bound vs free variable") { + checkDoesNotUnify(exists(x, p1(x)), exists(y, p1(z))) + } + test("exists schematic predicate to exists expression") { + checkUnifiesAs(exists(x, sp1(x)), exists(y, p1(y) /\ a))(emptyResult + (x -> y) + AssignedPredicate(sp1, LambdaPredicate(v => p1(v) /\ a))) + } + test("exists schematic boolean constant to exists predicate") { + checkUnifiesAs(exists(x, sa), exists(x, p1(t)))(emptyResult + (x -> x) + AssignedPredicate(sa, p1(t))) + } + test("exists schematic boolean constant to exists unary predicate (does not unify)") { + checkDoesNotUnify(exists(x, sa), exists(x, p1(x))) + } + test("exists schematic unary predicate to exists unary predicate with incompatible predicate mapping") { + checkDoesNotUnify(exists(x, sp1(x)), exists(x, p1(x)), emptyContext + (x -> x) + AssignedPredicate(sp1, LambdaPredicate(_ => p1(x)))) + } + test("exists expression: switch bound and free variable") { + checkUnifiesAs(exists(x, exists(y, p1(x) /\ p1(y))), exists(y, exists(x, p1(y) /\ p1(x))))( + emptyResult + (x -> y) + (y -> x) + ) + } + + test("term with different schematic variables to a term with the same variable") { + checkUnifiesAs(p1(st) /\ p1(su), p1(x) /\ p1(x))(emptyResult + AssignedFunction(st, x) + AssignedFunction(su, x)) + } + test("term with same schematic variable to a term with different variables (does not unify)") { + checkDoesNotUnify(p1(st) /\ p1(st), p1(x) /\ p1(y)) + } + test("term with same schematic variable to a term with different schematic variables (does not unify)") { + checkDoesNotUnify(p1(st) /\ p1(st), p1(su) /\ p1(sv)) + } + test("rename schematic variable") { + checkUnifiesAs(p1(st) /\ p1(st), p1(su) /\ p1(su))(emptyResult + AssignedFunction(st, su)) + } +} diff --git a/lisa-kernel/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala b/lisa-kernel/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala index 1faa2a20..c936b33a 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala @@ -7,7 +7,7 @@ private[fol] trait CommonDefinitions { val MaxArity: Int = 1000000 /** - * An labelled node for tree-like structures. + * A labelled node for tree-like structures. */ protected trait Label { val arity: Int @@ -15,16 +15,32 @@ private[fol] trait CommonDefinitions { } /** - * Constant label can represent a symbol of a theory + * \begin{lstlisting}[language=Scala, frame=single] + * object MyTheoryName extends lisa.Main { + * + * THEOREM("theoremName") of "desired conclusion" PROOF { + * + * ... : Proof + * + * } using (listOfJustifications) + * show + * } + * \end{lstlisting} + * A constant label can represent a fixed symbol of a theory or a logical symbol */ trait ConstantLabel extends Label /** - * Schematic label in a formula can be substituted by any constant label of the respective - * kind (predicate or function) + * A schematic label in a formula or a term can be substituted by any formula or term of the adequate kind. */ trait SchematicLabel extends Label + /** + * return am identifier that is different from a set of give identifier. + * @param taken ids which are not available + * @param base prefix of the new id + * @return a fresh id. + */ def freshId(taken: Set[String], base: String): String = { var i = 0; while (taken contains base + "_" + i) i += 1 diff --git a/lisa-kernel/src/main/scala/lisa/kernel/fol/EquivalenceChecker.scala b/lisa-kernel/src/main/scala/lisa/kernel/fol/EquivalenceChecker.scala index def0f7fa..7949c815 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/fol/EquivalenceChecker.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/fol/EquivalenceChecker.scala @@ -12,6 +12,7 @@ import scala.math.Numeric.IntIsIntegral * For soundness, this relation should always be a subrelation of the usual FOL implication. * The implementation checks for Orthocomplemented Bismeilatices equivalence, plus symetry and reflexivity * of equality and alpha-equivalence. + * See https://github.com/epfl-lara/OCBSL for more informations */ private[fol] trait EquivalenceChecker extends FormulaDefinitions { sealed abstract class SimpleFormula { @@ -21,6 +22,9 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { case class SimplePredicate(id: PredicateLabel, args: List[Term]) extends SimpleFormula { val size = 1 } + case class SimpleConnector(id: ConnectorLabel, args: List[SimpleFormula]) extends SimpleFormula { + val size = 1 + } case class SNeg(child: SimpleFormula) extends SimpleFormula { val size: Int = 1 + child.size } @@ -41,6 +45,7 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { val code: Int } case class NormalPredicate(id: PredicateLabel, args: List[Term], code: Int) extends NormalFormula + case class NormalConnector(id: ConnectorLabel, args: List[NormalFormula], code: Int) extends NormalFormula case class NNeg(child: NormalFormula, code: Int) extends NormalFormula case class NOr(children: List[NormalFormula], code: Int) extends NormalFormula case class NForall(x: String, inner: NormalFormula, code: Int) extends NormalFormula @@ -76,6 +81,7 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { case And => SNeg(SOr(args.map(c => SNeg(removeSugar(c))).toList)) case Or => SOr((args map removeSugar).toList) + case _ => SimpleConnector(label, args.toList.map(removeSugar)) } case BinderFormula(label, bound, inner) => label match { @@ -94,14 +100,15 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { ) def toLocallyNameless(t: Term, subst: Map[VariableLabel, Int], i: Int): Term = t match { - case VariableTerm(label) => + case Term(label: VariableLabel, _) => if (subst.contains(label)) VariableTerm(VariableLabel("x" + (i - subst(label)))) else VariableTerm(VariableLabel("_" + label)) - case FunctionTerm(label, args) => FunctionTerm(label, args.map(c => toLocallyNameless(c, subst, i))) + case Term(label, args) => Term(label, args.map(c => toLocallyNameless(c, subst, i))) } def toLocallyNameless(phi: SimpleFormula, subst: Map[VariableLabel, Int], i: Int): SimpleFormula = phi match { case SimplePredicate(id, args) => SimplePredicate(id, args.map(c => toLocallyNameless(c, subst, i))) + case SimpleConnector(id, args) => SimpleConnector(id, args.map(f => toLocallyNameless(f, subst, i))) case SNeg(child) => SNeg(toLocallyNameless(child, subst, i)) case SOr(children) => SOr(children.map(toLocallyNameless(_, subst, i))) case SForall(x, inner) => SForall("", toLocallyNameless(inner, subst + (VariableLabel(x) -> i), i + 1)) @@ -119,9 +126,9 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { def codesOfTerm(t: Term): Int = codesTerms.getOrElseUpdate( t, t match { - case VariableTerm(label) => + case Term(label: VariableLabel, _) => codesSigTerms.getOrElseUpdate((label, Nil), codesSigTerms.size) - case FunctionTerm(label, args) => + case Term(label, args) => val c = args map codesOfTerm codesSigTerms.getOrElseUpdate((label, c), codesSigTerms.size) @@ -195,7 +202,11 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { val r: List[NormalFormula] = phi match { case SimplePredicate(id, args) => val lab = "pred_" + id.id + "_" + id.arity - if (id == equality) { + if (id == top) { + phi.normalForm = Some(NLiteral(true)) + } else if (id == bot) { + phi.normalForm = Some(NLiteral(false)) + } else if (id == equality) { if (codesOfTerm(args(0)) == codesOfTerm(args(1))) phi.normalForm = Some(NLiteral(true)) else @@ -204,6 +215,10 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { phi.normalForm = Some(NormalPredicate(id, args, updateCodesSig((lab, args map codesOfTerm)))) } phi.normalForm.get :: acc + case SimpleConnector(id, args) => + val lab = "conn_" + id.id + "_" + id.arity + phi.normalForm = Some(NormalConnector(id, args.map(_.normalForm.get), updateCodesSig((lab, args map OCBSLCode)))) + phi.normalForm.get :: acc case SNeg(child) => pNeg(child, phi, acc) case SOr(children) => children.foldLeft(acc)((p, a) => pDisj(a, p)) case SForall(x, inner) => @@ -229,7 +244,13 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { val r: List[NormalFormula] = phi match { case SimplePredicate(id, args) => val lab = "pred_" + id.id + "_" + id.arity - if (id == equality) { + if (id == top) { + phi.normalForm = Some(NLiteral(true)) + parent.normalForm = Some(NLiteral(false)) + } else if (id == bot) { + phi.normalForm = Some(NLiteral(false)) + parent.normalForm = Some(NLiteral(true)) + } else if (id == equality) { if (codesOfTerm(args(0)) == codesOfTerm(args(1))) { phi.normalForm = Some(NLiteral(true)) parent.normalForm = Some(NLiteral(false)) @@ -240,8 +261,14 @@ private[fol] trait EquivalenceChecker extends FormulaDefinitions { } else { phi.normalForm = Some(NormalPredicate(id, args, updateCodesSig((lab, args map codesOfTerm)))) parent.normalForm = Some(NNeg(phi.normalForm.get, updateCodesSig(("neg", List(phi.normalForm.get.code))))) + // phi.normalForm = Some(NormalPredicate(id, args, updateCodesSig((lab, args map codesOfTerm)))) } parent.normalForm.get :: acc + case SimpleConnector(id, args) => + val lab = "conn_" + id.id + "_" + id.arity + phi.normalForm = Some(NormalConnector(id, args.map(_.normalForm.get), updateCodesSig((lab, args map OCBSLCode)))) + parent.normalForm = Some(NNeg(phi.normalForm.get, updateCodesSig(("neg", List(phi.normalForm.get.code))))) + parent.normalForm.get :: acc case SNeg(child) => pDisj(child, acc) case SForall(x, inner) => val r = OCBSLCode(inner) diff --git a/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaDefinitions.scala b/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaDefinitions.scala index 84321108..81247cb9 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaDefinitions.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaDefinitions.scala @@ -10,13 +10,34 @@ private[fol] trait FormulaDefinitions extends FormulaLabelDefinitions with TermD * The parent class of formulas. * A formula is a tree whose nodes are either terms or labeled by predicates or logical connectors. */ - sealed abstract class Formula extends TreeWithLabel[FormulaLabel] { + sealed trait Formula extends TreeWithLabel[FormulaLabel] { + val arity: Int = label.arity + override def constantTermLabels: Set[ConstantFunctionLabel] + override def schematicTermLabels: Set[SchematicTermLabel] + override def freeSchematicTermLabels: Set[SchematicTermLabel] + override def freeVariables: Set[VariableLabel] - def constantFunctions: Set[ConstantFunctionLabel] - def schematicTerms: Set[SchematicTermLabel] + /** + * @return The list of constant predicate symbols in the formula. + */ + def constantPredicateLabels: Set[ConstantPredicateLabel] + + /** + * @return The list of schematic predicate symbols in the formula, including variable formulas . + */ + def schematicPredicateLabels: Set[SchematicVarOrPredLabel] + + /** + * @return The list of schematic connector symbols in the formula. + */ + def schematicConnectorLabels: Set[SchematicConnectorLabel] + + /** + * @return The list of schematic connector, predicate and formula variable symbols in the formula. + */ + def schematicFormulaLabels: Set[SchematicFormulaLabel] = + (schematicPredicateLabels.toSet: Set[SchematicFormulaLabel]) union (schematicConnectorLabels.toSet: Set[SchematicFormulaLabel]) - def constantPredicates: Set[ConstantPredicateLabel] - def schematicPredicates: Set[SchematicPredicateLabel] } /** @@ -24,48 +45,68 @@ private[fol] trait FormulaDefinitions extends FormulaLabelDefinitions with TermD */ sealed case class PredicateFormula(label: PredicateLabel, args: Seq[Term]) extends Formula { require(label.arity == args.size) - override def freeVariables: Set[VariableLabel] = args.foldLeft(Set.empty[VariableLabel])((prev, next) => prev union next.freeVariables) - - override def constantPredicates: Set[ConstantPredicateLabel] = label match { + override def constantTermLabels: Set[ConstantFunctionLabel] = + args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantTermLabels) + override def schematicTermLabels: Set[SchematicTermLabel] = + args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTermLabels) + override def freeSchematicTermLabels: Set[SchematicTermLabel] = + args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.freeSchematicTermLabels) + override def freeVariables: Set[VariableLabel] = + args.foldLeft(Set.empty[VariableLabel])((prev, next) => prev union next.freeVariables) + override def constantPredicateLabels: Set[ConstantPredicateLabel] = label match { case l: ConstantPredicateLabel => Set(l) - case l: SchematicPredicateLabel => Set() + case _ => Set() } - override def schematicPredicates: Set[SchematicPredicateLabel] = label match { - case l: ConstantPredicateLabel => Set() - case l: SchematicPredicateLabel => Set(l) + override def schematicPredicateLabels: Set[SchematicVarOrPredLabel] = label match { + case l: SchematicVarOrPredLabel => Set(l) + case _ => Set() } - - override def constantFunctions: Set[ConstantFunctionLabel] = args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantFunctions) - override def schematicTerms: Set[SchematicTermLabel] = args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTerms) + override def schematicConnectorLabels: Set[SchematicConnectorLabel] = Set() } /** * The formula counterpart of [[ConnectorLabel]]. */ sealed case class ConnectorFormula(label: ConnectorLabel, args: Seq[Formula]) extends Formula { - require(label.arity == -1 || label.arity == args.length) - override def freeVariables: Set[VariableLabel] = args.foldLeft(Set.empty[VariableLabel])((prev, next) => prev union next.freeVariables) - - override def constantFunctions: Set[ConstantFunctionLabel] = args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantFunctions) - override def schematicTerms: Set[SchematicTermLabel] = args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTerms) - - override def constantPredicates: Set[ConstantPredicateLabel] = args.foldLeft(Set.empty[ConstantPredicateLabel])((prev, next) => prev union next.constantPredicates) - override def schematicPredicates: Set[SchematicPredicateLabel] = args.foldLeft(Set.empty[SchematicPredicateLabel])((prev, next) => prev union next.schematicPredicates) + require(label.arity == args.size || label.arity == -1) + require(label.arity != 0) + override def constantTermLabels: Set[ConstantFunctionLabel] = + args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantTermLabels) + override def schematicTermLabels: Set[SchematicTermLabel] = + args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTermLabels) + override def freeSchematicTermLabels: Set[SchematicTermLabel] = + args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.freeSchematicTermLabels) + override def freeVariables: Set[VariableLabel] = + args.foldLeft(Set.empty[VariableLabel])((prev, next) => prev union next.freeVariables) + override def constantPredicateLabels: Set[ConstantPredicateLabel] = + args.foldLeft(Set.empty[ConstantPredicateLabel])((prev, next) => prev union next.constantPredicateLabels) + override def schematicPredicateLabels: Set[SchematicVarOrPredLabel] = + args.foldLeft(Set.empty[SchematicVarOrPredLabel])((prev, next) => prev union next.schematicPredicateLabels) + override def schematicConnectorLabels: Set[SchematicConnectorLabel] = label match { + case l: ConstantConnectorLabel => + args.foldLeft(Set.empty[SchematicConnectorLabel])((prev, next) => prev union next.schematicConnectorLabels) + case l: SchematicConnectorLabel => + args.foldLeft(Set(l))((prev, next) => prev union next.schematicConnectorLabels) + } } /** * The formula counterpart of [[BinderLabel]]. */ sealed case class BinderFormula(label: BinderLabel, bound: VariableLabel, inner: Formula) extends Formula { + override def constantTermLabels: Set[ConstantFunctionLabel] = inner.constantTermLabels + override def schematicTermLabels: Set[SchematicTermLabel] = inner.schematicTermLabels + override def freeSchematicTermLabels: Set[SchematicTermLabel] = inner.freeSchematicTermLabels - bound override def freeVariables: Set[VariableLabel] = inner.freeVariables - bound - - override def constantFunctions: Set[ConstantFunctionLabel] = inner.constantFunctions - override def schematicTerms: Set[SchematicTermLabel] = inner.schematicTerms - bound - - override def constantPredicates: Set[ConstantPredicateLabel] = inner.constantPredicates - override def schematicPredicates: Set[SchematicPredicateLabel] = inner.schematicPredicates + override def constantPredicateLabels: Set[ConstantPredicateLabel] = inner.constantPredicateLabels + override def schematicPredicateLabels: Set[SchematicVarOrPredLabel] = inner.schematicPredicateLabels + override def schematicConnectorLabels: Set[SchematicConnectorLabel] = inner.schematicConnectorLabels } + /** + * Binds multiple variables at the same time + */ + @deprecated def bindAll(binder: BinderLabel, vars: Seq[VariableLabel], phi: Formula): Formula = vars.foldLeft(phi)((f, v) => BinderFormula(binder, v, f)) diff --git a/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala b/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala index d3252743..fb6e3bc7 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala @@ -7,37 +7,26 @@ private[fol] trait FormulaLabelDefinitions extends CommonDefinitions { /** * The parent class of formula labels. - * It similar as with terms; they denote the Predicates and logical connector themselves, and not the terms they help forming. - * They label the nodes of a tree that defines a formula. - */ - sealed abstract class FormulaLabel extends Label with Ordered[FormulaLabel] { - def priority: Int = this match { - case _: ConstantPredicateLabel => 1 - case _: SchematicPredicateLabel => 2 - case _: ConnectorLabel => 3 - case _: BinderLabel => 4 - } - - /** - * Compare two formula labels by type, then by arity, then by id. - */ - def compare(that: FormulaLabel): Int = (this, that) match { - case (thi: ConstantPredicateLabel, tha: ConstantPredicateLabel) => (2 * (thi.arity compare tha.arity) + (thi.id compare tha.id)).sign - case (thi: SchematicPredicateLabel, tha: SchematicPredicateLabel) => (2 * (thi.arity compare tha.arity) + (thi.id compare tha.id)).sign - case (thi: ConnectorLabel, tha: ConnectorLabel) => thi.id compare tha.id - case (thi: BinderLabel, tha: BinderLabel) => thi.id compare tha.id - case _ => this.priority - that.priority - } - } + * These are labels that can be applied to nodes that form the tree of a formula. + * In logical terms, those labels are FOL symbols or predicate symbols, including equality. + */ + sealed abstract class FormulaLabel extends Label /** * The label for a predicate, namely a function taking a fixed number of terms and returning a formula. * In logical terms it is a predicate symbol. */ - sealed abstract class PredicateLabel extends FormulaLabel { + sealed trait PredicateLabel extends FormulaLabel { require(arity < MaxArity && arity >= 0) } + /** + * The label for a connector, namely a function taking a fixed number of formulas and returning another formula. + */ + sealed trait ConnectorLabel extends FormulaLabel { + require(arity < MaxArity && arity >= -1) + } + /** * A standard predicate symbol. Typical example are equality (=) and membership (∈) */ @@ -45,42 +34,53 @@ private[fol] trait FormulaLabelDefinitions extends CommonDefinitions { /** * The equality symbol (=) for first order logic. + * It is represented as any other predicate symbol but has unique semantic and deduction rules. */ val equality: ConstantPredicateLabel = ConstantPredicateLabel("=", 2) + val top: ConstantPredicateLabel = ConstantPredicateLabel("⊤", 0) + val bot: ConstantPredicateLabel = ConstantPredicateLabel("⊥", 0) /** - * A predicate symbol that can be instantiated with any formula. + * The label for a connector, namely a function taking a fixed number of formulas and returning another formula. */ - sealed abstract class SchematicPredicateLabel extends PredicateLabel with SchematicLabel + sealed abstract class ConstantConnectorLabel(val id: String, val arity: Int) extends ConnectorLabel with ConstantLabel + case object Neg extends ConstantConnectorLabel("¬", 1) + + case object Implies extends ConstantConnectorLabel("⇒", 2) + + case object Iff extends ConstantConnectorLabel("↔", 2) + + case object And extends ConstantConnectorLabel("∧", -1) + + case object Or extends ConstantConnectorLabel("∨", -1) /** - * A predicate symbol of non-zero arity that can be instantiated with any formula taking arguments. + * A schematic symbol that can be instantiated with some formula. + * We distinguish arity-0 schematic formula labels, arity->1 schematic predicates and arity->1 schematic connectors. */ - sealed case class SchematicNPredicateLabel(id: String, arity: Int) extends SchematicPredicateLabel + sealed trait SchematicFormulaLabel extends FormulaLabel with SchematicLabel + + /** + * A schematic symbol whose arguments are any number of Terms. This means the symbol is either a variable formula or a predicate schema + */ + sealed trait SchematicVarOrPredLabel extends SchematicFormulaLabel with PredicateLabel /** * A predicate symbol of arity 0 that can be instantiated with any formula. */ - sealed case class VariableFormulaLabel(id: String) extends SchematicPredicateLabel { + sealed case class VariableFormulaLabel(id: String) extends SchematicVarOrPredLabel { val arity = 0 } /** - * The label for a connector, namely a function taking a fixed number of formulas and returning another formula. + * A predicate symbol of non-zero arity that can be instantiated with any functional formula taking term arguments. */ - sealed abstract class ConnectorLabel(val id: String, val arity: Int) extends FormulaLabel { - require(arity < MaxArity && arity >= -1) - } - - case object Neg extends ConnectorLabel("¬", 1) - - case object Implies extends ConnectorLabel("⇒", 2) + sealed case class SchematicPredicateLabel(id: String, arity: Int) extends SchematicVarOrPredLabel - case object Iff extends ConnectorLabel("↔", 2) - - case object And extends ConnectorLabel("∧", -1) - - case object Or extends ConnectorLabel("∨", -1) + /** + * A predicate symbol of non-zero arity that can be instantiated with any functional formula taking formula arguments. + */ + sealed case class SchematicConnectorLabel(id: String, arity: Int) extends SchematicFormulaLabel with ConnectorLabel /** * The label for a binder, namely an object with a body that has the ability to bind variables in it. @@ -89,12 +89,24 @@ private[fol] trait FormulaLabelDefinitions extends CommonDefinitions { val arity = 1 } + /** + * The symbol of the universal quantifier ∀ + */ case object Forall extends BinderLabel(id = "∀") + /** + * The symbol of the existential quantifier ∃ + */ case object Exists extends BinderLabel(id = "∃") + /** + * The symbol of the quantifier for existence and unicity ∃! + */ case object ExistsOne extends BinderLabel(id = "∃!") - def isSame(l: FormulaLabel, r: FormulaLabel): Boolean = (l compare r) == 0 + /** + * A function returning true if and only if the two symbols are considered "the same", i.e. same category, same arity and same id. + */ + def isSame(l: FormulaLabel, r: FormulaLabel): Boolean = l == r } diff --git a/lisa-kernel/src/main/scala/lisa/kernel/fol/Substitutions.scala b/lisa-kernel/src/main/scala/lisa/kernel/fol/Substitutions.scala index edc86647..7f810269 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/fol/Substitutions.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/fol/Substitutions.scala @@ -3,7 +3,8 @@ package lisa.kernel.fol trait Substitutions extends FormulaDefinitions { /** - * A lambda term to express a "term with holes". Main use is to be substituted in place of a function schema. + * A lambda term to express a "term with holes". Main use is to be substituted in place of a function schema or variable. + * Also used for some deduction rules. * Morally equivalent to a 2-tuples containing the same informations. * @param vars The names of the "holes" in the term, necessarily of arity 0. The bound variables of the functional term. * @param body The term represented by the object, up to instantiation of the bound schematic variables in args. @@ -13,7 +14,8 @@ trait Substitutions extends FormulaDefinitions { } /** - * A lambda formula to express a "formula with holes". Main use is to be substituted in place of a predicate schema. + * A lambda formula to express a "formula with term holes". Main use is to be substituted in place of a predicate schema. + * Also used for some deduction rules. * Morally equivalent to a 2-tuples containing the same informations. * @param vars The names of the "holes" in a formula, necessarily of arity 0. The bound variables of the functional formula. * @param body The formula represented by the object, up to instantiation of the bound schematic variables in args. @@ -22,17 +24,20 @@ trait Substitutions extends FormulaDefinitions { def apply(args: Seq[Term]): Formula = { substituteVariables(body, (vars zip args).toMap) } - // def instantiateFunctionSchemas(phi: Formula, m: Map[SchematicFunctionLabel, LambdaTermTerm]):Formula = ??? } /** - * A lambda formula to express a "formula with holes". Usefull for rules such as Iff substitution + * A lambda formula to express a "formula with formula holes". Main use is to be substituted in place of a connector schema. + * Also used for some deduction rules. * Morally equivalent to a 2-tuples containing the same informations. * @param vars The names of the "holes" in a formula, necessarily of arity 0. * @param body The formula represented by the object, up to instantiation of the bound schematic variables in args. */ case class LambdaFormulaFormula(vars: Seq[VariableFormulaLabel], body: Formula) { - def apply(args: Seq[Formula]): Formula = instantiatePredicateSchemas(body, (vars zip (args map (LambdaTermFormula(Nil, _)))).toMap) + def apply(args: Seq[Formula]): Formula = { + substituteFormulaVariables(body, (vars zip args).toMap) + // instantiatePredicateSchemas(body, (vars zip (args map (LambdaTermFormula(Nil, _)))).toMap) + } } ////////////////////////// @@ -40,34 +45,33 @@ trait Substitutions extends FormulaDefinitions { ////////////////////////// /** - * Performs simultaneous substitution of multiple variables by multiple terms in a term t. + * Performs simultaneous substitution of multiple variables by multiple terms in a term. * @param t The base term * @param m A map from variables to terms. * @return t[m] */ def substituteVariables(t: Term, m: Map[VariableLabel, Term]): Term = t match { - case VariableTerm(label) => m.getOrElse(label, t) - case FunctionTerm(label, args) => FunctionTerm(label, args.map(substituteVariables(_, m))) + case Term(label: VariableLabel, _) => m.getOrElse(label, t) + case Term(label, args) => Term(label, args.map(substituteVariables(_, m))) } /** * Performs simultaneous substitution of schematic function symbol by "functional" terms, or terms with holes. * If the arity of one of the function symbol to substitute doesn't match the corresponding number of arguments, it will produce an error. * @param t The base term - * @param m The map from schematic function symbols to "terms with holes". A such term is a pair containing a list of - * variable symbols (holes) and a term that is the body of the functional term. + * @param m The map from schematic function symbols to lambda expressions Term(s) -> Term [[LambdaTermTerm]]. * @return t[m] */ def instantiateTermSchemas(t: Term, m: Map[SchematicTermLabel, LambdaTermTerm]): Term = { require(m.forall { case (symbol, LambdaTermTerm(arguments, body)) => arguments.length == symbol.arity }) t match { - case VariableTerm(label) => m.get(label).map(_.apply(Nil)).getOrElse(t) - case FunctionTerm(label, args) => + case Term(label: VariableLabel, _) => m.get(label).map(_.apply(Nil)).getOrElse(t) + case Term(label, args) => val newArgs = args.map(instantiateTermSchemas(_, m)) label match { - case label: ConstantFunctionLabel => FunctionTerm(label, newArgs) - case label: SchematicFunctionLabel => - m.get(label).map(_(newArgs)).getOrElse(FunctionTerm(label, newArgs)) + case label: ConstantFunctionLabel => Term(label, newArgs) + case label: SchematicTermLabel => + m.get(label).map(_(newArgs)).getOrElse(Term(label, newArgs)) } } } @@ -77,13 +81,13 @@ trait Substitutions extends FormulaDefinitions { ///////////////////////////// /** - * Performs simultaneous substitution of multiple variables by multiple terms in a formula f. + * Performs simultaneous substitution of multiple variables by multiple terms in a formula. * - * @param f The base formula - * @param m A map from variables to terms. + * @param phi The base formula + * @param m A map from variables to terms * @return t[m] */ - def substituteVariables(f: Formula, m: Map[VariableLabel, Term]): Formula = f match { + def substituteVariables(phi: Formula, m: Map[VariableLabel, Term]): Formula = phi match { case PredicateFormula(label, args) => PredicateFormula(label, args.map(substituteVariables(_, m))) case ConnectorFormula(label, args) => ConnectorFormula(label, args.map(substituteVariables(_, m))) case BinderFormula(label, bound, inner) => @@ -97,13 +101,32 @@ trait Substitutions extends FormulaDefinitions { } /** - * Performs simultaneous substitution of schematic function symbol by "functional" terms, or terms with holes. - * If the arity of one of the function symbol to substitute doesn't match the corresponding number of arguments, it will produce an error. + * Performs simultaneous substitution of multiple formula variables by multiple formula terms in a formula. + * * @param phi The base formula - * @param m The map from schematic function symbols to "terms with holes". A such term is a pair containing a list of - * variable symbols (holes) and a term that is the body of the functional term. + * @param m A map from variables to terms * @return t[m] */ + def substituteFormulaVariables(phi: Formula, m: Map[VariableFormulaLabel, Formula]): Formula = phi match { + case PredicateFormula(label: VariableFormulaLabel, _) => m.getOrElse(label, phi) + case _: PredicateFormula => phi + case ConnectorFormula(label, args) => ConnectorFormula(label, args.map(substituteFormulaVariables(_, m))) + case BinderFormula(label, bound, inner) => + val fv = m.values.flatMap(_.freeVariables).toSet + if (fv.contains(bound)) { + val newBoundVariable = VariableLabel(freshId(fv.map(_.name), bound.name)) + val newInner = substituteVariables(inner, Map(bound -> VariableTerm(newBoundVariable))) + BinderFormula(label, newBoundVariable, substituteFormulaVariables(newInner, m)) + } else BinderFormula(label, bound, substituteFormulaVariables(inner, m)) + } + + /** + * Performs simultaneous substitution of schematic function symbol by "functional" terms, or terms with holes. + * If the arity of one of the predicate symbol to substitute doesn't match the corresponding number of arguments, it will produce an error. + * @param phi The base formula + * @param m The map from schematic function symbols to lambda expressions Term(s) -> Term [[LambdaTermTerm]]. + * @return phi[m] + */ def instantiateTermSchemas(phi: Formula, m: Map[SchematicTermLabel, LambdaTermTerm]): Formula = { require(m.forall { case (symbol, LambdaTermTerm(arguments, body)) => arguments.length == symbol.arity }) phi match { @@ -122,19 +145,18 @@ trait Substitutions extends FormulaDefinitions { /** * Instantiate a schematic predicate symbol in a formula, using higher-order instantiation. - * + * If the arity of one of the connector symbol to substitute doesn't match the corresponding number of arguments, it will produce an error. * @param phi The base formula - * @param m The map from schematic function symbols to "terms with holes". A such term is a pair containing a list of - * variable symbols (holes) and a term that is the body of the functional term. - * @return t[m] + * @param m The map from schematic predicate symbols to lambda expressions Term(s) -> Formula [[LambdaTermFormula]]. + * @return phi[m] */ - def instantiatePredicateSchemas(phi: Formula, m: Map[SchematicPredicateLabel, LambdaTermFormula]): Formula = { + def instantiatePredicateSchemas(phi: Formula, m: Map[SchematicVarOrPredLabel, LambdaTermFormula]): Formula = { require(m.forall { case (symbol, LambdaTermFormula(arguments, body)) => arguments.length == symbol.arity }) phi match { case PredicateFormula(label, args) => label match { case label: SchematicPredicateLabel if m.contains(label) => m(label)(args) - case label => phi + case _ => phi } case ConnectorFormula(label, args) => ConnectorFormula(label, args.map(instantiatePredicateSchemas(_, m))) case BinderFormula(label, bound, inner) => @@ -147,6 +169,33 @@ trait Substitutions extends FormulaDefinitions { } } + /** + * Instantiate a schematic connector symbol in a formula, using higher-order instantiation. + * + * @param phi The base formula + * @param m The map from schematic function symbols to lambda expressions Formula(s) -> Formula [[LambdaFormulaFormula]]. + * @return phi[m] + */ + def instantiateConnectorSchemas(phi: Formula, m: Map[SchematicConnectorLabel, LambdaFormulaFormula]): Formula = { + require(m.forall { case (symbol, LambdaFormulaFormula(arguments, body)) => arguments.length == symbol.arity }) + phi match { + case _: PredicateFormula => phi + case ConnectorFormula(label, args) => + label match { + case label: SchematicConnectorLabel if m.contains(label) => m(label)(args) + case _ => ConnectorFormula(label, args.map(instantiateConnectorSchemas(_, m))) + } + case BinderFormula(label, bound, inner) => + val fv: Set[VariableLabel] = (m.flatMap { case (symbol, LambdaFormulaFormula(arguments, body)) => body.freeVariables }).toSet + if (fv.contains(bound)) { + val newBoundVariable = VariableLabel(freshId(fv.map(_.name), bound.name)) + val newInner = substituteVariables(inner, Map(bound -> VariableTerm(newBoundVariable))) + BinderFormula(label, newBoundVariable, instantiateConnectorSchemas(newInner, m)) + } else BinderFormula(label, bound, instantiateConnectorSchemas(inner, m)) + } + } + + @deprecated def instantiateBinder(f: BinderFormula, t: Term): Formula = substituteVariables(f.inner, Map(f.bound -> t)) } diff --git a/lisa-kernel/src/main/scala/lisa/kernel/fol/TermDefinitions.scala b/lisa-kernel/src/main/scala/lisa/kernel/fol/TermDefinitions.scala index 23359f10..58d7ad10 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/fol/TermDefinitions.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/fol/TermDefinitions.scala @@ -7,63 +7,70 @@ private[fol] trait TermDefinitions extends TermLabelDefinitions { protected trait TreeWithLabel[A] { val label: A + val arity: Int /** - * @return The list of free variables in the term + * @return The list of free variables in the tree. */ def freeVariables: Set[VariableLabel] /** * @return The list of constant (i.e. non schematic) function symbols, including of arity 0. */ - def constantFunctions: Set[ConstantFunctionLabel] + def constantTermLabels: Set[ConstantFunctionLabel] /** - * @return The list of schematic function symbols (including free variables) in the term + * @return The list of schematic term symbols (including free and bound variables) in the tree. */ - def schematicTerms: Set[SchematicTermLabel] - } + def schematicTermLabels: Set[SchematicTermLabel] - /** - * The parent classes of terms. - * A term is a tree with nodes labeled by functions labels or variables. - * The number of children of a node is restricted by the arity imposed by the label. - */ - sealed abstract class Term extends TreeWithLabel[TermLabel] - - /** - * A term which consists of a single variable. - * - * @param label The label of the variable. - */ - sealed case class VariableTerm(label: VariableLabel) extends Term { - override def freeVariables: Set[VariableLabel] = Set(label) - - override def constantFunctions: Set[ConstantFunctionLabel] = Set.empty - override def schematicTerms: Set[SchematicTermLabel] = Set(label) + /** + * @return The list of schematic term symbols (excluding bound variables) in the tree. + */ + def freeSchematicTermLabels: Set[SchematicTermLabel] } /** - * A term labelled by a function symbol. It must contain a number of children equal to the arity of the symbol - * + * A term labelled by a function symbol. It must contain a number of children equal to the arity of the symbol. + * The label can be a constant or schematic term label of any arity, including a variable label. * @param label The label of the node * @param args children of the node. The number of argument must be equal to the arity of the function. */ - sealed case class FunctionTerm(label: FunctionLabel, args: Seq[Term]) extends Term { + sealed case class Term(label: TermLabel, args: Seq[Term]) extends TreeWithLabel[TermLabel] { require(label.arity == args.size) + val arity: Int = label.arity - override def freeVariables: Set[VariableLabel] = args.foldLeft(Set.empty[VariableLabel])((prev, next) => prev union next.freeVariables) + override def freeVariables: Set[VariableLabel] = label match { + case l: VariableLabel => Set(l) + case _ => args.foldLeft(Set.empty[VariableLabel])((prev, next) => prev union next.freeVariables) + } - override def constantFunctions: Set[ConstantFunctionLabel] = label match { - case l: ConstantFunctionLabel => args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantFunctions) + l - case l: SchematicFunctionLabel => args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantFunctions) + override def constantTermLabels: Set[ConstantFunctionLabel] = label match { + case l: ConstantFunctionLabel => args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantTermLabels) + l + case l: SchematicTermLabel => args.foldLeft(Set.empty[ConstantFunctionLabel])((prev, next) => prev union next.constantTermLabels) } - override def schematicTerms: Set[SchematicTermLabel] = label match { - case l: ConstantFunctionLabel => args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTerms) - case l: SchematicFunctionLabel => args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTerms) + l + override def schematicTermLabels: Set[SchematicTermLabel] = label match { + case l: ConstantFunctionLabel => args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTermLabels) + case l: SchematicTermLabel => args.foldLeft(Set.empty[SchematicTermLabel])((prev, next) => prev union next.schematicTermLabels) + l } + override def freeSchematicTermLabels: Set[SchematicTermLabel] = schematicTermLabels + } + + /** + * A VariableTerm is exactly an arity-0 term whose label is a variable label, but we provide specific constructors and destructors. + */ + object VariableTerm extends (VariableLabel => Term) { - val arity: Int = args.size + /** + * A term which consists of a single variable. + * + * @param label The label of the variable. + */ + def apply(label: VariableLabel): Term = Term(label, Seq()) + def unapply(t: Term): Option[VariableLabel] = t.label match { + case l: VariableLabel => Some(l) + case _ => None + } } } diff --git a/lisa-kernel/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala b/lisa-kernel/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala index 3c2ca18d..cb8b2aeb 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala @@ -9,58 +9,38 @@ private[fol] trait TermLabelDefinitions extends CommonDefinitions { * The parent class of term labels. * These are labels that can be applied to nodes that form the tree of a term. * For example, Powerset is not a term itself, it's a label for a node with a single child in a tree corresponding to a term. - * In logical terms, those labels are essentially symbols of sme language. + * In logical terms, those labels are essentially symbols of some language. */ - sealed abstract class TermLabel extends Label with Ordered[TermLabel] { - def priority: Int = this match { - case _: VariableLabel => 1 - case _: ConstantFunctionLabel => 2 - case _: SchematicFunctionLabel => 3 - } - - /** - * Sorts labels according to first whether the term is a variable or function, then according to arity, then to the id. - */ - def compare(that: TermLabel): Int = (this, that) match { - case (thi: VariableLabel, tha: VariableLabel) => thi.id compare tha.id - case (thi: ConstantFunctionLabel, tha: ConstantFunctionLabel) => (2 * (thi.arity compare tha.arity) + (thi.id compare tha.id)).sign - case (thi: SchematicFunctionLabel, tha: SchematicFunctionLabel) => (2 * (thi.arity compare tha.arity) + (thi.id compare tha.id)).sign - case _ => this.priority - that.priority - } - } - - /** - * The label of a function-like term. Constants are functions of arity 0. - * There are two kinds of function symbols: Standards and schematic. - * Standard function symbols denote a particular function. Schematic function symbols - * can be instantiated with any term. This is particularly useful to express axiom schemas. - */ - sealed abstract class FunctionLabel extends TermLabel { + sealed abstract class TermLabel extends Label { require(arity >= 0 && arity < MaxArity) } /** - * A function symbol. + * A fixed function symbol. If arity is 0, it is just a regular constant symbol. * * @param id The name of the function symbol. * @param arity The arity of the function symbol. A function symbol of arity 0 is a constant */ - sealed case class ConstantFunctionLabel(id: String, arity: Int) extends FunctionLabel with ConstantLabel + sealed case class ConstantFunctionLabel(id: String, arity: Int) extends TermLabel with ConstantLabel - sealed trait SchematicTermLabel extends TermLabel {} + /** + * A schematic symbol which is uninterpreted and can be substituted by functional term of the same arity. + * We distinguish arity 0 schematic term labels which we call variables and can be bound, and arity>1 schematic symbols. + */ + sealed trait SchematicTermLabel extends TermLabel with SchematicLabel {} /** * A schematic function symbol that can be substituted. * * @param id The name of the function symbol. - * @param arity The arity of the function symbol. A function symbol of arity 0 is a constant + * @param arity The arity of the function symbol. Must be greater than 1. */ - sealed case class SchematicFunctionLabel(id: String, arity: Int) extends FunctionLabel with SchematicTermLabel { + sealed case class SchematicFunctionLabel(id: String, arity: Int) extends SchematicTermLabel { require(arity >= 1 && arity < MaxArity) } /** - * The label of a term which is a variable. + * The label of a term which is a variable. Can be bound in a formulas, or substituted for an arbitrary term. * * @param id The name of the variable, for example "x" or "y". */ @@ -70,7 +50,7 @@ private[fol] trait TermLabelDefinitions extends CommonDefinitions { } /** - * A function returning true if and only if the two symbols are considered "the same". + * A function returning true if and only if the two symbols are considered "the same", i.e. same category, same arity and same id. */ - def isSame(l: TermLabel, r: TermLabel): Boolean = (l compare r) == 0 + def isSame(l: TermLabel, r: TermLabel): Boolean = l == r } diff --git a/lisa-kernel/src/main/scala/lisa/kernel/proof/RunningTheory.scala b/lisa-kernel/src/main/scala/lisa/kernel/proof/RunningTheory.scala index 5d17880e..b6c4ec29 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/proof/RunningTheory.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/proof/RunningTheory.scala @@ -68,17 +68,6 @@ class RunningTheory { private[proof] val knownSymbols: mMap[String, ConstantLabel] = mMap(equality.id -> equality) - /** - * Check if a label is a symbol of the theory - */ - - def isSymbol(label: ConstantLabel): Boolean = label match { - case c: ConstantFunctionLabel => funDefinitions.contains(c) - case c: ConstantPredicateLabel => predDefinitions.contains(c) - } - - def isAvailable(label: ConstantLabel): Boolean = !knownSymbols.contains(label.id) - /** * From a given proof, if it is true in the Running theory, add that theorem to the theory and returns it. * The proof's imports must be justified by the list of justification, and the conclusion of the theorem @@ -113,15 +102,14 @@ class RunningTheory { * and the formula can't contain symbols that are not in the theory. * * @param label The desired label. - * @param args The variables representing the arguments of the predicate in the formula phi. - * @param phi The formula defining the predicate. + * @param expression The functional formula defining the predicate. * @return A definition object if the parameters are correct, */ def makePredicateDefinition(label: ConstantPredicateLabel, expression: LambdaTermFormula): RunningTheoryJudgement[this.PredicateDefinition] = { val LambdaTermFormula(vars, body) = expression if (belongsToTheory(body)) if (isAvailable(label)) - if (body.schematicTerms.subsetOf(vars.toSet) && body.schematicPredicates.isEmpty) { + if (body.freeSchematicTermLabels.subsetOf(vars.toSet) && body.schematicPredicateLabels.isEmpty) { val newDef = PredicateDefinition(label, expression) predDefinitions.update(label, Some(newDef)) knownSymbols.update(label.id, label) @@ -141,9 +129,8 @@ class RunningTheory { * @param proof The proof of existence and uniqueness * @param justifications The justifications of the proof. * @param label The desired label. - * @param args The variables representing the arguments of the predicate in the formula phi. + * @param expression The functional term defining the function symbol. * @param out The variable representing the function's result in the formula - * @param phi The formula defining the predicate. * @return A definition object if the parameters are correct, */ def makeFunctionDefinition( @@ -156,7 +143,7 @@ class RunningTheory { val LambdaTermFormula(vars, body) = expression if (belongsToTheory(body)) if (isAvailable(label)) { - if (body.schematicTerms.subsetOf((vars appended out).toSet) && body.schematicPredicates.isEmpty) + if (body.freeSchematicTermLabels.subsetOf((vars appended out).toSet) && body.schematicPredicateLabels.isEmpty) { if (proof.imports.forall(i => justifications.exists(j => isSameSequent(i, sequentFromJustification(j))))) { val r = SCProofChecker.checkSCProof(proof) r match { @@ -175,7 +162,13 @@ class RunningTheory { case r @ SCProofCheckerJudgement.SCInvalidProof(_, path, message) => InvalidJustification("The given proof is incorrect: " + message, Some(r)) } } else InvalidJustification("Not all imports of the proof are correctly justified.", None) - else InvalidJustification("The definition is not allowed to contain schematic symbols or free variables.", None) + } else { + println(body.schematicTermLabels.subsetOf((vars appended out).toSet)) + println(body.schematicTermLabels) + println((vars appended out).toSet) + println(body.schematicPredicateLabels.isEmpty) + InvalidJustification("The definition is not allowed to contain schematic symbols or free variables.", None) + } } else InvalidJustification("The specified symbol id is already part of the theory and can't be redefined.", None) else InvalidJustification("All symbols in the conclusion of the proof must belong to the theory. You need to add missing symbols to the theory.", None) } @@ -184,7 +177,7 @@ class RunningTheory { case Theorem(name, proposition) => proposition case Axiom(name, ax) => Sequent(Set.empty, Set(ax)) case PredicateDefinition(label, LambdaTermFormula(vars, body)) => - val inner = ConnectorFormula(Iff, Seq(PredicateFormula(label, vars.map(VariableTerm)), body)) + val inner = ConnectorFormula(Iff, Seq(PredicateFormula(label, vars.map(VariableTerm.apply)), body)) Sequent(Set(), Set(inner)) case FunctionDefinition(label, out, LambdaTermFormula(vars, body)) => val inner = BinderFormula( @@ -193,7 +186,7 @@ class RunningTheory { ConnectorFormula( Iff, Seq( - PredicateFormula(equality, Seq(FunctionTerm(label, vars.map(VariableTerm)), VariableTerm(out))), + PredicateFormula(equality, Seq(Term(label, vars.map(VariableTerm.apply)), VariableTerm(out))), body ) ) @@ -202,6 +195,54 @@ class RunningTheory { } + /** + * Add a new axiom to the Theory. For example, if the theory contains the language and theorems + * of Zermelo-Fraenkel Set Theory, this function may add the axiom of choice to it. + * If the axiom belongs to the language of the theory, adds it and return true. Else, returns false. + * + * @param f the new axiom to be added. + * @return true if the axiom was added to the theory, false else. + */ + def addAxiom(name: String, f: Formula): Boolean = { + if (belongsToTheory(f)) { + theoryAxioms.update(name, Axiom(name, f)) + true + } else false + } + + /** + * Add a new symbol to the theory, without providing a definition. An ad-hoc definition can be + * added via an axiom, typically if the desired object is not derivable in the base theory itself. + * For example, This function can add the empty set symbol to a theory, and then an axiom asserting + * that it is empty can be introduced as well. + */ + + def addSymbol(s: ConstantLabel): Unit = { + if (isAvailable(s)) { + knownSymbols.update(s.id, s) + s match { + case c: ConstantFunctionLabel => funDefinitions.update(c, None) + case c: ConstantPredicateLabel => predDefinitions.update(c, None) + } + } else {} + } + + /** + * Add all constant symbols in the sequent. Note that this can't be reversed and will prevent from giving them a definition later. + */ + def makeFormulaBelongToTheory(phi: Formula): Unit = { + phi.constantPredicateLabels.foreach(addSymbol) + phi.constantTermLabels.foreach(addSymbol) + } + + /** + * Add all constant symbols in the sequent. Note that this can't be reversed and will prevent from giving them a definition later. + */ + def makeSequentBelongToTheory(s: Sequent): Unit = { + s.left.foreach(makeFormulaBelongToTheory) + s.right.foreach(makeFormulaBelongToTheory) + } + /** * Verify if a given formula belongs to some language * @@ -212,112 +253,97 @@ class RunningTheory { case PredicateFormula(label, args) => label match { case l: ConstantPredicateLabel => isSymbol(l) && args.forall(belongsToTheory) - case _: SchematicPredicateLabel => args.forall(belongsToTheory) + case _ => args.forall(belongsToTheory) } case ConnectorFormula(label, args) => args.forall(belongsToTheory) case BinderFormula(label, bound, inner) => belongsToTheory(inner) } - def makeFormulaBelongToTheory(phi: Formula): Unit = { - phi.constantPredicates.foreach(addSymbol) - phi.constantFunctions.foreach(addSymbol) - } - /** - * Verify if a given term belongs to some language + * Verify if a given term belongs to the language of the theory. * * @param t The term to check - * @return Weather t belongs to the specified language + * @return Weather t belongs to the specified language. */ def belongsToTheory(t: Term): Boolean = t match { - case VariableTerm(label) => true - case FunctionTerm(label, args) => + case Term(label, args) => label match { - case l: ConstantFunctionLabel => isSymbol(l) && args.forall(belongsToTheory(_)) - case _: SchematicFunctionLabel => args.forall(belongsToTheory(_)) + case l: ConstantFunctionLabel => isSymbol(l) && args.forall(belongsToTheory) + case _: SchematicTermLabel => args.forall(belongsToTheory) } } /** - * Verify if a given sequent belongs to some language + * Verify if a given sequent belongs to the language of the theory. * * @param s The sequent to check * @return Weather s belongs to the specified language */ def belongsToTheory(s: Sequent): Boolean = - s.left.forall(belongsToTheory(_)) && s.right.forall(belongsToTheory(_)) - - def makeSequentBelongToTheory(s: Sequent): Unit = { - s.left.foreach(makeFormulaBelongToTheory) - s.right.foreach(makeFormulaBelongToTheory) - } + s.left.forall(belongsToTheory) && s.right.forall(belongsToTheory) /** - * Add a new axiom to the Theory. For example, if the theory contains the language and theorems - * of Zermelo-Fraenkel Set Theory, this function can add the axiom of choice to it. - * If the axiom belongs to the language of the theory, adds it and return true. Else, returns false. + * Public accessor to the set of symbol currently in the theory's language. * - * @param f the new axiom to be added. - * @return true if the axiom was added to the theory, false else. + * @return the set of symbol currently in the theory's language. */ - def addAxiom(name: String, f: Formula): Boolean = { - if (belongsToTheory(f)) { - theoryAxioms.update(name, Axiom(name, f)) - true - } else false - } + def language(): List[(ConstantLabel, Option[Definition])] = funDefinitions.toList ++ predDefinitions.toList /** - * Add a new symbol to the theory, without providing a definition. An ad-hoc definition can be - * added via an axiom, typically if the desired object is not derivable in the base theory itself. - * For example, This function can add the empty set symbol to a theory, and then an axiom asserting - * the it is empty can be introduced as well. + * Check if a label is a symbol of the theory. */ - - def addSymbol(s: ConstantLabel): Unit = { - if (isAvailable(s)) { - knownSymbols.update(s.id, s) - s match { - case c: ConstantFunctionLabel => funDefinitions.update(c, None) - case c: ConstantPredicateLabel => predDefinitions.update(c, None) - } - } else {} + def isSymbol(label: ConstantLabel): Boolean = label match { + case c: ConstantFunctionLabel => funDefinitions.contains(c) + case c: ConstantPredicateLabel => predDefinitions.contains(c) } /** - * Public accessor to the set of symbol currently in the theory's language. - * - * @return the set of symbol currently in the theory's language. + * Check if a label is not already used in the theory. + * @return */ - - def language: List[(ConstantLabel, Option[Definition])] = funDefinitions.toList ++ predDefinitions.toList + def isAvailable(label: ConstantLabel): Boolean = !knownSymbols.contains(label.id) /** * Public accessor to the current set of axioms of the theory * * @return the current set of axioms of the theory */ - def axiomsList: Iterable[Axiom] = theoryAxioms.values + def axiomsList(): Iterable[Axiom] = theoryAxioms.values /** * Verify if a given formula is an axiom of the theory - * - * @param f the candidate axiom - * @return wether f is an axiom of the theory */ def isAxiom(f: Formula): Boolean = theoryAxioms.exists(a => isSame(a._2.ax, f)) + /** + * Get the Axiom that is the same as the given formula, if it exists in the theory. + */ def getAxiom(f: Formula): Option[Axiom] = theoryAxioms.find(a => isSame(a._2.ax, f)).map(_._2) + /** + * Get the definition of the given label, if it is defined in the theory. + */ def getDefinition(label: ConstantPredicateLabel): Option[PredicateDefinition] = predDefinitions.get(label).flatten + /** + * Get the definition of the given label, if it is defined in the theory. + */ def getDefinition(label: ConstantFunctionLabel): Option[FunctionDefinition] = funDefinitions.get(label).flatten + /** + * Get the Axiom with the given name, if it exists in the theory. + */ def getAxiom(name: String): Option[Axiom] = theoryAxioms.get(name) + /** + * Get the Theorem with the given name, if it exists in the theory. + */ def getTheorem(name: String): Option[Theorem] = theorems.get(name) + /** + * Get the definition for the given identifier, if it is defined in the theory. + */ def getDefinition(name: String): Option[Definition] = knownSymbols.get(name).flatMap { case f: ConstantPredicateLabel => getDefinition(f) case f: ConstantFunctionLabel => getDefinition(f) diff --git a/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProof.scala b/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProof.scala index de5b860c..fb4e5c79 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProof.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProof.scala @@ -28,7 +28,7 @@ case class SCProof(steps: IndexedSeq[SCProofStep], imports: IndexedSeq[Sequent] /** * Get the ith sequent of the proof. If the index is positive, give the bottom sequent of proof step number i. - * If the index is positive, return the (-i-1)th imported sequent. + * If the index is negative, return the <code>(-i-1)</code>th imported sequent. * * @param i The reference number of a sequent in the proof * @return A sequent, either imported or reached during the proof. diff --git a/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProofChecker.scala b/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProofChecker.scala index b11218c3..822bd7fa 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProofChecker.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/proof/SCProofChecker.scala @@ -6,19 +6,15 @@ import lisa.kernel.proof.SequentCalculus._ object SCProofChecker { - private object Set { - def unapplySeq[T](s: Set[T]): Option[Seq[T]] = Some(s.toSeq) - } - /** - * This function verifies that a single SCProofStep is correctly applied. It verify that the step only refers to sequents with a lower number, and that - * the type and parameters of the proofstep correspond to the sequent claimed sequent. + * This function verifies that a single SCProofStep is correctly applied. It verifies that the step only refers to sequents with a lower number, + * and that the type, premises and parameters of the proof step correspond to the claimed conclusion. * * @param no The number of the given proof step. Needed to vewrify that the proof step doesn't refer to posterior sequents. * @param step The proof step whose correctness needs to be checked * @param references A function that associates sequents to a range of positive and negative integers that the proof step may refer to. Typically, * a proof's [[SCProof.getSequent]] function. - * @return + * @return A Judgement about the correctness of the proof step. */ def checkSingleSCStep(no: Int, step: SCProofStep, references: Int => Sequent, importsSize: Option[Int] = None): SCProofCheckerJudgement = { val ref = references @@ -39,6 +35,15 @@ object SCProofChecker { */ case Rewrite(s, t1) => if (isSameSequent(s, ref(t1))) SCValidProof(SCProof(step)) else SCInvalidProof(SCProof(step), Nil, s"The premise and the conclusion are not trivially equivalent.") + + /* + * + * ------------ + * Γ |- Γ + */ + case RewriteTrue(s) => + val truth = Sequent(Set(), Set(PredicateFormula(top, Nil))) + if (isSameSequent(s, truth)) SCValidProof(SCProof(step)) else SCInvalidProof(SCProof(step), Nil, s"The desired conclusion is not a trivial tautology") /* * * -------------- @@ -480,7 +485,7 @@ object SCProofChecker { /** * Verifies if a given pure SequentCalculus is conditionally correct, as the imported sequents are assumed. - * If the proof is not correct, the functrion will report the faulty line and a brief explanation. + * If the proof is not correct, the function will report the faulty line and a brief explanation. * * @param proof A SC proof to check * @return SCValidProof(SCProof(step)) if the proof is correct, else SCInvalidProof with the path to the incorrect proof step diff --git a/lisa-kernel/src/main/scala/lisa/kernel/proof/SequentCalculus.scala b/lisa-kernel/src/main/scala/lisa/kernel/proof/SequentCalculus.scala index 75c34935..16fa26ef 100644 --- a/lisa-kernel/src/main/scala/lisa/kernel/proof/SequentCalculus.scala +++ b/lisa-kernel/src/main/scala/lisa/kernel/proof/SequentCalculus.scala @@ -54,11 +54,20 @@ object SequentCalculus { * <pre> * Γ |- Δ * ------------ - * Γ |- Δ + * Γ |- Δ (OCBSL rewrite) * </pre> */ case class Rewrite(bot: Sequent, t1: Int) extends SCProofStep { val premises = Seq(t1) } + /** + * <pre> + * + * ------------ + * Γ |- Γ (OCBSL tautology) + * </pre> + */ + case class RewriteTrue(bot: Sequent) extends SCProofStep { val premises = Seq() } + /** * <pre> * @@ -227,7 +236,7 @@ object SequentCalculus { */ case class RightExistsOne(bot: Sequent, t1: Int, phi: Formula, x: VariableLabel) extends SCProofStep { val premises = Seq(t1) } - // Structural rules + // Structural rule /** * <pre> * Γ |- Δ @@ -292,6 +301,7 @@ object SequentCalculus { */ case class RightSubstIff(bot: Sequent, t1: Int, equals: List[(Formula, Formula)], lambdaPhi: LambdaFormulaFormula) extends SCProofStep { val premises = Seq(t1) } + // Rules for schemas /** * <pre> * Γ |- Δ @@ -308,9 +318,17 @@ object SequentCalculus { * Γ[ψ(a)/?p] |- Δ[ψ(a)/?p] * </pre> */ - case class InstPredSchema(bot: Sequent, t1: Int, insts: Map[SchematicPredicateLabel, LambdaTermFormula]) extends SCProofStep { val premises = Seq(t1) } + case class InstPredSchema(bot: Sequent, t1: Int, insts: Map[SchematicVarOrPredLabel, LambdaTermFormula]) extends SCProofStep { val premises = Seq(t1) } // Proof Organisation rules + + /** + * Encapsulate a proof into a single step. The imports of the subproof correspond to the premisces of the step. + * @param sp The encapsulated subproof. + * @param premises The indices of steps on the outside proof that are equivalent to the import of the subproof. + * @param display A boolean value indicating whether the subproof needs to be expanded when printed. Should probably go and + * be replaced by encapsulation. + */ case class SCSubproof(sp: SCProof, premises: Seq[Int] = Seq.empty, display: Boolean = true) extends SCProofStep { // premises is a list of ints similar to t1, t2... that verifies that imports of the subproof sp are justified by previous steps. val bot: Sequent = sp.conclusion diff --git a/src/main/scala/lisa/proven/Main.scala b/lisa-theories/src/main/scala/lisa/Main.scala similarity index 78% rename from src/main/scala/lisa/proven/Main.scala rename to lisa-theories/src/main/scala/lisa/Main.scala index bf01e93e..f4e9dcfd 100644 --- a/src/main/scala/lisa/proven/Main.scala +++ b/lisa-theories/src/main/scala/lisa/Main.scala @@ -1,15 +1,17 @@ -package lisa.proven +package lisa /** * The parent trait of all theory files containing mathematical development */ trait Main { - export lisa.proven.SetTheoryLibrary.{*, given} + export lisa.settheory.SetTheoryLibrary.{_, given} private val realOutput: String => Unit = println private var outString: List[String] = List() private val lineBreak = "\n" + given output: (String => Unit) = s => outString = lineBreak :: s :: outString + given finishOutput: (Throwable => Nothing) = e => { main(Array[String]()) throw e @@ -18,6 +20,8 @@ trait Main { /** * This specific implementation make sure that what is "shown" in theory files is only printed for the one we run, and not for the whole library. */ - def main(args: Array[String]): Unit = { realOutput(outString.reverse.mkString("")) } + def main(args: Array[String]): Unit = { + realOutput(outString.reverse.mkString("")) + } } diff --git a/lisa-theories/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala index 20263c2a..4a9c4892 100644 --- a/lisa-theories/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala +++ b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala @@ -13,22 +13,60 @@ private[settheory] trait SetTheoryDefinitions { def axioms: Set[(String, Formula)] = Set.empty // Predicates + /** + * The symbol for the set membership predicate. + */ final val in = ConstantPredicateLabel("set_membership", 2) + + /** + * The symbol for the subset predicate. + */ final val subset = ConstantPredicateLabel("subset_of", 2) + + /** + * The symbol for the equicardinality predicate. Needed for Tarski's axiom. + */ final val sim = ConstantPredicateLabel("same_cardinality", 2) // Equicardinality + /** + * Set Theory basic predicates + */ final val predicates = Set(in, subset, sim) - // val application - // val pick + // val choice // Functions + /** + * The symbol for the empty set constant. + */ final val emptySet = ConstantFunctionLabel("empty_set", 0) + + /** + * The symbol for the unordered pair function. + */ final val pair = ConstantFunctionLabel("unordered_pair", 2) - final val singleton = ConstantFunctionLabel("singleton", 1) + + /** + * The symbol for the powerset function. + */ final val powerSet = ConstantFunctionLabel("power_set", 1) + + /** + * The symbol for the set union function. + */ final val union = ConstantFunctionLabel("union", 1) + + /** + * The symbol for the universe function. Defined in TG set theory. + */ final val universe = ConstantFunctionLabel("universe", 1) - final val functions = Set(emptySet, pair, singleton, powerSet, union, universe) + /** + * Set Theory basic functions. + */ + final val functions = Set(emptySet, pair, powerSet, union, universe) + + /** + * The kernel theory loaded with Set Theory symbols and axioms. + */ val runningSetTheory: RunningTheory = new RunningTheory() // given RunningTheory = runningSetTheory diff --git a/src/main/scala/lisa/proven/SetTheoryLibrary.scala b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryLibrary.scala similarity index 66% rename from src/main/scala/lisa/proven/SetTheoryLibrary.scala rename to lisa-theories/src/main/scala/lisa/settheory/SetTheoryLibrary.scala index 8045393e..b127bbea 100644 --- a/src/main/scala/lisa/proven/SetTheoryLibrary.scala +++ b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryLibrary.scala @@ -1,9 +1,11 @@ -package lisa.proven +package lisa.settheory + +import lisa.utils.Library /** * Specific implementation of [[utilities.Library]] for Set Theory, with a RunningTheory that is supposed to be used by the standard library. */ -object SetTheoryLibrary extends lisa.utils.Library(lisa.settheory.AxiomaticSetTheory.runningSetTheory) { +object SetTheoryLibrary extends Library(lisa.settheory.AxiomaticSetTheory.runningSetTheory) { val AxiomaticSetTheory: lisa.settheory.AxiomaticSetTheory.type = lisa.settheory.AxiomaticSetTheory export AxiomaticSetTheory.* diff --git a/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZAxioms.scala b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZAxioms.scala index 824498f5..b60d2f55 100644 --- a/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZAxioms.scala +++ b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZAxioms.scala @@ -11,13 +11,13 @@ private[settheory] trait SetTheoryZAxioms extends SetTheoryDefinitions { private val (x, y, z) = (VariableLabel("x"), VariableLabel("y"), VariableLabel("z")) - private final val sPhi = SchematicNPredicateLabel("P", 2) + private final val sPhi = SchematicPredicateLabel("P", 2) final val emptySetAxiom: Formula = forall(x, !in(x, emptySet())) final val extensionalityAxiom: Formula = forall(x, forall(y, forall(z, in(z, x) <=> in(z, y)) <=> (x === y))) + final val subsetAxiom: Formula = forall(x, forall(y, subset(x, y) <=> forall(z, in(z, x) ==> in(z, y)))) final val pairAxiom: Formula = forall(x, forall(y, forall(z, in(z, pair(x, y)) <=> (x === z) \/ (y === z)))) final val unionAxiom: Formula = forall(x, forall(z, in(x, union(z)) <=> exists(y, in(x, y) /\ in(y, z)))) - final val subsetAxiom: Formula = forall(x, forall(y, subset(x, y) <=> forall(z, (in(z, x) ==> in(z, y))))) final val powerAxiom: Formula = forall(x, forall(y, in(x, powerSet(y)) <=> subset(x, y))) final val foundationAxiom: Formula = forall(x, !(x === emptySet()) ==> exists(y, in(y, x) /\ forall(z, in(z, x) ==> !in(z, y)))) diff --git a/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZFAxioms.scala b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZFAxioms.scala index e075fabb..bd95ad45 100644 --- a/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZFAxioms.scala +++ b/lisa-theories/src/main/scala/lisa/settheory/SetTheoryZFAxioms.scala @@ -9,7 +9,7 @@ import lisa.utils.Helpers.{_, given} private[settheory] trait SetTheoryZFAxioms extends SetTheoryZAxioms { private val (x, y, a, b) = (VariableLabel("x"), VariableLabel("y"), VariableLabel("A"), VariableLabel("B")) - private final val sPsi = SchematicNPredicateLabel("P", 3) + private final val sPsi = SchematicPredicateLabel("P", 3) final val replacementSchema: Formula = forall( a, diff --git a/lisa-tptp/src/main/scala/lisa/tptp/KernelParser.scala b/lisa-tptp/src/main/scala/lisa/tptp/KernelParser.scala index 66455c19..f75368da 100644 --- a/lisa-tptp/src/main/scala/lisa/tptp/KernelParser.scala +++ b/lisa-tptp/src/main/scala/lisa/tptp/KernelParser.scala @@ -74,7 +74,7 @@ object KernelParser { * @return the same term in LISA */ def convertTermToKernel(term: CNF.Term): K.Term = term match { - case CNF.AtomicTerm(f, args) => K.FunctionTerm(K.ConstantFunctionLabel(f, args.size), args map convertTermToKernel) + case CNF.AtomicTerm(f, args) => K.Term(K.ConstantFunctionLabel(f, args.size), args map convertTermToKernel) case CNF.Variable(name) => K.VariableTerm(K.VariableLabel(name)) case CNF.DistinctObject(name) => ??? } @@ -85,7 +85,7 @@ object KernelParser { */ def convertTermToKernel(term: FOF.Term): K.Term = term match { case FOF.AtomicTerm(f, args) => - K.FunctionTerm(K.ConstantFunctionLabel(f, args.size), args map convertTermToKernel) + K.Term(K.ConstantFunctionLabel(f, args.size), args map convertTermToKernel) case FOF.Variable(name) => K.VariableTerm(K.VariableLabel(name)) case FOF.DistinctObject(name) => ??? case FOF.NumberTerm(value) => ??? diff --git a/lisa-utils/src/main/scala/lisa/utils/KernelHelpers.scala b/lisa-utils/src/main/scala/lisa/utils/KernelHelpers.scala index 6a2c7478..56981317 100644 --- a/lisa-utils/src/main/scala/lisa/utils/KernelHelpers.scala +++ b/lisa-utils/src/main/scala/lisa/utils/KernelHelpers.scala @@ -5,7 +5,7 @@ import lisa.kernel.proof.RunningTheoryJudgement import lisa.kernel.proof.RunningTheoryJudgement.InvalidJustification import lisa.kernel.proof.SCProof import lisa.kernel.proof.SCProofCheckerJudgement.SCInvalidProof -import lisa.kernel.proof.SequentCalculus._ +import lisa.kernel.proof.SequentCalculus.* /** * A helper file that provides various syntactic sugars for LISA's FOL and proofs. Best imported through utilities.Helpers @@ -26,6 +26,8 @@ trait KernelHelpers { /* Prefix syntax */ def neg(f: Formula): Formula = ConnectorFormula(Neg, Seq(f)) + def and(list: Formula*): Formula = ConnectorFormula(And, list) + def or(list: Formula*): Formula = ConnectorFormula(Or, list) def and(l: Formula, r: Formula): Formula = ConnectorFormula(And, Seq(l, r)) def or(l: Formula, r: Formula): Formula = ConnectorFormula(Or, Seq(l, r)) def implies(l: Formula, r: Formula): Formula = ConnectorFormula(Implies, Seq(l, r)) @@ -39,7 +41,7 @@ trait KernelHelpers { extension (label: ConnectorLabel) def apply(args: Formula*): Formula = ConnectorFormula(label, args) - extension (label: FunctionLabel) def apply(args: Term*): Term = FunctionTerm(label, args) + extension (label: TermLabel) def apply(args: Term*): Term = Term(label, args) extension (label: BinderLabel) def apply(bound: VariableLabel, inner: Formula): Formula = BinderFormula(label, bound, inner) @@ -88,13 +90,11 @@ trait KernelHelpers { /* Conversions */ - given Conversion[VariableLabel, VariableTerm] = VariableTerm.apply - given Conversion[VariableTerm, VariableLabel] = _.label + given Conversion[VariableLabel, Term] = Term(_, Seq()) + given Conversion[Term, TermLabel] = _.label given Conversion[PredicateFormula, PredicateLabel] = _.label given Conversion[PredicateLabel, Formula] = _.apply() - given Conversion[FunctionTerm, FunctionLabel] = _.label - given Conversion[SchematicFunctionLabel, Term] = _.apply() - given Conversion[VariableFormulaLabel, PredicateFormula] = PredicateFormula.apply(_, Nil) + given Conversion[VariableFormulaLabel, PredicateFormula] = PredicateFormula(_, Nil) given Conversion[(Boolean, List[Int], String), Option[(List[Int], String)]] = tr => if (tr._1) None else Some(tr._2, tr._3) given Conversion[Formula, Sequent] = () |- _ @@ -148,7 +148,7 @@ trait KernelHelpers { extension [A, T1 <: A](left: T1)(using SetConverter[Formula, T1]) infix def |-[B, T2 <: B](right: T2)(using SetConverter[Formula, T2]): Sequent = Sequent(any2set(left), any2set(right)) - def instantiatePredicateSchemaInSequent(s: Sequent, m: Map[SchematicPredicateLabel, LambdaTermFormula]): Sequent = { + def instantiatePredicateSchemaInSequent(s: Sequent, m: Map[SchematicVarOrPredLabel, LambdaTermFormula]): Sequent = { s.left.map(phi => instantiatePredicateSchemas(phi, m)) |- s.right.map(phi => instantiatePredicateSchemas(phi, m)) } def instantiateFunctionSchemaInSequent(s: Sequent, m: Map[SchematicTermLabel, LambdaTermTerm]): Sequent = { @@ -156,6 +156,11 @@ trait KernelHelpers { } extension (sp: SCSubproof) { + + /** + * Explore a proof with a specific path and returns the pointed proofstep. + * @param path A path through subproofs of a proof. + */ def followPath(path: Seq[Int]): SCProofStep = path match { case Nil => sp case n :: Nil => sp.sp(n) diff --git a/lisa-utils/src/main/scala/lisa/utils/Library.scala b/lisa-utils/src/main/scala/lisa/utils/Library.scala index 1cc2db84..cfcb6846 100644 --- a/lisa-utils/src/main/scala/lisa/utils/Library.scala +++ b/lisa-utils/src/main/scala/lisa/utils/Library.scala @@ -13,7 +13,7 @@ abstract class Library(val theory: RunningTheory) { export lisa.kernel.proof.SequentCalculus.* export lisa.kernel.proof.SCProof as Proof export theory.{Justification, Theorem, Definition, Axiom, PredicateDefinition, FunctionDefinition} - export lisa.utils.Helpers.{*, given} + export lisa.utils.Helpers.{_, given} import lisa.kernel.proof.RunningTheoryJudgement as Judgement /** @@ -85,7 +85,7 @@ abstract class Library(val theory: RunningTheory) { case Judgement.ValidJustification(just) => last = Some(just) just - case wrongJudgement: Judgement.InvalidJustification[_] => wrongJudgement.showAndGet + case wrongJudgement: Judgement.InvalidJustification[?] => wrongJudgement.showAndGet } /** @@ -101,7 +101,7 @@ abstract class Library(val theory: RunningTheory) { */ def simpleDefinition(symbol: String, expression: LambdaTermTerm): Judgement[theory.FunctionDefinition] = { val LambdaTermTerm(vars, body) = expression - val out: VariableLabel = VariableLabel(freshId((vars.map(_.id) ++ body.schematicTerms.map(_.id)).toSet, "y")) + val out: VariableLabel = VariableLabel(freshId((vars.map(_.id) ++ body.schematicTermLabels.map(_.id)).toSet, "y")) val proof: Proof = simpleFunctionDefinition(expression, out) theory.functionDefinition(symbol, LambdaTermFormula(vars, out === body), out, proof, Nil) } @@ -135,7 +135,7 @@ abstract class Library(val theory: RunningTheory) { case Judgement.ValidJustification(just) => last = Some(just) just - case wrongJudgement: Judgement.InvalidJustification[_] => wrongJudgement.showAndGet + case wrongJudgement: Judgement.InvalidJustification[?] => wrongJudgement.showAndGet } definition.label } @@ -148,7 +148,7 @@ abstract class Library(val theory: RunningTheory) { case Judgement.ValidJustification(just) => last = Some(just) just - case wrongJudgement: Judgement.InvalidJustification[_] => wrongJudgement.showAndGet + case wrongJudgement: Judgement.InvalidJustification[?] => wrongJudgement.showAndGet } definition.label } @@ -194,7 +194,7 @@ abstract class Library(val theory: RunningTheory) { case Judgement.ValidJustification(just) => last = Some(just) just - case wrongJudgement: Judgement.InvalidJustification[_] => wrongJudgement.showAndGet + case wrongJudgement: Judgement.InvalidJustification[?] => wrongJudgement.showAndGet } definition.label } @@ -211,7 +211,7 @@ abstract class Library(val theory: RunningTheory) { def DEFINE(symbol: String, vars: VariableLabel*): FunSymbolDefine = FunSymbolDefine(symbol, vars) /** - * For a definition of the type f(x) := term, construct the required proof ∃!y. y = term. + * For a definition of the type f(x) := term, construct the required proof ?!y. y = term. */ private def simpleFunctionDefinition(expression: LambdaTermTerm, out: VariableLabel): Proof = { val x = out diff --git a/lisa-utils/src/main/scala/lisa/utils/Parser.scala b/lisa-utils/src/main/scala/lisa/utils/Parser.scala index 65bcb145..1cb6546d 100644 --- a/lisa-utils/src/main/scala/lisa/utils/Parser.scala +++ b/lisa-utils/src/main/scala/lisa/utils/Parser.scala @@ -98,7 +98,7 @@ object Parser { .printTerm(t) .getOrElse({ t match { - case FunctionTerm(_, args) => args.foreach(printTerm) + case Term(_, args) => args.foreach(printTerm) case VariableTerm(_) => () } throw PrintFailedException(t) @@ -286,7 +286,7 @@ object Parser { def invertTerm(t: Term): Token ~ Option[Seq[Term]] = t match { case VariableTerm(label) => SchematicToken(label.id) ~ None - case FunctionTerm(label, args) => + case Term(label, args) => val optArgs = args match { case Seq() => None case _ => Some(args) @@ -302,8 +302,8 @@ object Parser { { case ConstantToken(id) ~ maybeArgs => val args = maybeArgs.getOrElse(Seq()) - FunctionTerm(ConstantFunctionLabel(id, args.length), args) - case SchematicToken(id) ~ Some(args) => FunctionTerm(SchematicFunctionLabel(id, args.length), args) + Term(ConstantFunctionLabel(id, args.length), args) + case SchematicToken(id) ~ Some(args) => Term(SchematicFunctionLabel(id, args.length), args) case SchematicToken(id) ~ None => VariableTerm(VariableLabel(id)) case _ => throw UnreachableException }, @@ -317,12 +317,12 @@ object Parser { lazy val simpleFormula: Syntax[Formula] = predicate.up[Formula] | negated.up[Formula] | bool.up[Formula] lazy val subformula: Syntax[Formula] = simpleFormula | open.skip ~ formula ~ closed.skip - def createFunctionTerm(label: Token, args: Seq[Term]): Term = label match { - case ConstantToken(id) => FunctionTerm(ConstantFunctionLabel(id, args.size), args) + def createTerm(label: Token, args: Seq[Term]): Term = label match { + case ConstantToken(id) => Term(ConstantFunctionLabel(id, args.size), args) case SchematicToken(id) => args match { case Seq() => VariableTerm(VariableLabel(id)) - case _ => FunctionTerm(SchematicFunctionLabel(id, args.size), args) + case _ => Term(SchematicFunctionLabel(id, args.size), args) } case _ => throw UnreachableException } @@ -345,13 +345,13 @@ object Parser { case ConstantToken(id) ~ maybeArgs ~ None => val args = maybeArgs.getOrElse(Seq()) PredicateFormula(ConstantPredicateLabel(id, args.size), args) - case SchematicToken(id) ~ Some(args) ~ None => PredicateFormula(SchematicNPredicateLabel(id, args.size), args) + case SchematicToken(id) ~ Some(args) ~ None => PredicateFormula(SchematicPredicateLabel(id, args.size), args) case SchematicToken(id) ~ None ~ None => PredicateFormula(VariableFormulaLabel(id), Seq()) // equality of two function applications case fun1 ~ args1 ~ Some(fun2 ~ args2) => - PredicateFormula(FOL.equality, Seq(createFunctionTerm(fun1, args1.getOrElse(Seq())), createFunctionTerm(fun2, args2.getOrElse(Seq())))) + PredicateFormula(FOL.equality, Seq(createTerm(fun1, args1.getOrElse(Seq())), createTerm(fun2, args2.getOrElse(Seq())))) case _ => throw UnreachableException }, @@ -367,7 +367,7 @@ object Parser { case ConstantPredicateLabel(id, 0) => ConstantToken(id) ~ None case ConstantPredicateLabel(id, _) => ConstantToken(id) ~ Some(args) case VariableFormulaLabel(id) => SchematicToken(id) ~ None - case SchematicNPredicateLabel(id, _) => SchematicToken(id) ~ Some(args) + case SchematicPredicateLabel(id, _) => SchematicToken(id) ~ Some(args) } Seq(predicateApp ~ None) } diff --git a/lisa-utils/src/main/scala/lisa/utils/ProofsShrink.scala b/lisa-utils/src/main/scala/lisa/utils/ProofsShrink.scala new file mode 100644 index 00000000..c9ada08f --- /dev/null +++ b/lisa-utils/src/main/scala/lisa/utils/ProofsShrink.scala @@ -0,0 +1,296 @@ +package lisa.utils + +import lisa.kernel.fol.FOL.* +import lisa.kernel.proof.SCProof +import lisa.kernel.proof.SequentCalculus.* + +/** + * Utilities to work with sequent-calculus proofs. + * All of these methods assume that the provided proof are well-formed but not necessarily valid. + * If the provided proofs are valid, then the resulting proofs will also be valid. + */ +object ProofsShrink { + + /** + * Computes the size of a proof. Size corresponds to the number of proof steps. + * Subproofs are count as one plus the size of their body. + * @param proof the proof to analyze + * @return the size of that proof + */ + def proofSize(proof: SCProof): Int = + proof.steps.map { + case SCSubproof(sp, _, _) => 1 + proofSize(sp) + case _ => 1 + }.sum + + /** + * Computes the depth of a proof. Depth corresponds to the maximum number of nested subproofs plus one. + * @param proof the proof to analyze + * @return the depth of that proof + */ + def proofDepth(proof: SCProof): Int = + proof.steps.map { + case SCSubproof(sp, _, _) => 1 + proofDepth(sp) + case _ => 1 + }.max + + /** + * Updates the indices of the premises in a proof step according to some provided mapping. For example: + * <pre> + * mapPremises(Rewrite(sequent, 1), i => i + 1) == Rewrite(sequent, 2) + * </pre> + * @param step the proof step to update + * @param mapping the provided mapping + * @return a new step with the updated indices + */ + def mapPremises(step: SCProofStep, mapping: Int => Int): SCProofStep = step match { + case s: Rewrite => s.copy(t1 = mapping(s.t1)) + case s: RewriteTrue => s + case s: Hypothesis => s + case s: Cut => s.copy(t1 = mapping(s.t1), t2 = mapping(s.t2)) + case s: LeftAnd => s.copy(t1 = mapping(s.t1)) + case s: LeftOr => s.copy(t = s.t.map(mapping)) + case s: LeftImplies => s.copy(t1 = mapping(s.t1), t2 = mapping(s.t2)) + case s: LeftIff => s.copy(t1 = mapping(s.t1)) + case s: LeftNot => s.copy(t1 = mapping(s.t1)) + case s: LeftForall => s.copy(t1 = mapping(s.t1)) + case s: LeftExists => s.copy(t1 = mapping(s.t1)) + case s: LeftExistsOne => s.copy(t1 = mapping(s.t1)) + case s: RightAnd => s.copy(t = s.t.map(mapping)) + case s: RightOr => s.copy(t1 = mapping(s.t1)) + case s: RightImplies => s.copy(t1 = mapping(s.t1)) + case s: RightIff => s.copy(t1 = mapping(s.t1), t2 = mapping(s.t2)) + case s: RightNot => s.copy(t1 = mapping(s.t1)) + case s: RightForall => s.copy(t1 = mapping(s.t1)) + case s: RightExists => s.copy(t1 = mapping(s.t1)) + case s: RightExistsOne => s.copy(t1 = mapping(s.t1)) + case s: Weakening => s.copy(t1 = mapping(s.t1)) + case s: LeftRefl => s.copy(t1 = mapping(s.t1)) + case s: RightRefl => s + case s: LeftSubstEq => s.copy(t1 = mapping(s.t1)) + case s: RightSubstEq => s.copy(t1 = mapping(s.t1)) + case s: LeftSubstIff => s.copy(t1 = mapping(s.t1)) + case s: RightSubstIff => s.copy(t1 = mapping(s.t1)) + case s: SCSubproof => s.copy(premises = s.premises.map(mapping)) + case s: InstFunSchema => s.copy(t1 = mapping(s.t1)) + case s: InstPredSchema => s.copy(t1 = mapping(s.t1)) + } + + /** + * Flattens a proof recursively; in other words it removes all occurrences of [[SCSubproof]]. + * Because subproofs imports can be rewritten, [[Rewrite]] steps may be inserted where that is necessary. + * The order of proof steps is preserved, indices of premises are adapted to reflect the new sequence. + * @param proof the proof to be flattened + * @return the flattened proof + */ + def flattenProof(proof: SCProof): SCProof = { + def flattenProofRecursive(steps: IndexedSeq[SCProofStep], topPremises: IndexedSeq[(Int, Sequent)], offset: Int): IndexedSeq[SCProofStep] = { + val (finalAcc, _) = steps.foldLeft((IndexedSeq.empty[SCProofStep], IndexedSeq.empty[(Int, Sequent)])) { case ((acc, localToGlobal), step) => + def resolve(i: Int): (Int, Sequent) = if (i >= 0) localToGlobal(i) else topPremises(-i - 1) + val newAcc = step match { + case SCSubproof(subProof, subPremises, _) => + val (rewrittenPremises, rewrittenSeq) = subPremises.zipWithIndex.flatMap { case (i, j) => + val (k, sequent) = resolve(i) + val imported = subProof.imports(j) + if (sequent != imported) { + Some((Rewrite(imported, k), -(j - 1) -> imported)) + } else { + None + } + }.unzip + val rewrittenMap = rewrittenSeq.zipWithIndex.map { case ((i, sequent), j) => i -> (offset + acc.size + j, sequent) }.toMap + val childTopPremises = subPremises.map(i => rewrittenMap.getOrElse(i, resolve(i))).toIndexedSeq + acc ++ rewrittenPremises ++ flattenProofRecursive(subProof.steps, childTopPremises, offset + acc.size + rewrittenPremises.size) + case _ => + acc :+ mapPremises(step, i => resolve(i)._1) + } + (newAcc, localToGlobal :+ (offset + newAcc.size - 1, step.bot)) + } + finalAcc + } + SCProof(flattenProofRecursive(proof.steps, proof.imports.zipWithIndex.map { case (imported, i) => (-i - 1, imported) }, 0), proof.imports) + } + + /** + * Eliminates all steps that are not indirectly referenced by the conclusion (last step) of the proof. + * This procedure is applied recursively on all subproofs. The elimination of unused top-level imports can be configured. + * The order of proof steps is preserved, indices of premises are adapted to reflect the new sequence. + * @param proof the proof to be simplified + * @param eliminateTopLevelDeadImports whether the unused top-level imports should be eliminated as well + * @return the proof with dead steps eliminated + */ + def deadStepsElimination(proof: SCProof, eliminateTopLevelDeadImports: Boolean = true): SCProof = { + def deadStepsEliminationInternal(proof: SCProof, eliminateDeadImports: Boolean): (SCProof, IndexedSeq[Int]) = { + // We process the leaves first, otherwise we could miss dead branches (subproofs that more imports than necessary) + val processedSteps = proof.steps.map { + case SCSubproof(sp, premises, display) => + val (newSubProof, newImportsIndices) = deadStepsEliminationInternal(sp, true) + SCSubproof(newSubProof, newImportsIndices.map(premises), display) + case other => other + } + val graph = processedSteps.map(_.premises) + val nodes = graph.indices + def bfs(visited: Set[Int], toVisit: Set[Int]): Set[Int] = { + if (toVisit.nonEmpty) { + val next = toVisit.flatMap(graph).diff(visited) + bfs(visited ++ next, next.filter(_ >= 0)) + } else { + visited + } + } + val conclusionNode = nodes.last // Must exist by assumption + val visited = bfs(Set(conclusionNode), Set(conclusionNode)) + val newNodes = nodes.filter(visited.contains) + val newImports = proof.imports.indices.map(i => -(i + 1)).filter(i => !eliminateDeadImports || visited.contains(i)) + val newImportsIndices = newImports.map(i => -(i + 1)) + val oldToNewStep = newNodes.zipWithIndex.toMap + val oldToNewImport = newImports.zipWithIndex.map { case (i, j) => (i, -(j + 1)) }.toMap + val map = oldToNewStep ++ oldToNewImport + val newSteps = newNodes.map(processedSteps).map(step => mapPremises(step, map)) + val newProof = SCProof(newSteps, newImportsIndices.map(proof.imports)) + (newProof, newImportsIndices) + } + val (newProof, _) = deadStepsEliminationInternal(proof, eliminateTopLevelDeadImports) + newProof + } + + /** + * Removes proof steps that are identified to be redundant. The registered simplifications are the following: + * <ul> + * <li>Double/fruitless rewrites/weakening</li> + * <li>Fruitless instantiations</li> + * <li>Useless cut</li> + * </ul> + * This procedure may need to be called several times; it is guaranteed that a fixed point will eventually be reached. + * Imports will not change, dead branches will be preserved (but can still be simplified). + * @param proof the proof to be simplified + * @return the simplified proof + */ + def simplifyProof(proof: SCProof): SCProof = { + def isSequentSubset(subset: Sequent, superset: Sequent): Boolean = + isSubset(subset.left, superset.left) && isSubset(subset.right, superset.right) + def schematicPredicatesLabels(sequent: Sequent): Set[SchematicVarOrPredLabel] = + (sequent.left ++ sequent.right).flatMap(_.schematicPredicateLabels) + def schematicTermLabels(sequent: Sequent): Set[SchematicTermLabel] = + (sequent.left ++ sequent.right).flatMap(_.schematicTermLabels) + def freeSchematicTerms(sequent: Sequent): Set[SchematicTermLabel] = + (sequent.left ++ sequent.right).flatMap(_.freeSchematicTermLabels) + val (newSteps, _) = proof.steps.zipWithIndex.foldLeft((IndexedSeq.empty[SCProofStep], IndexedSeq.empty[Int])) { case ((acc, map), (oldStep, i)) => + def resolveLocal(j: Int): Int = { + require(j < i) + if (j >= 0) map(j) else j + } + def getSequentLocal(j: Int): Sequent = { + require(j < i) + if (j >= 0) acc(map(j)).bot else proof.getSequent(j) + } + object LocalStep { + def unapply(j: Int): Option[SCProofStep] = { + require(j < i) + if (j >= 0) Some(acc(map(j))) else None + } + } + val step = mapPremises(oldStep, resolveLocal) + val either: Either[SCProofStep, Int] = step match { + // General unary steps + case _ if step.premises.sizeIs == 1 && getSequentLocal(step.premises.head) == step.bot => + Right(step.premises.head) + case _ if !step.isInstanceOf[Rewrite] && step.premises.sizeIs == 1 && isSameSequent(getSequentLocal(step.premises.head), step.bot) => + Left(Rewrite(step.bot, step.premises.head)) + case _ + if !step.isInstanceOf[Rewrite] && !step.isInstanceOf[Weakening] + && step.premises.sizeIs == 1 && isSequentSubset(getSequentLocal(step.premises.head), step.bot) => + Left(Weakening(step.bot, step.premises.head)) + // Recursive + case SCSubproof(sp, premises, display) => + Left(SCSubproof(simplifyProof(sp), premises, display)) + // Double rewrite + case Rewrite(bot1, LocalStep(Rewrite(bot2, t2))) if isSameSequent(bot1, bot2) => + Left(Rewrite(bot1, t2)) + // Double weakening + case Weakening(bot1, LocalStep(Weakening(bot2, t2))) if isSequentSubset(bot2, bot1) => + Left(Weakening(bot1, t2)) + // Rewrite and weakening + case Weakening(bot1, LocalStep(Rewrite(_, t2))) if isSequentSubset(getSequentLocal(t2), bot1) => + Left(Weakening(bot1, t2)) + // Weakening and rewrite + case Rewrite(bot1, LocalStep(Weakening(_, t2))) if isSequentSubset(getSequentLocal(t2), bot1) => + Left(Weakening(bot1, t2)) + // Hypothesis and rewrite + case Rewrite(bot1, LocalStep(Hypothesis(_, phi))) if bot1.left.contains(phi) && bot1.right.contains(phi) => + Left(Hypothesis(bot1, phi)) + // Hypothesis and weakening + case Weakening(bot1, LocalStep(Hypothesis(_, phi))) if bot1.left.contains(phi) && bot1.right.contains(phi) => + Left(Hypothesis(bot1, phi)) + // Useless cut + case Cut(bot, _, t2, phi) if bot.left.contains(phi) => + Left(Weakening(bot, t2)) + case Cut(bot, t1, _, phi) if bot.right.contains(phi) => + Left(Weakening(bot, t1)) + // Fruitless instantiation + case InstPredSchema(bot, t1, _) if isSameSequent(bot, getSequentLocal(t1)) => + Left(Rewrite(bot, t1)) + case InstFunSchema(bot, t1, _) if isSameSequent(bot, getSequentLocal(t1)) => + Left(Rewrite(bot, t1)) + // Instantiation simplification + case InstPredSchema(bot, t1, insts) if !insts.keySet.subsetOf(schematicPredicatesLabels(getSequentLocal(t1))) => + val newInsts = insts -- insts.keySet.diff(schematicPredicatesLabels(getSequentLocal(t1))) + Left(InstPredSchema(bot, t1, newInsts)) + case InstFunSchema(bot, t1, insts) if !insts.keySet.subsetOf(schematicTermLabels(getSequentLocal(t1))) => + val newInsts = insts -- insts.keySet.diff(schematicTermLabels(getSequentLocal(t1))) + Left(InstFunSchema(bot, t1, newInsts)) + case other => Left(other) + } + either match { + case Left(newStep) => (acc :+ newStep, map :+ acc.size) + case Right(index) => (acc :+ oldStep, map :+ index) + } + } + SCProof(newSteps, proof.imports) + } + + /** + * Attempts to factor the premises such that the first occurrence of a proven sequent is used. + * This procedure is greedy. + * Unused proof steps will not be removed. Use [[deadStepsElimination]] for that. + * @param proof the proof to be factored + * @return the factored proof + */ + def factorProof(proof: SCProof): SCProof = { + val (initialMap, initialCache) = proof.imports.zipWithIndex.foldLeft((Map.empty[Int, Int], Map.empty[Sequent, Int])) { case ((map, cache), (sequent, i)) => + val originalIndex = -(i + 1) + cache.get(sequent) match { + case Some(existingIndex) => (map + (originalIndex -> existingIndex), cache) + case None => (map + (originalIndex -> originalIndex), cache + (sequent -> originalIndex)) + } + } + val (newSteps, _, _) = proof.steps.zipWithIndex.foldLeft((IndexedSeq.empty[SCProofStep], initialMap, initialCache)) { case ((acc, map, cache), (step, i)) => + val sequent = step.bot + val mappedStep = mapPremises(step, map) match { + case SCSubproof(sp, premises, display) => + SCSubproof(factorProof(sp), premises, display) + case other => other + } + val (newMap, newCache) = cache.get(sequent) match { + case Some(existingIndex) => (map + (i -> existingIndex), cache) + case None => (map + (i -> i), cache + (sequent -> i)) + } + (acc :+ mappedStep, newMap, newCache) + } + SCProof(newSteps, proof.imports) + } + + /** + * Optimizes a proof by applying all the available reduction rules until a fixed point is reached. + * @param proof the proof to be optimized + * @return the optimized proof + */ + def optimizeProofIteratively(proof: SCProof): SCProof = { + def optimizeFixedPoint(proof: SCProof): SCProof = { + val optimized = deadStepsElimination(factorProof(simplifyProof(proof))) + if (optimized == proof) optimized else optimizeFixedPoint(optimized) + } + optimizeFixedPoint(flattenProof(proof)) + } + +} diff --git a/lisa-utils/src/main/scala/lisa/utils/TheoriesHelpers.scala b/lisa-utils/src/main/scala/lisa/utils/TheoriesHelpers.scala index 384aff2a..504c9077 100644 --- a/lisa-utils/src/main/scala/lisa/utils/TheoriesHelpers.scala +++ b/lisa-utils/src/main/scala/lisa/utils/TheoriesHelpers.scala @@ -76,10 +76,12 @@ trait TheoriesHelpers extends KernelHelpers { case d: RunningTheory#Definition => d match { case pd: RunningTheory#PredicateDefinition => - output(s" Definition of predicate symbol ${pd.label.id} := ${Printer.prettyFormula(pd.label(pd.expression.vars.map(VariableTerm)*) <=> pd.expression.body)}\n") // (label, args, phi) + output( + s" Definition of predicate symbol ${pd.label.id} := ${Printer.prettyFormula(pd.label(pd.expression.vars.map(VariableTerm.apply)*) <=> pd.expression.body)}\n" + ) // (label, args, phi) case fd: RunningTheory#FunctionDefinition => - output(s" Definition of function symbol ${Printer.prettyTerm(fd.label(fd.expression.vars.map(VariableTerm)*))} := the ${fd.out.id} such that ${Printer - .prettyFormula((fd.out === fd.label(fd.expression.vars.map(VariableTerm)*)) <=> fd.expression.body)})\n") + output(s" Definition of function symbol ${Printer.prettyTerm(fd.label(fd.expression.vars.map(VariableTerm.apply)*))} := the ${fd.out.id} such that ${Printer + .prettyFormula((fd.out === fd.label(fd.expression.vars.map(VariableTerm.apply)*)) <=> fd.expression.body)})\n") } } just diff --git a/lisa-utils/src/test/scala/lisa/kernel/FolTests.scala b/lisa-utils/src/test/scala/lisa/kernel/FolTests.scala index 19c8e670..c3e944f3 100644 --- a/lisa-utils/src/test/scala/lisa/kernel/FolTests.scala +++ b/lisa-utils/src/test/scala/lisa/kernel/FolTests.scala @@ -28,7 +28,7 @@ class FolTests extends AnyFunSuite { val r = gen.between(0, 3) if (r == 0) { val name = "" + ('a' to 'e')(gen.between(0, 5)) - FunctionTerm(ConstantFunctionLabel(name, 0), List()) + Term(ConstantFunctionLabel(name, 0), List()) } else { val name = "" + ('v' to 'z')(gen.between(0, 5)) VariableTerm(VariableLabel(name)) @@ -38,16 +38,16 @@ class FolTests extends AnyFunSuite { val name = "" + ('f' to 'j')(gen.between(0, 5)) if (r == 0) { val name = "" + ('a' to 'e')(gen.between(0, 5)) - FunctionTerm(ConstantFunctionLabel(name, 0), List()) + Term(ConstantFunctionLabel(name, 0), List()) } else if (r == 1) { val name = "" + ('v' to 'z')(gen.between(0, 5)) VariableTerm(VariableLabel(name)) } - if (r <= 3) FunctionTerm(ConstantFunctionLabel(name, 1), Seq(termGenerator(maxDepth - 1, gen))) - else if (r <= 5) FunctionTerm(ConstantFunctionLabel(name, 2), Seq(termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen))) - else if (r == 6) FunctionTerm(ConstantFunctionLabel(name, 3), Seq(termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen))) + if (r <= 3) Term(ConstantFunctionLabel(name, 1), Seq(termGenerator(maxDepth - 1, gen))) + else if (r <= 5) Term(ConstantFunctionLabel(name, 2), Seq(termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen))) + else if (r == 6) Term(ConstantFunctionLabel(name, 3), Seq(termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen))) else - FunctionTerm( + Term( ConstantFunctionLabel(name, 4), Seq(termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen), termGenerator(maxDepth - 1, gen)) ) diff --git a/lisa-utils/src/test/scala/lisa/kernel/InvalidProofPathTests.scala b/lisa-utils/src/test/scala/lisa/kernel/InvalidProofPathTests.scala index 257f5193..1bf566e3 100644 --- a/lisa-utils/src/test/scala/lisa/kernel/InvalidProofPathTests.scala +++ b/lisa-utils/src/test/scala/lisa/kernel/InvalidProofPathTests.scala @@ -2,7 +2,7 @@ package lisa.kernel import lisa.kernel.proof.SCProofCheckerJudgement.SCInvalidProof import lisa.kernel.proof.SequentCalculus.* -import lisa.kernel.proof._ +import lisa.kernel.proof.* import lisa.test.ProofCheckerSuite import lisa.utils.Helpers.{_, given} diff --git a/lisa-utils/src/test/scala/lisa/test/TestTheoryLibrary.scala b/lisa-utils/src/test/scala/lisa/test/TestTheoryLibrary.scala index 24315915..3e06e42b 100644 --- a/lisa-utils/src/test/scala/lisa/test/TestTheoryLibrary.scala +++ b/lisa-utils/src/test/scala/lisa/test/TestTheoryLibrary.scala @@ -1,5 +1,7 @@ package lisa.test -object TestTheoryLibrary extends lisa.utils.Library(TestTheory.runningTestTheory) { +import lisa.utils.Library + +object TestTheoryLibrary extends Library(TestTheory.runningTestTheory) { export TestTheory.* } diff --git a/lisa-utils/src/test/scala/lisa/utils/ParserTest.scala b/lisa-utils/src/test/scala/lisa/utils/ParserTest.scala index 73eabd63..8930b571 100644 --- a/lisa-utils/src/test/scala/lisa/utils/ParserTest.scala +++ b/lisa-utils/src/test/scala/lisa/utils/ParserTest.scala @@ -7,7 +7,7 @@ import org.scalatest.funsuite.AnyFunSuite class ParserTest extends AnyFunSuite with TestUtils { test("constant") { - assert(Parser.parseTerm("x") == FunctionTerm(cx, Seq())) + assert(Parser.parseTerm("x") == Term(cx, Seq())) } test("variable") { @@ -15,29 +15,29 @@ class ParserTest extends AnyFunSuite with TestUtils { } test("constant function application") { - assert(Parser.parseTerm("f()") == FunctionTerm(f0, Seq())) - assert(Parser.parseTerm("f(x)") == FunctionTerm(f1, Seq(cx))) - assert(Parser.parseTerm("f(x, y)") == FunctionTerm(f2, Seq(cx, cy))) - assert(Parser.parseTerm("f(x, y, z)") == FunctionTerm(f3, Seq(cx, cy, cz))) + assert(Parser.parseTerm("f()") == Term(f0, Seq())) + assert(Parser.parseTerm("f(x)") == Term(f1, Seq(cx))) + assert(Parser.parseTerm("f(x, y)") == Term(f2, Seq(cx, cy))) + assert(Parser.parseTerm("f(x, y, z)") == Term(f3, Seq(cx, cy, cz))) - assert(Parser.parseTerm("f(?x)") == FunctionTerm(f1, Seq(x))) - assert(Parser.parseTerm("f(?x, ?y)") == FunctionTerm(f2, Seq(x, y))) - assert(Parser.parseTerm("f(?x, ?y, ?z)") == FunctionTerm(f3, Seq(x, y, z))) + assert(Parser.parseTerm("f(?x)") == Term(f1, Seq(x))) + assert(Parser.parseTerm("f(?x, ?y)") == Term(f2, Seq(x, y))) + assert(Parser.parseTerm("f(?x, ?y, ?z)") == Term(f3, Seq(x, y, z))) } test("schematic function application") { // Parser.parseTerm("?f()") -- schematic functions of 0 arguments do not exist, those are variables - assert(Parser.parseTerm("?f(x)") == FunctionTerm(sf1, Seq(cx))) - assert(Parser.parseTerm("?f(x, y)") == FunctionTerm(sf2, Seq(cx, cy))) - assert(Parser.parseTerm("?f(x, y, z)") == FunctionTerm(sf3, Seq(cx, cy, cz))) + assert(Parser.parseTerm("?f(x)") == Term(sf1, Seq(cx))) + assert(Parser.parseTerm("?f(x, y)") == Term(sf2, Seq(cx, cy))) + assert(Parser.parseTerm("?f(x, y, z)") == Term(sf3, Seq(cx, cy, cz))) - assert(Parser.parseTerm("?f(?x)") == FunctionTerm(sf1, Seq(x))) - assert(Parser.parseTerm("?f(?x, ?y)") == FunctionTerm(sf2, Seq(x, y))) - assert(Parser.parseTerm("?f(?x, ?y, ?z)") == FunctionTerm(sf3, Seq(x, y, z))) + assert(Parser.parseTerm("?f(?x)") == Term(sf1, Seq(x))) + assert(Parser.parseTerm("?f(?x, ?y)") == Term(sf2, Seq(x, y))) + assert(Parser.parseTerm("?f(?x, ?y, ?z)") == Term(sf3, Seq(x, y, z))) } test("nested function application") { - assert(Parser.parseTerm("?f(?f(?x), ?y)") == FunctionTerm(sf2, Seq(FunctionTerm(sf1, Seq(x)), y))) + assert(Parser.parseTerm("?f(?f(?x), ?y)") == Term(sf2, Seq(Term(sf1, Seq(x)), y))) } test("0-ary predicate") { diff --git a/lisa-utils/src/test/scala/lisa/utils/PrinterTest.scala b/lisa-utils/src/test/scala/lisa/utils/PrinterTest.scala index 3a45954c..e8c2a63c 100644 --- a/lisa-utils/src/test/scala/lisa/utils/PrinterTest.scala +++ b/lisa-utils/src/test/scala/lisa/utils/PrinterTest.scala @@ -40,7 +40,7 @@ class PrinterTest extends AnyFunSuite with TestUtils { } test("constant") { - assert(Parser.printTerm(FunctionTerm(cx, Seq())) == "x") + assert(Parser.printTerm(Term(cx, Seq())) == "x") } test("variable") { @@ -48,27 +48,27 @@ class PrinterTest extends AnyFunSuite with TestUtils { } test("constant function application") { - assert(Parser.printTerm(FunctionTerm(f1, Seq(cx))) == "f(x)") - assert(Parser.printTerm(FunctionTerm(f2, Seq(cx, cy))) == "f(x, y)") - assert(Parser.printTerm(FunctionTerm(f3, Seq(cx, cy, cz))) == "f(x, y, z)") + assert(Parser.printTerm(Term(f1, Seq(cx))) == "f(x)") + assert(Parser.printTerm(Term(f2, Seq(cx, cy))) == "f(x, y)") + assert(Parser.printTerm(Term(f3, Seq(cx, cy, cz))) == "f(x, y, z)") - assert(Parser.printTerm(FunctionTerm(f1, Seq(x))) == "f(?x)") - assert(Parser.printTerm(FunctionTerm(f2, Seq(x, y))) == "f(?x, ?y)") - assert(Parser.printTerm(FunctionTerm(f3, Seq(x, y, z))) == "f(?x, ?y, ?z)") + assert(Parser.printTerm(Term(f1, Seq(x))) == "f(?x)") + assert(Parser.printTerm(Term(f2, Seq(x, y))) == "f(?x, ?y)") + assert(Parser.printTerm(Term(f3, Seq(x, y, z))) == "f(?x, ?y, ?z)") } test("schematic function application") { - assert(Parser.printTerm(FunctionTerm(sf1, Seq(cx))) == "?f(x)") - assert(Parser.printTerm(FunctionTerm(sf2, Seq(cx, cy))) == "?f(x, y)") - assert(Parser.printTerm(FunctionTerm(sf3, Seq(cx, cy, cz))) == "?f(x, y, z)") + assert(Parser.printTerm(Term(sf1, Seq(cx))) == "?f(x)") + assert(Parser.printTerm(Term(sf2, Seq(cx, cy))) == "?f(x, y)") + assert(Parser.printTerm(Term(sf3, Seq(cx, cy, cz))) == "?f(x, y, z)") - assert(Parser.printTerm(FunctionTerm(sf1, Seq(x))) == "?f(?x)") - assert(Parser.printTerm(FunctionTerm(sf2, Seq(x, y))) == "?f(?x, ?y)") - assert(Parser.printTerm(FunctionTerm(sf3, Seq(x, y, z))) == "?f(?x, ?y, ?z)") + assert(Parser.printTerm(Term(sf1, Seq(x))) == "?f(?x)") + assert(Parser.printTerm(Term(sf2, Seq(x, y))) == "?f(?x, ?y)") + assert(Parser.printTerm(Term(sf3, Seq(x, y, z))) == "?f(?x, ?y, ?z)") } test("nested function application") { - assert(Parser.printTerm(FunctionTerm(sf2, Seq(FunctionTerm(sf1, Seq(x)), y))) == "?f(?f(?x), ?y)") + assert(Parser.printTerm(Term(sf2, Seq(Term(sf1, Seq(x)), y))) == "?f(?f(?x), ?y)") } test("0-ary predicate") { diff --git a/lisa-utils/src/test/scala/lisa/utils/SCProofStepFinderTests.scala b/lisa-utils/src/test/scala/lisa/utils/SCProofStepFinderTests.scala new file mode 100644 index 00000000..bc5b1da0 --- /dev/null +++ b/lisa-utils/src/test/scala/lisa/utils/SCProofStepFinderTests.scala @@ -0,0 +1,190 @@ +package lisa.utils + +import org.scalatest.funsuite.AnyFunSuite + +import scala.language.adhocExtensions +/* +import utilities.Helpers.* +import utilities.Printer +import lisa.kernel.fol.FOL.* +import lisa.kernel.proof.* +import lisa.kernel.proof.SequentCalculus.* +import lisa.settheory.AxiomaticSetTheory.* +import lisa.kernel.proof.SCProofCheckerJudgement.* +import org.scalatest.funsuite.AnyFunSuite +import me.cassayre.florian.masterproject.util.SCProofBuilder.{_{_, given} + +import util.chaining.* +import scala.util.{Failure, Success, Try} + */ +class SCProofStepFinderTests extends AnyFunSuite { + /* + test("proof steps reconstruction") { + // These tests ensure that all the kernel proof steps can be generated + // To achieve that, we design proofs that require these steps to be used at some point + + val (x, y, z) = (VariableLabel("x"), VariableLabel("y"), VariableLabel("z")) + val theory = new RunningTheory() + val (la, lb, lc) = (ConstantPredicateLabel("a", 0), ConstantPredicateLabel("b", 0), ConstantPredicateLabel("c", 0)) + Seq(la, lb, lc).foreach(theory.addSymbol) + val (a, b, c) = (PredicateFormula(la, Seq.empty), PredicateFormula(lb, Seq.empty), PredicateFormula(lc, Seq.empty)) + val (ls, lt) = (ConstantFunctionLabel("s", 0), ConstantFunctionLabel("t", 0)) + Seq(ls, lt).foreach(theory.addSymbol) + val (s, t) = (FunctionTerm(ls, Seq.empty), FunctionTerm(lt, Seq.empty)) + + implicit class VariableLabelEq(l: VariableLabel) { // Helper due to conflicts with scalatest's `===` + def ====(r: Term): Formula = PredicateFormula(equality, Seq(VariableTerm(l), r)) + def ====(r: VariableLabel): Formula = PredicateFormula(equality, Seq(VariableTerm(l), VariableTerm(r))) + } + implicit class FunctionLabelEq(l: FunctionLabel) { + def ====(r: Term): Formula = PredicateFormula(equality, Seq(FunctionTerm(l, Seq.empty), r)) + def ====(r: FunctionLabel): Formula = PredicateFormula(equality, Seq(FunctionTerm(l, Seq.empty), FunctionTerm(r, Seq.empty))) + } + implicit class TermEq(l: Term) { + def ====(r: Term): Formula = PredicateFormula(equality, Seq(l, r)) + def ====(r: FunctionLabel): Formula = PredicateFormula(equality, Seq(l, FunctionTerm(r, Seq.empty))) + } + + val proofs: Seq[((String, SCProofBuilder), PartialFunction[SCProofStep, Unit])] = Seq( + // (1.1) + "Hypothesis" -> SCProofBuilder( + a |- a, + ) -> { case _: Hypothesis => () }, + "Cut" -> SCProofBuilder( + a |- a, + Seq(a, b) |- a by 0, + c |- c, + c |- Seq(b, c) by 2, + Seq(a, c) |- Seq(a, c) by (1, 3), + ) -> { case _: Cut => () }, + "LeftAnd" -> SCProofBuilder( + a |- a, + (a /\ b) |- a by 0, + ) -> { case _: LeftAnd => () }, + "RightAnd" -> SCProofBuilder( + a |- a, + b |- b, + Seq(a, b) |- (a /\ b) by (0, 1), + ) -> { case _: RightAnd => () }, + "LeftOr" -> SCProofBuilder( + a |- a, + b |- b, + Seq(a, b, a \/ b) |- Seq(a, b) by (0, 1) + ) -> { case _: LeftOr => () }, + "RightOr" -> SCProofBuilder( + a |- a, + a |- (a \/ b) by 0, + ) -> { case _: RightOr => () }, + "LeftImplies" -> SCProofBuilder( + a |- a, + b |- b, + Seq(a, a ==> b) |- b by (0, 1), + ) -> { case _: LeftImplies => () }, + "RightImplies" -> SCProofBuilder( + a |- a, + a |- Seq(a, a ==> a) by 0, + ) -> { case _: RightImplies => () }, + "LeftIff" -> SCProofBuilder( + (a ==> b) |- (a ==> b), + (a <=> b) |- (a ==> b) by 0, + ) -> { case _: LeftIff => () }, + "RightIff" -> SCProofBuilder( + (a ==> b) |- (a ==> b), + (b ==> a) |- (b ==> a), + Seq(a ==> b, b ==> a) |- (a <=> b) by (0, 1), + ) -> { case _: RightIff => () }, + "LeftNot" -> SCProofBuilder( + a |- a, + a |- Seq(a, b) by 0, + Seq(a, !b) |- a by 1, + ) -> { case _: LeftNot => () }, + "RightNot" -> SCProofBuilder( + a |- a, + Seq(a, b) |- a by 0, + a |- Seq(a, !b) by 1, + ) -> { case _: RightNot => () }, + "LeftForall" -> SCProofBuilder( + (y ==== z) |- (y ==== z), + forall(x, x ==== z) |- (y ==== z) by 0, + ) -> { case _: LeftForall => () }, + "RightForall" -> SCProofBuilder( + (y ==== z) |- (y ==== z), + (y ==== z) |- forall(x, y ==== z) by 0, + ) -> { case _: RightForall => () }, + "LeftExists" -> SCProofBuilder( + (y ==== z) |- (y ==== z), + exists(x, y ==== z) |- (y ==== z) by 0, + ) -> { case _: LeftExists => () }, + "RightExists" -> SCProofBuilder( + (y ==== z) |- (y ==== z), + (y ==== z) |- exists(x, x ==== z) by 0, + ) -> { case _: RightExists => () }, + "LeftExistsOne" -> SCProofBuilder( + exists(y, forall(x, (x ==== y) <=> a)).pipe(f => f |- f), + existsOne(x, a) |- exists(y, forall(x, (x ==== y) <=> a)) by 0, + ) -> { case _: LeftExistsOne => () }, + "RightExistsOne" -> SCProofBuilder( + exists(y, forall(x, (x ==== y) <=> a)).pipe(f => f |- f), + exists(y, forall(x, (x ==== y) <=> a)) |- existsOne(x, a) by 0, + ) -> { case _: RightExistsOne => () }, + "(Left)Weakening" -> SCProofBuilder( + a |- a, + Seq(a, b) |- a by 0, + ) -> { case _: Weakening => () }, + "(Right)Weakening" -> SCProofBuilder( + a |- a, + a |- Seq(a, b) by 0, + ) -> { case _: Weakening => () }, + // (1.2) + "LeftSubstEq" -> SCProofBuilder( + (s ==== emptySet) |- (s ==== emptySet), + Seq(s ==== t, t ==== emptySet) |- (s ==== emptySet) by 0, + ) -> { case _: LeftSubstEq => () }, + "RightSubstEq" -> SCProofBuilder( + (s ==== emptySet) |- (s ==== emptySet), + Seq(s ==== emptySet, s ==== t) |- Seq(s ==== emptySet, t ==== emptySet) by 0, + ) -> { case _: RightSubstEq => () }, + "LeftSubstIff" -> SCProofBuilder( + a |- a, + Seq(b, a <=> b) |- a by 0, + ) -> { case _: LeftSubstIff => () }, + "RightSubstIff" -> SCProofBuilder( + a |- a, + Seq(a, a <=> b) |- b by 0, + ) -> { case _: RightSubstIff => () }, + "LeftRefl" -> SCProofBuilder( + a |- a, + Seq(a, b) |- a by 0, + Seq(a, b, emptySet ==== emptySet) |- a by 1, + Seq(a, b) |- a by 2, + ) -> { case _: LeftRefl => () }, + "RightRefl" -> SCProofBuilder( + () |- (emptySet ==== emptySet), + ) -> { case _: RightRefl => () }, + ) + + proofs.foreach { case ((testname, proofBuilder), partialFunction) => + val filter: SCProofStep => Boolean = partialFunction.lift(_).nonEmpty + Try(proofBuilder.build) match { + case Success(proof) => + SCProofChecker.checkSCProof(proof) match { + case SCValidProof(_) => // OK + println(testname) + println(Printer.prettySCProof(proof)) + println() + // Dirty, but only way to test that + val proofWithoutLast = proof.copy(steps = proof.steps.init) + proofBuilder.steps.last match { + case SCImplicitProofStep(conclusion, premises, imports) => + val view = SCProofStepFinder.findPossibleProofSteps(proofWithoutLast, conclusion, premises) + assert(view.exists(filter), s"The proof step finder was not able to find the step '$testname'") + case SCExplicitProofStep(step) => assert(false) + } + case invalid: SCInvalidProof => throw new AssertionError(s"The reconstructed proof for '$testname' is incorrect:\n${Printer.prettySCProof(invalid)}") + } + case Failure(exception) => throw new AssertionError(s"Couldn't reconstruct the proof for '$testname'", exception) // Couldn't reconstruct this proof + } + } + } + */ +} diff --git a/lisa-utils/src/test/scala/lisa/utils/TestUtils.scala b/lisa-utils/src/test/scala/lisa/utils/TestUtils.scala index 93976742..3d7ee4df 100644 --- a/lisa-utils/src/test/scala/lisa/utils/TestUtils.scala +++ b/lisa-utils/src/test/scala/lisa/utils/TestUtils.scala @@ -11,11 +11,11 @@ trait TestUtils { val (cx, cy, cz) = (ConstantFunctionLabel("x", 0), ConstantFunctionLabel("y", 0), ConstantFunctionLabel("z", 0)) val (f0, f1, f2, f3) = (ConstantFunctionLabel("f", 0), ConstantFunctionLabel("f", 1), ConstantFunctionLabel("f", 2), ConstantFunctionLabel("f", 3)) val (sf1, sf2, sf3) = (SchematicFunctionLabel("f", 1), SchematicFunctionLabel("f", 2), SchematicFunctionLabel("f", 3)) - val (sPhi1, sPhi2) = (SchematicNPredicateLabel("phi", 1), SchematicNPredicateLabel("phi", 2)) + val (sPhi1, sPhi2) = (SchematicPredicateLabel("phi", 1), SchematicPredicateLabel("phi", 2)) given Conversion[PredicateLabel, PredicateFormula] = PredicateFormula(_, Seq.empty) - given Conversion[ConstantFunctionLabel, FunctionTerm] = FunctionTerm(_, Seq()) + given Conversion[ConstantFunctionLabel, Term] = Term(_, Seq()) - given Conversion[VariableLabel, VariableTerm] = VariableTerm.apply + given Conversion[VariableLabel, Term] = VariableTerm.apply } diff --git a/src/main/scala/lisa/automation/Proof2.scala b/src/main/scala/lisa/automation/Proof2.scala new file mode 100644 index 00000000..f29aa465 --- /dev/null +++ b/src/main/scala/lisa/automation/Proof2.scala @@ -0,0 +1,51 @@ +package lisa.automation + +/** + * The proof package. + */ +object Proof2 { + export lisa.front.proof.Proof.* + export lisa.automation.front.predef.Predef.* + val introHypo: RuleHypothesis.type = RuleHypothesis + val introLAnd: RuleIntroductionLeftAnd.type = RuleIntroductionLeftAnd + val introRAnd: RuleIntroductionRightAnd.type = RuleIntroductionRightAnd + val introLOr: RuleIntroductionLeftOr.type = RuleIntroductionLeftOr + val introROr: RuleIntroductionRightOr.type = RuleIntroductionRightOr + val introLImp: RuleIntroductionLeftImplies.type = RuleIntroductionLeftImplies + val introRImp: RuleIntroductionRightImplies.type = RuleIntroductionRightImplies + val introLIff: RuleIntroductionLeftIff.type = RuleIntroductionLeftIff + val introRIff: RuleIntroductionRightIff.type = RuleIntroductionRightIff + val introLNot: RuleIntroductionLeftNot.type = RuleIntroductionLeftNot + val introRNot: RuleIntroductionRightNot.type = RuleIntroductionRightNot + val introRRefl: RuleIntroductionRightRefl.type = RuleIntroductionRightRefl + val introLForall: RuleIntroductionLeftForall.type = RuleIntroductionLeftForall + val introRForall: RuleIntroductionRightForall.type = RuleIntroductionRightForall + val introLExists: RuleIntroductionLeftExists.type = RuleIntroductionLeftExists + val introRExists: RuleIntroductionRightExists.type = RuleIntroductionRightExists + val introLSubstEq: RuleIntroductionLeftSubstEq.type = RuleIntroductionLeftSubstEq + val introRSubstEq: RuleIntroductionRightSubstEq.type = RuleIntroductionRightSubstEq + val introLSubstIff: RuleIntroductionLeftSubstIff.type = RuleIntroductionLeftSubstIff + val introRSubstIff: RuleIntroductionRightSubstIff.type = RuleIntroductionRightSubstIff + // RuleIntroductionLeftExistsOne & RuleIntroductionRightExistsOne + val introRForallS: RuleIntroductionRightForallSchema.type = RuleIntroductionRightForallSchema + val introLExistsS: RuleIntroductionLeftExistsSchema.type = RuleIntroductionLeftExistsSchema + + val elimCut: RuleCut.type = RuleCut + val elimLRefl: RuleEliminationLeftRefl.type = RuleEliminationLeftRefl + val elimRForallS: RuleEliminationRightForallSchema.type = RuleEliminationRightForallSchema + val elimLSubstIff: RuleEliminationLeftSubstIff.type = RuleEliminationLeftSubstIff + val elimRSubstIff: RuleEliminationRightSubstIff.type = RuleEliminationRightSubstIff + val elimLSubstEq: RuleEliminationLeftSubstEq.type = RuleEliminationLeftSubstEq + val elimRSubstEq: RuleEliminationRightSubstEq.type = RuleEliminationRightSubstEq + val elimRNot: RuleEliminationRightNot.type = RuleEliminationRightNot + + val instFunS: TacticInstantiateFunctionSchema.type = TacticInstantiateFunctionSchema + + val solvePropFast: TacticSolverNative.type = TacticSolverNative + val solveProp: TacticPropositionalSolver.type = TacticPropositionalSolver + val rewrite: TacticalRewrite.type = TacticalRewrite + val weaken: TacticalWeaken.type = TacticalWeaken + + val justificationInst: TacticInstantiateApplyJustification.type = TacticInstantiateApplyJustification + +} diff --git a/src/main/scala/lisa/automation/front/predef/Predef.scala b/src/main/scala/lisa/automation/front/predef/Predef.scala new file mode 100644 index 00000000..b275add8 --- /dev/null +++ b/src/main/scala/lisa/automation/front/predef/Predef.scala @@ -0,0 +1,3 @@ +package lisa.automation.front.predef + +object Predef extends PredefRulesDefinitions with PredefTacticsDefinitions with PredefCombinedDefinitions {} diff --git a/src/main/scala/lisa/automation/front/predef/PredefCombinedDefinitions.scala b/src/main/scala/lisa/automation/front/predef/PredefCombinedDefinitions.scala new file mode 100644 index 00000000..8c47760c --- /dev/null +++ b/src/main/scala/lisa/automation/front/predef/PredefCombinedDefinitions.scala @@ -0,0 +1,25 @@ +package lisa.automation.front.predef + +import lisa.front.proof.Proof.* + +trait PredefCombinedDefinitions extends PredefRulesDefinitions { + + val TacticPropositionalSolver: Tactic = TacticRepeat( + TacticFallback( + Seq( + RuleHypothesis, + RuleIntroductionLeftAnd, + RuleIntroductionRightAnd, + RuleIntroductionLeftOr, + RuleIntroductionRightOr, + RuleIntroductionLeftImplies, + RuleIntroductionRightImplies, + RuleIntroductionLeftIff, + RuleIntroductionRightIff, + RuleIntroductionLeftNot, + RuleIntroductionRightNot + ) + ) + ) + +} diff --git a/src/main/scala/lisa/automation/front/predef/PredefRulesDefinitions.scala b/src/main/scala/lisa/automation/front/predef/PredefRulesDefinitions.scala new file mode 100644 index 00000000..49fe2ec4 --- /dev/null +++ b/src/main/scala/lisa/automation/front/predef/PredefRulesDefinitions.scala @@ -0,0 +1,459 @@ +package lisa.automation.front.predef + +import lisa.front.fol.FOL.* +import lisa.front.proof.Proof.* +import lisa.front.proof.state.RuleDefinitions +import lisa.kernel.fol.FOL.LambdaFormulaFormula +import lisa.kernel.fol.FOL.LambdaTermFormula +import lisa.kernel.fol.FOL.LambdaTermTerm +import lisa.kernel.proof.SequentCalculus.* + +trait PredefRulesDefinitions { + + private case class SideBuilder(formulas: IndexedSeq[Formula], partial: Boolean) { + def |-(other: SideBuilder): PartialSequent = PartialSequent(formulas, other.formulas, partial, other.partial) + } + private def *(formulas: Formula*): SideBuilder = SideBuilder(formulas.toIndexedSeq, true) + private def $(formulas: Formula*): SideBuilder = SideBuilder(formulas.toIndexedSeq, false) + // This *must* be a def (see https://github.com/lampepfl/dotty/issues/14667) + private def ** : SideBuilder = SideBuilder(IndexedSeq.empty, true) + private def $$ : SideBuilder = SideBuilder(IndexedSeq.empty, false) + // private def &(hypotheses: PartialSequent*): IndexedSeq[PartialSequent] = hypotheses.toIndexedSeq + private given Conversion[PartialSequent, IndexedSeq[PartialSequent]] = IndexedSeq(_) + private val __ : IndexedSeq[PartialSequent] = IndexedSeq.empty + + import Notations.* + + case object RuleHypothesis + extends RuleBase( + __, + *(a) |- *(a), + (bot, ctx) => IndexedSeq(Hypothesis(bot, ctx(a))) + ) + + // Introduction + + case object RuleIntroductionLeftAnd + extends RuleBase( + *(a, b) |- **, + *(a /\ b) |- **, + (bot, ctx) => IndexedSeq(LeftAnd(bot, -1, ctx(a), ctx(b))) + ) + + case object RuleIntroductionRightAnd + extends RuleBase( + (** |- *(a)) :+ (** |- *(b)), + ** |- *(a /\ b), + (bot, ctx) => IndexedSeq(RightAnd(bot, Seq(-1, -2), Seq(ctx(a), ctx(b)))) + ) + + case object RuleIntroductionLeftOr + extends RuleBase( + (*(a) |- **) :+ (*(b) |- **), + *(a \/ b) |- **, + (bot, ctx) => IndexedSeq(LeftOr(bot, Seq(-1, -2), Seq(ctx(a), ctx(b)))) + ) + + case object RuleIntroductionRightOr + extends RuleBase( + ** |- *(a, b), + ** |- *(a \/ b), + (bot, ctx) => IndexedSeq(RightOr(bot, -1, ctx(a), ctx(b))) + ) + + case object RuleIntroductionLeftImplies + extends RuleBase( + (** |- *(a)) :+ (*(b) |- **), + *(a ==> b) |- **, + (bot, ctx) => IndexedSeq(LeftImplies(bot, -1, -2, ctx(a), ctx(b))) + ) + + case object RuleIntroductionRightImplies + extends RuleBase( + *(a) |- *(b), + ** |- *(a ==> b), + (bot, ctx) => IndexedSeq(RightImplies(bot, -1, ctx(a), ctx(b))) + ) + + case object RuleIntroductionLeftIff + extends RuleBase( + *(a ==> b, b ==> a) |- **, + *(a <=> b) |- **, + (bot, ctx) => IndexedSeq(LeftIff(bot, -1, ctx(a), ctx(b))) + ) + + case object RuleIntroductionRightIff + extends RuleBase( + (** |- *(a ==> b)) :+ (** |- *(b ==> a)), + ** |- *(a <=> b), + (bot, ctx) => IndexedSeq(RightIff(bot, -1, -2, ctx(a), ctx(b))) + ) + + case object RuleIntroductionLeftNot + extends RuleBase( + ** |- *(a), + *(!a) |- **, + (bot, ctx) => IndexedSeq(LeftNot(bot, -1, ctx(a))) + ) + + case object RuleIntroductionRightNot + extends RuleBase( + *(a) |- **, + ** |- *(!a), + (bot, ctx) => IndexedSeq(RightNot(bot, -1, ctx(a))) + ) + + case object RuleIntroductionRightRefl + extends RuleBase( + __, + ** |- *(s === s), + (bot, ctx) => IndexedSeq(RightRefl(bot, ctx(s) === ctx(s))) + ) + + // Substitution + + case object RuleIntroductionLeftForall + extends RuleBase( + *(p(t)) |- **, + *(forall(x, p(x))) |- **, + (bot, ctx) => { + val lambda = ctx(p) + val px = lambda(VariableTerm(ctx(x))) + IndexedSeq( + LeftForall(bot, -1, px, ctx.variables(x), ctx(t)) + ) + } + ) + + case object RuleIntroductionRightForall + extends RuleBase( + ** |- *(p(x)), + ** |- *(forall(x, p(x))), + { + case (bot, ctx) if !(bot.left ++ bot.right).flatMap(_.freeVariables).contains(ctx(x)) => + // TODO x not already free in sequent; ideally this should be handled automatically in `Rule`, not here + val lambda = ctx(p) + val px = lambda(VariableTerm(ctx(x))) + IndexedSeq( + RightForall(bot, -1, px, ctx.variables(x)) + ) + } + ) + + case object RuleIntroductionLeftExists + extends RuleBase( + *(p(x)) |- **, + *(exists(x, p(x))) |- **, + { + case (bot, ctx) if !(bot.left ++ bot.right).flatMap(_.freeVariables).contains(ctx(x)) => + val lambda = ctx(p) + val px = lambda(VariableTerm(ctx(x))) + IndexedSeq( + LeftExists(bot, -1, px, ctx.variables(x)) + ) + } + ) + + case object RuleIntroductionRightExists + extends RuleBase( + ** |- *(p(t)), + ** |- *(exists(x, p(x))), + (bot, ctx) => { + val lambda = ctx(p) + val px = lambda(VariableTerm(ctx(x))) + IndexedSeq( + RightExists(bot, -1, px, ctx.variables(x), ctx(t)) + ) + } + ) + + case object RuleIntroductionLeftExistsOne + extends RuleBase( + *(exists(y, exists(x, (x === y) <=> p(x)))) |- **, + *(existsOne(x, p(x))) |- **, + (bot, ctx) => { + // TODO y not free in p + val lambda = ctx(p) + val px = lambda(VariableTerm(ctx(x))) + ??? + } + ) + + // RuleIntroductionLeftExistsOne + + case object RuleIntroductionLeftSubstEq + extends RuleBase( + *(p(s)) |- **, + *(s === t, p(t)) |- **, + (bot, ctx) => + IndexedSeq( + LeftSubstEq(bot, -1, List(ctx(s) -> ctx(t)), ctx(p)) + ) + ) + + case object RuleIntroductionRightSubstEq + extends RuleBase( + ** |- *(p(s)), + *(s === t) |- *(p(t)), + (bot, ctx) => + IndexedSeq( + RightSubstEq(bot, -1, List(ctx(s) -> ctx(t)), ctx(p)) + ) + ) + + case object RuleIntroductionLeftSubstIff + extends RuleBase( + *(f(a)) |- **, + *(a <=> b, f(b)) |- **, + (bot, ctx) => + IndexedSeq( + LeftSubstIff(bot, -1, List(ctx(a) -> ctx(b)), ctx(f)) + ) + ) + + case object RuleIntroductionRightSubstIff + extends RuleBase( + ** |- *(f(a)), + *(a <=> b) |- *(f(b)), + (bot, ctx) => + IndexedSeq( + RightSubstIff(bot, -1, List(ctx(a) -> ctx(b)), ctx(f)) + ) + ) + + // + + case object RuleSubstituteRightIff + extends RuleBase( + (** |- *(f(a))) :+ ($$ |- $(a <=> b)), + ** |- *(f(b)), + (bot, ctx) => + IndexedSeq( + RightSubstIff(bot +< (ctx(a) <=> ctx(b)), -1, List(ctx(a) -> ctx(b)), ctx(f)), + Cut(bot, -2, 0, ctx(a) <=> ctx(b)) + ) + ) + + case object RuleSubstituteLeftIff + extends RuleBase( + (*(f(a)) |- **) :+ ($$ |- $(a <=> b)), + *(f(b)) |- **, + (bot, ctx) => + IndexedSeq( + LeftSubstIff(bot +< (ctx(a) <=> ctx(b)), -1, List(ctx(a) -> ctx(b)), ctx(f)), + Cut(bot, -2, 0, ctx(a) <=> ctx(b)) + ) + ) + + // Elimination + + case object RuleCut + extends RuleBase( + (** |- *(a)) :+ (*(a) |- **), + ** |- **, + (bot, ctx) => IndexedSeq(Cut(bot, -1, -2, ctx(a))) + ) + + case object RuleEliminationLeftRefl + extends RuleBase( + *(s === s) |- **, + ** |- **, + (bot, ctx) => IndexedSeq(LeftRefl(bot, -1, ctx(s) === ctx(s))) + ) + + case object RuleEliminationLeftAnd + extends RuleBase( + *(a /\ b) |- **, + *(a, b) |- **, + (bot, ctx) => + IndexedSeq( + Hypothesis(bot +> ctx(a), ctx(a)), + Hypothesis(bot +> ctx(b), ctx(b)), + RightAnd(bot +> (ctx(a) /\ ctx(b)), Seq(0, 1), Seq(ctx(a), ctx(b))), + Cut(bot, 2, -1, ctx(a) /\ ctx(b)) + ) + ) + + case object RuleEliminationRightOr + extends RuleBase( + ** |- *(a \/ b), + ** |- *(a, b), + (bot, ctx) => + IndexedSeq( + Hypothesis(bot +< ctx(a), ctx(a)), + Hypothesis(bot +< ctx(b), ctx(b)), + LeftOr(bot +< (ctx(a) \/ ctx(b)), Seq(0, 1), Seq(ctx(a), ctx(b))), + Cut(bot, -1, 2, ctx(a) \/ ctx(b)) + ) + ) + + case object RuleEliminationRightForallSchema + extends RuleBase( + ** |- *(forall(x, p(x))), + ** |- *(p(t)), + (bot, ctx) => { + val xlab: VariableLabel = ctx.variables(x) + val vx = VariableTerm(xlab) + val tv = ctx(t) + val (px, pt) = (ctx(p)(vx), ctx(p)(tv)) + val fpx = forall(ctx.variables(x), px) + val cBot = bot -> pt + IndexedSeq( + Hypothesis(bot +< pt, pt), + LeftForall(bot +< fpx, 0, px, xlab, tv), + Cut(bot, -1, 1, fpx) + ) + } + ) + + case object RuleModusPonens + extends RuleBase( + (** |- *(a)) :+ ($(a) |- $(b)), + ** |- *(b), + (bot, ctx) => + IndexedSeq( + Cut(bot, -1, -2, ctx(a)) + ) + ) + + case object RuleEliminationRightNot + extends RuleBase( + ** |- *(!a), + *(a) |- **, + (bot, ctx) => + IndexedSeq( + Hypothesis(ctx(a) |- ctx(a), ctx(a)), + LeftNot((ctx(a), !ctx(a)) |- (), 0, ctx(a)), + Cut(bot, -1, 1, !ctx(a)) + ) + ) + + case object RuleIntroductionRightForallSchema + extends RuleBase( + ** |- *(p(t)), + ** |- *(forall(x, p(x))), + (bot, ctx) => { + ctx(t) match { + case Term(pl: SchematicTermLabel[?], Seq()) => + val xlab = ctx.variables(x) + val vx = VariableTerm(xlab) + val px = ctx(p)(xlab) + val cBot = bot -> forall(xlab, px) + val pBot = cBot +> px + require(!(bot.left ++ bot.right).flatMap(_.freeVariables).contains(ctx(x))) + require(!(pBot.left ++ pBot.right).flatMap(_.freeSchematicTermLabels).contains(pl)) + IndexedSeq( + InstFunSchema(pBot, -1, Map(toKernel(pl) -> LambdaFunction(vx))), + RightForall(bot, 0, px, xlab) + ) + case e => throw new MatchError(e) + } + } + ) + + case object RuleIntroductionLeftExistsSchema + extends RuleBase( + *(p(t)) |- **, + *(exists(x, p(x))) |- **, + (bot, ctx) => { + ctx(t) match { + case Term(pl: SchematicTermLabel[?], Seq()) => + val xlab = ctx.variables(x) + val vx = VariableTerm(xlab) + val px = ctx(p)(vx) + val cBot = bot -< exists(xlab, px) + val pBot = cBot +< px + require(!(bot.left ++ bot.right).flatMap(_.freeVariables).contains(ctx(x))) + require(!(pBot.left ++ pBot.right).flatMap(_.freeSchematicTermLabels).contains(pl)) + IndexedSeq( + InstFunSchema(pBot, -1, Map(toKernel(pl) -> LambdaFunction(vx))), + LeftExists(bot, 0, px, xlab) + ) + case e => throw new MatchError(e) + } + } + ) + + case object RuleEliminationLeftSubstEq + extends RuleBase( + (*(p(s)) |- **) +: (** |- *(s === t)), + *(p(t)) |- **, + (bot, ctx) => + IndexedSeq( + LeftSubstEq(bot +< (ctx(s) === ctx(t)), -1, List(ctx(s) -> ctx(t)), ctx(p)), + Cut(bot, -2, 0, ctx(s) === ctx(t)) + ) + ) + + case object RuleEliminationRightSubstEq + extends RuleBase( + (** |- *(p(s))) +: (** |- *(s === t)), + ** |- *(p(t)), + (bot, ctx) => + IndexedSeq( + RightSubstEq(bot +< (ctx(s) === ctx(t)), -1, List(ctx(s) -> ctx(t)), ctx(p)), + Cut(bot, -2, 0, ctx(s) === ctx(t)) + ) + ) + + case object RuleEliminationLeftSubstIff + extends RuleBase( + (*(f(a)) |- **) +: (** |- *(a <=> b)), + *(f(b)) |- **, + (bot, ctx) => + IndexedSeq( + LeftSubstIff(bot +< (ctx(a) <=> ctx(b)), -1, List(ctx(a) -> ctx(b)), ctx(f)), + Cut(bot, -2, 0, ctx(a) <=> ctx(b)) + ) + ) + + case object RuleEliminationRightSubstIff + extends RuleBase( + (** |- *(f(a))) +: (** |- *(a <=> b)), + ** |- *(f(b)), + (bot, ctx) => + IndexedSeq( + RightSubstIff(bot +< (ctx(a) <=> ctx(b)), -1, List(ctx(a) -> ctx(b)), ctx(f)), + Cut(bot, -2, 0, ctx(a) <=> ctx(b)) + ) + ) + + // TODO more rules + + // Move this + /*case class GeneralTacticRightIff(parameters: RuleTacticParameters) extends GeneralTactic { + import Notations.* + + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructGeneral)] = { + parameters.formulas.collect { case (IndexedSeq(), IndexedSeq(i)) if proofGoal.right.indices.contains(i) => + val formula = proofGoal.right(i) + val ea = instantiatePredicateSchemas(parameters.predicates(c), Map(e -> (parameters.predicates(a), Seq.empty))) + val eb = instantiatePredicateSchemas(parameters.predicates(c), Map(e -> (parameters.predicates(b), Seq.empty))) + if(formula == eb) { // TODO isSame + val bot: lisa.kernel.proof.SequentCalculus.Sequent = proofGoal + Some( + IndexedSeq( + proofGoal.copy(right = (proofGoal.right.take(i) :+ ea) ++ proofGoal.right.drop(i + 1)), + () |- parameters.predicates(a) <=> parameters.predicates(b), + ), + () => + IndexedSeq( + RightSubstIff( + bot +< (parameters.predicates(a) <=> parameters.predicates(b)), + -1, + parameters.predicates(a), + parameters.predicates(b), + parameters.predicates(c), // f(e) + e, + ), + Cut(bot, -2, 0, parameters.predicates(a) <=> parameters.predicates(b)) + ) + ) + } else { + None + } + }.flatten + } + }*/ + +} diff --git a/src/main/scala/lisa/automation/front/predef/PredefTacticsDefinitions.scala b/src/main/scala/lisa/automation/front/predef/PredefTacticsDefinitions.scala new file mode 100644 index 00000000..22a9b758 --- /dev/null +++ b/src/main/scala/lisa/automation/front/predef/PredefTacticsDefinitions.scala @@ -0,0 +1,173 @@ +package lisa.automation.front.predef + +import lisa.automation.kernel.SimplePropositionalSolver +import lisa.front.fol.FOL.* +import lisa.front.proof.Proof.* +import lisa.front.proof.state.ProofEnvironmentDefinitions +import lisa.front.proof.unification.UnificationUtils +import lisa.kernel.proof.SCProof +import lisa.kernel.proof.SequentCalculus as KSC + +trait PredefTacticsDefinitions { + + case object TacticSolverNative extends TacticGoalFunctional { + import Notations.* + + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] = { + val steps = SimplePropositionalSolver.solveSequent(proofGoal).steps + Some((IndexedSeq.empty, () => steps)) + } + } + case class TacticRewritePartial(left: Map[Int, Formula] = Map.empty, right: Map[Int, Formula] = Map.empty) extends TacticGoalFunctional { + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] = { + if (left.keySet.forall(proofGoal.left.indices.contains) && right.keySet.forall(proofGoal.right.indices.contains)) { + val rewritten = Sequent( + proofGoal.left.indices.map(i => left.getOrElse(i, proofGoal.left(i))), + proofGoal.right.indices.map(i => right.getOrElse(i, proofGoal.right(i))) + ) + if (isSameSequent(proofGoal, rewritten)) { + Some((IndexedSeq(rewritten), () => IndexedSeq(KSC.Rewrite(rewritten, -1)))) + } else { + None + } + } else { + None + } + } + } + + case class TacticRewriteSequent(rewritten: Sequent) extends TacticGoalFunctional { + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] = { + if (isSameSequent(proofGoal, rewritten)) { + Some((IndexedSeq(rewritten), () => IndexedSeq(KSC.Rewrite(proofGoal, -1)))) + } else { + None + } + } + } + + object TacticalRewrite { + def apply(left: Map[Int, Formula] = Map.empty, right: Map[Int, Formula] = Map.empty): TacticRewritePartial = + TacticRewritePartial(left, right) + def apply(rewritten: Sequent): TacticRewriteSequent = TacticRewriteSequent(rewritten) + } + + case class TacticWeakenPartial(left: Set[Int] = Set.empty, right: Set[Int] = Set.empty) extends TacticGoalFunctional { + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] = { + if (left.forall(proofGoal.left.indices.contains) && right.forall(proofGoal.right.indices.contains)) { + val weaker = Sequent( + proofGoal.left.zipWithIndex.filter { case (_, i) => !left.contains(i) }.map { case (f, _) => f }, + proofGoal.right.zipWithIndex.filter { case (_, i) => !right.contains(i) }.map { case (f, _) => f } + ) + Some((IndexedSeq(weaker), () => IndexedSeq(KSC.Weakening(proofGoal, -1)))) + } else { + None + } + } + } + + case class TacticWeakenSequent(weaker: Sequent) extends TacticGoalFunctional { + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] = { + // This can be made powerful with a matching algorithm + ??? + } + } + + object TacticalWeaken { + def apply(left: Set[Int] = Set.empty, right: Set[Int] = Set.empty): TacticWeakenPartial = + TacticWeakenPartial(left, right) + // def apply(weaker: Sequent): TacticWeakenSequent = TacticWeakenSequent(weaker) + } + + case class TacticInstantiateFunctionSchema(sequent: Sequent, assigned: AssignedFunction) extends TacticGoalFunctional { + override def apply(proofGoal: Sequent): Option[(IndexedSeq[Sequent], ReconstructSteps)] = { + val map = Seq(assigned) + val instantiated = Sequent( + sequent.left.map(formula => instantiateFormulaSchemas(formula, functions = map)), + sequent.right.map(formula => instantiateFormulaSchemas(formula, functions = map)) + ) + if (isSameSequent(proofGoal, instantiated)) { + Some( + ( + IndexedSeq(sequent), + () => IndexedSeq(KSC.InstFunSchema(proofGoal, -1, Map(toKernel(assigned.schema) -> assigned.lambda))) + ) + ) + } else { + None + } + } + } + + extension (theorem: Theorem) { + def apply(assigned: AssignedFunction): Theorem = { + val map: Seq[AssignedFunction] = Seq(assigned) + val replaced = Sequent( + theorem.sequent.left.map(formula => instantiateFormulaSchemas(formula, functions = map)), + theorem.sequent.right.map(formula => instantiateFormulaSchemas(formula, functions = map)) + ) + val scProof = SCProof( + IndexedSeq( + KSC.InstFunSchema(replaced, -1, Map(toKernel(assigned.schema) -> assigned.lambda)) + ), + IndexedSeq(sequentToKernel(theorem.sequent)) + ) + theorem.environment.mkTheorem(replaced, scProof, IndexedSeq(theorem)) + } + def apply(assigned: AssignedPredicate): Theorem = { + val map: Seq[AssignedPredicate] = Seq(assigned) + val replaced = Sequent( + theorem.sequent.left.map(formula => instantiateFormulaSchemas(formula, predicates = map)), + theorem.sequent.right.map(formula => instantiateFormulaSchemas(formula, predicates = map)) + ) + val scProof = SCProof( + IndexedSeq( + KSC.InstPredSchema(replaced, -1, Map(toKernel(assigned.schema) -> assigned.lambda)) + ), + IndexedSeq(sequentToKernel(theorem.sequent)) + ) + theorem.environment.mkTheorem(replaced, scProof, IndexedSeq(theorem)) + } + + def rewrite(rewritten: Sequent): Theorem = + TacticRewriteSequent(theorem.sequent) + .apply(rewritten) + .map { case (_, reconstruct) => + val scProof = SCProof(reconstruct(), IndexedSeq(sequentToKernel(theorem.sequent))) + theorem.environment.mkTheorem(rewritten, scProof, IndexedSeq(theorem)) + } + .get + } + + case class TacticInstantiateApplyJustification(justified: Justified) extends TacticGoalFunctionalPruning { + def apply(proofGoal: Sequent): Option[(IndexedSeq[Either[Sequent, Justified]], ReconstructSteps)] = { + val patterns = IndexedSeq(PartialSequent(justified.sequent.left, justified.sequent.right)) + val values = IndexedSeq(proofGoal) + matchIndices(Map.empty, patterns, values).flatMap { selector => + // TODO we should instantiate to temporary schemas first otherwise we risk clashing names + unifyAndResolve(patterns, values, patterns, UnificationContext(), selector).map { case (IndexedSeq(resolved), ctx) => + val withFunctions = instantiateSequentSchemas(justified.sequent, ctx.assignedFunctions, Seq.empty, Seq.empty) + val withFunctionsAndPredicates = instantiateSequentSchemas(withFunctions, Seq.empty, ctx.assignedPredicates, Seq.empty) + ( + IndexedSeq(Right(justified)), + () => + IndexedSeq( + KSC.InstFunSchema( + sequentToKernel(withFunctions), + -1, + ctx.assignedFunctions.map(assigned => toKernel(assigned.schema) -> toKernel(assigned.lambda)).toMap + ), + KSC.InstPredSchema( + sequentToKernel(withFunctionsAndPredicates), + 0, + ctx.assignedPredicates.map(assigned => toKernel(assigned.schema) -> toKernel(assigned.lambda)).toMap + ), + KSC.Weakening(sequentToKernel(proofGoal), 1) + ) + ) + } + }.headOption + } + } + +} diff --git a/src/main/scala/lisa/proven/tactics/Destructors.scala b/src/main/scala/lisa/automation/kernel/Destructors.scala similarity index 91% rename from src/main/scala/lisa/proven/tactics/Destructors.scala rename to src/main/scala/lisa/automation/kernel/Destructors.scala index f7b978dd..08736ca7 100644 --- a/src/main/scala/lisa/proven/tactics/Destructors.scala +++ b/src/main/scala/lisa/automation/kernel/Destructors.scala @@ -1,10 +1,9 @@ -package lisa.proven.tactics +package lisa.automation.kernel +import lisa.automation.kernel.ProofTactics.hypothesis import lisa.kernel.fol.FOL.* import lisa.kernel.proof.SCProof import lisa.kernel.proof.SequentCalculus.* -import lisa.kernel.proof.SequentCalculus.* -import lisa.proven.tactics.ProofTactics.hypothesis import lisa.utils.Helpers.* object Destructors { @@ -17,12 +16,14 @@ object Destructors { val p3 = Cut(p.conclusion -> mat.get +> a +> b, p.length - 1, p.length + 2, a \/ b) p withNewSteps IndexedSeq(p0, p1, p2, p3) } + def destructRightAnd(p: SCProof, f: Formula, g: Formula): SCProof = { val p0 = hypothesis(f) // n val p1 = LeftAnd(emptySeq +< (f /\ g) +> f, p.length, f, g) // n+1 val p2 = Cut(p.conclusion -> (f /\ g) -> (g /\ f) +> f, p.length - 1, p.length + 1, f /\ g) p withNewSteps IndexedSeq(p0, p1, p2) } + def destructRightImplies(p: SCProof, f: Formula, g: Formula): SCProof = { // |- f=>g val p0 = hypothesis(f) // f |- f val p1 = hypothesis(g) // g |- g diff --git a/src/main/scala/lisa/proven/tactics/ProofTactics.scala b/src/main/scala/lisa/automation/kernel/ProofTactics.scala similarity index 97% rename from src/main/scala/lisa/proven/tactics/ProofTactics.scala rename to src/main/scala/lisa/automation/kernel/ProofTactics.scala index c75768cb..d90b8208 100644 --- a/src/main/scala/lisa/proven/tactics/ProofTactics.scala +++ b/src/main/scala/lisa/automation/kernel/ProofTactics.scala @@ -1,4 +1,4 @@ -package lisa.proven.tactics +package lisa.automation.kernel import lisa.kernel.fol.FOL.* import lisa.kernel.proof.SCProof @@ -6,8 +6,6 @@ import lisa.kernel.proof.SequentCalculus.* import lisa.utils.Helpers.{_, given} import lisa.utils.Printer.* -import scala.collection.immutable.Set - /** * SCProof tactics are a set of strategies that help the user write proofs in a more expressive way * by focusing on the final goal rather on the individual steps. @@ -30,6 +28,7 @@ object ProofTactics { case _ => throw new Exception("not a forall") } } + def instantiateForall(p: SCProof, phi: Formula, t: Term*): SCProof = { // given a proof with a formula quantified with \forall on the right, extend the proof to the same formula with something instantiated instead. t.foldLeft((p, phi)) { case ((p, f), t1) => ( @@ -41,19 +40,25 @@ object ProofTactics { ) }._1 } + def instantiateForall(p: SCProof, t: Term): SCProof = instantiateForall(p, p.conclusion.right.head, t) // if a single formula on the right + def instantiateForall(p: SCProof, t: Term*): SCProof = { // given a proof with a formula quantified with \forall on the right, extend the proof to the same formula with something instantiated instead. t.foldLeft(p)((p1, t1) => instantiateForall(p1, t1)) } + def generalizeToForall(p: SCProof, phi: Formula, x: VariableLabel): SCProof = { require(p.conclusion.right.contains(phi)) val p1 = RightForall(p.conclusion -> phi +> forall(x, phi), p.length - 1, phi, x) p appended p1 } + def generalizeToForall(p: SCProof, x: VariableLabel): SCProof = generalizeToForall(p, p.conclusion.right.head, x) + def generalizeToForall(p: SCProof, x: VariableLabel*): SCProof = { // given a proof with a formula on the right, extend the proof to the same formula with variables universally quantified. x.foldRight(p)((x1, p1) => generalizeToForall(p1, x1)) } + def byEquiv(f: Formula, f1: Formula)(pEq: SCProofStep, pr1: SCProofStep): SCProof = { require(pEq.bot.right.contains(f)) require(pr1.bot.right.contains(f1)) @@ -89,6 +94,7 @@ object ProofTactics { })*/ SCProof(v) } + // p1 is a proof of psi given phi, p2 is a proof of psi given !phi def byCase(phi: Formula)(pa: SCProofStep, pb: SCProofStep): SCProof = { val nphi = !phi @@ -99,6 +105,7 @@ object ProofTactics { val p3 = Cut(pa.bot -< leftAphi.get ++ (pb.bot -< leftBnphi.get), 2, 1, nphi) SCProof(IndexedSeq(pa, pb, p2, p3)) } + // pa is a proof of phi, pb is a proof of phi ==> ??? // |- phi ==> psi, phi===>gamma |- phi // ------------------------------------- @@ -141,8 +148,9 @@ object ProofTactics { } case _ => (c, false) } + def detectSubstitutionT(x: VariableLabel, t: Term, s: Term, c: Option[Term] = None): (Option[Term], Boolean) = (t, s) match { - case (y: VariableTerm, z: Term) => { + case (y @ Term(l: VariableLabel, _), z: Term) => { if (isSame(y.label, x)) { if (c.isDefined) { (c, isSame(c.get, z)) @@ -151,7 +159,7 @@ object ProofTactics { } } else (c, isSame(y, z)) } - case (FunctionTerm(la1, args1), FunctionTerm(la2, args2)) if isSame(la1, la2) => { + case (Term(la1, args1), Term(la2, args2)) if isSame(la1, la2) => { args1 .zip(args2) .foldLeft[(Option[Term], Boolean)](c, true)((r1, a) => { diff --git a/src/main/scala/lisa/proven/tactics/SimplePropositionalSolver.scala b/src/main/scala/lisa/automation/kernel/SimplePropositionalSolver.scala similarity index 98% rename from src/main/scala/lisa/proven/tactics/SimplePropositionalSolver.scala rename to src/main/scala/lisa/automation/kernel/SimplePropositionalSolver.scala index 65ce706f..ab43254c 100644 --- a/src/main/scala/lisa/proven/tactics/SimplePropositionalSolver.scala +++ b/src/main/scala/lisa/automation/kernel/SimplePropositionalSolver.scala @@ -1,4 +1,4 @@ -package lisa.proven.tactics +package lisa.automation.kernel import lisa.kernel.fol.FOL.* import lisa.kernel.proof.SCProof @@ -27,6 +27,7 @@ object SimplePropositionalSolver { case Iff => if (add) iffs.add(phi) else iffs.remove(phi) case And => if (add) ands.add(phi) else ands.remove(phi) case Or => if (add) ors.add(phi) else ors.remove(phi) + case _ => if (add) others.add(phi) else others.remove(phi) } case _ => if (add) others.add(phi) else others.remove(phi) }) diff --git a/src/main/scala/lisa/proven/mathematics/Mapping.scala b/src/main/scala/lisa/proven/mathematics/Mapping.scala index 70ad8ffe..33715213 100644 --- a/src/main/scala/lisa/proven/mathematics/Mapping.scala +++ b/src/main/scala/lisa/proven/mathematics/Mapping.scala @@ -1,7 +1,7 @@ package lisa.proven.mathematics -import lisa.proven.tactics.Destructors.* -import lisa.proven.tactics.ProofTactics.* +import lisa.automation.kernel.Destructors.* +import lisa.automation.kernel.ProofTactics.* import SetTheory.* @@ -9,7 +9,7 @@ import SetTheory.* * This file contains theorem related to the replacement schema, i.e. how to "map" a set through a functional symbol. * Leads to the definition of the cartesian product. */ -object Mapping extends lisa.proven.Main { +object Mapping extends lisa.Main { THEOREM("functionalMapping") of "∀a. (a ∈ ?A) ⇒ ∃!x. ?phi(x, a) ⊢ ∃!X. ∀x. (x ∈ X) ↔ ∃a. (a ∈ ?A) ∧ ?phi(x, a)" PROOF { @@ -23,9 +23,9 @@ object Mapping extends lisa.proven.Main { val X = VariableLabel("X") val B = VariableLabel("B") val B1 = VariableLabel("B1") - val phi = SchematicNPredicateLabel("phi", 2) - val sPhi = SchematicNPredicateLabel("P", 2) - val sPsi = SchematicNPredicateLabel("P", 3) + val phi = SchematicPredicateLabel("phi", 2) + val sPhi = SchematicPredicateLabel("P", 2) + val sPsi = SchematicPredicateLabel("P", 3) val H = existsOne(x, phi(x, a)) val H1 = forall(a, in(a, A) ==> H) @@ -248,8 +248,8 @@ object Mapping extends lisa.proven.Main { val X = VariableLabel("X") val B = VariableLabel("B") val B1 = VariableLabel("B1") - val phi = SchematicNPredicateLabel("phi", 2) - val psi = SchematicNPredicateLabel("psi", 3) + val phi = SchematicPredicateLabel("phi", 2) + val psi = SchematicPredicateLabel("psi", 3) val H = existsOne(x, phi(x, a)) val H1 = forall(a, in(a, A) ==> H) val i1 = thm"functionalMapping" @@ -306,7 +306,7 @@ object Mapping extends lisa.proven.Main { val z1 = VariableLabel("z1") val F = SchematicFunctionLabel("F", 1) val f = VariableLabel("f") - val phi = SchematicNPredicateLabel("phi", 1) + val phi = SchematicPredicateLabel("phi", 1) val g = VariableFormulaLabel("g") val g2 = SCSubproof({ @@ -357,8 +357,8 @@ object Mapping extends lisa.proven.Main { val F = SchematicFunctionLabel("F", 1) val A = VariableLabel("A") val B = VariableLabel("B") - val phi = SchematicNPredicateLabel("phi", 1) - val psi = SchematicNPredicateLabel("psi", 3) + val phi = SchematicPredicateLabel("phi", 1) + val psi = SchematicPredicateLabel("psi", 3) val i1 = thm"lemmaLayeredTwoArgumentsMap" val i2 = thm"applyFunctionToUniqueObject" @@ -376,54 +376,55 @@ object Mapping extends lisa.proven.Main { val B = VariableLabel("B") private val z = VariableLabel("z") val cartesianProduct: ConstantFunctionLabel = - DEFINE("cartProd", A, B) asThe z suchThat { - val a = VariableLabel("a") - val b = VariableLabel("b") - val x = VariableLabel("x") - val x0 = VariableLabel("x0") - val x1 = VariableLabel("x1") - val A = VariableLabel("A") - val B = VariableLabel("B") - exists(x, (z === union(x)) /\ forall(x0, in(x0, x) <=> exists(b, in(b, B) /\ forall(x1, in(x1, x0) <=> exists(a, in(a, A) /\ (x1 === oPair(a, b))))))) - } PROOF { - def makeFunctional(t: Term): Proof = { - val x = VariableLabel(freshId(t.freeVariables.map(_.id), "x")) - val y = VariableLabel(freshId(t.freeVariables.map(_.id), "y")) - val s0 = RightRefl(() |- t === t, t === t) - val s1 = Rewrite(() |- (x === t) <=> (x === t), 0) - val s2 = RightForall(() |- forall(x, (x === t) <=> (x === t)), 1, (x === t) <=> (x === t), x) - val s3 = RightExists(() |- exists(y, forall(x, (x === y) <=> (x === t))), 2, forall(x, (x === y) <=> (x === t)), y, t) - val s4 = Rewrite(() |- existsOne(x, x === t), 3) - Proof(s0, s1, s2, s3, s4) - } + DEFINE("cartProd", A, B) asThe + z suchThat { + val a = VariableLabel("a") + val b = VariableLabel("b") + val x = VariableLabel("x") + val x0 = VariableLabel("x0") + val x1 = VariableLabel("x1") + val A = VariableLabel("A") + val B = VariableLabel("B") + exists(x, (z === union(x)) /\ forall(x0, in(x0, x) <=> exists(b, in(b, B) /\ forall(x1, in(x1, x0) <=> exists(a, in(a, A) /\ (x1 === oPair(a, b))))))) + } PROOF { + def makeFunctional(t: Term): Proof = { + val x = VariableLabel(freshId(t.freeVariables.map(_.id), "x")) + val y = VariableLabel(freshId(t.freeVariables.map(_.id), "y")) + val s0 = RightRefl(() |- t === t, t === t) + val s1 = Rewrite(() |- (x === t) <=> (x === t), 0) + val s2 = RightForall(() |- forall(x, (x === t) <=> (x === t)), 1, (x === t) <=> (x === t), x) + val s3 = RightExists(() |- exists(y, forall(x, (x === y) <=> (x === t))), 2, forall(x, (x === y) <=> (x === t)), y, t) + val s4 = Rewrite(() |- existsOne(x, x === t), 3) + Proof(s0, s1, s2, s3, s4) + } - val a = VariableLabel("a") - val b = VariableLabel("b") - val x = VariableLabel("x") - val A = VariableLabel("A") - val B = VariableLabel("B") - val psi = SchematicNPredicateLabel("psi", 3) + val a = VariableLabel("a") + val b = VariableLabel("b") + val x = VariableLabel("x") + val A = VariableLabel("A") + val B = VariableLabel("B") + val psi = SchematicPredicateLabel("psi", 3) - val i1 = thm"mapTwoArguments" // ∀b. (b ∈ ?B) ⇒ ∀a. (a ∈ ?A) ⇒ ∃!x. ?psi(x, a, b) ⊢ ∃!z. ∃x. (z = U(x)) ∧ ∀x_0. (x_0 ∈ x) ↔ ∃b. (b ∈ ?B) ∧ ∀x1. (x1 ∈ x_0) ↔ ∃a. (a ∈ ?A) ∧ ?psi(x1, a, b) + val i1 = thm"mapTwoArguments" // ∀b. (b ∈ ?B) ⇒ ∀a. (a ∈ ?A) ⇒ ∃!x. ?psi(x, a, b) ⊢ ∃!z. ∃x. (z = U(x)) ∧ ∀x_0. (x_0 ∈ x) ↔ ∃b. (b ∈ ?B) ∧ ∀x1. (x1 ∈ x_0) ↔ ∃a. (a ∈ ?A) ∧ ?psi(x1, a, b) - val s0 = SCSubproof({ - val s0 = SCSubproof(makeFunctional(oPair(a, b))) - val s1 = Weakening((in(b, B), in(a, A)) |- s0.bot.right, 0) - val s2 = Rewrite(in(b, B) |- in(a, A) ==> s0.bot.right.head, 1) - val s3 = RightForall(in(b, B) |- forall(a, in(a, A) ==> s0.bot.right.head), 2, in(a, A) ==> s0.bot.right.head, a) - val s4 = Rewrite(() |- in(b, B) ==> forall(a, in(a, A) ==> s0.bot.right.head), 3) - val s5 = RightForall(() |- forall(b, in(b, B) ==> forall(a, in(a, A) ==> s0.bot.right.head)), 4, in(b, B) ==> forall(a, in(a, A) ==> s0.bot.right.head), b) - Proof(steps(s0, s1, s2, s3, s4, s5)) - }) // ∀b. (b ∈ ?B) ⇒ ∀a. (a ∈ ?A) ⇒ ∃!x. x= (a, b) + val s0 = SCSubproof({ + val s0 = SCSubproof(makeFunctional(oPair(a, b))) + val s1 = Weakening((in(b, B), in(a, A)) |- s0.bot.right, 0) + val s2 = Rewrite(in(b, B) |- in(a, A) ==> s0.bot.right.head, 1) + val s3 = RightForall(in(b, B) |- forall(a, in(a, A) ==> s0.bot.right.head), 2, in(a, A) ==> s0.bot.right.head, a) + val s4 = Rewrite(() |- in(b, B) ==> forall(a, in(a, A) ==> s0.bot.right.head), 3) + val s5 = RightForall(() |- forall(b, in(b, B) ==> forall(a, in(a, A) ==> s0.bot.right.head)), 4, in(b, B) ==> forall(a, in(a, A) ==> s0.bot.right.head), b) + Proof(steps(s0, s1, s2, s3, s4, s5)) + }) // ∀b. (b ∈ ?B) ⇒ ∀a. (a ∈ ?A) ⇒ ∃!x. x= (a, b) - val s1 = InstPredSchema( - instantiatePredicateSchemaInSequent(i1, Map(psi -> LambdaTermFormula(Seq(x, a, b), x === oPair(a, b)))), - -1, - Map(psi -> LambdaTermFormula(Seq(x, a, b), x === oPair(a, b))) - ) - val s2 = Cut(() |- s1.bot.right, 0, 1, s1.bot.left.head) - Proof(steps(s0, s1, s2), imports(i1)) - } using (thm"mapTwoArguments") + val s1 = InstPredSchema( + instantiatePredicateSchemaInSequent(i1, Map(psi -> LambdaTermFormula(Seq(x, a, b), x === oPair(a, b)))), + -1, + Map(psi -> LambdaTermFormula(Seq(x, a, b), x === oPair(a, b))) + ) + val s2 = Cut(() |- s1.bot.right, 0, 1, s1.bot.left.head) + Proof(steps(s0, s1, s2), imports(i1)) + } using (thm"mapTwoArguments") show } diff --git a/src/main/scala/lisa/proven/mathematics/SetTheory.scala b/src/main/scala/lisa/proven/mathematics/SetTheory.scala index 4b43d214..10a66935 100644 --- a/src/main/scala/lisa/proven/mathematics/SetTheory.scala +++ b/src/main/scala/lisa/proven/mathematics/SetTheory.scala @@ -1,21 +1,19 @@ package lisa.proven.mathematics -import lisa.proven.tactics.Destructors.* -import lisa.proven.tactics.ProofTactics.* +import lisa.automation.kernel.Destructors.* +import lisa.automation.kernel.ProofTactics.* /** * An embryo of mathematical development, containing a few example theorems and the definition of the ordered pair. */ -object SetTheory extends lisa.proven.Main { +object SetTheory extends lisa.Main { THEOREM("russelParadox") of "∀x. (x ∈ ?y) ↔ ¬(x ∈ x) ⊢" PROOF { val y = VariableLabel("y") val x = VariableLabel("x") - val contra = in(y, y) <=> !in(y, y) - val s0 = Hypothesis(contra |- contra, contra) - val s1 = LeftForall(forall(x, in(x, y) <=> !in(x, x)) |- contra, 0, in(x, y) <=> !in(x, x), x, y) - val s2 = Rewrite(forall(x, in(x, y) <=> !in(x, x)) |- (), 1) - Proof(s0, s1, s2) + val s0 = RewriteTrue(in(y, y) <=> !in(y, y) |- ()) + val s1 = LeftForall(forall(x, in(x, y) <=> !in(x, x)) |- (), 0, in(x, y) <=> !in(x, x), x, y) + Proof(s0, s1) } using () thm"russelParadox".show @@ -334,7 +332,7 @@ object SetTheory extends lisa.proven.Main { val y = VariableLabel("y") val z = VariableLabel("z") val h = VariableFormulaLabel("h") - val sPhi = SchematicNPredicateLabel("P", 2) + val sPhi = SchematicPredicateLabel("P", 2) // forall(z, exists(y, forall(x, in(x,y) <=> (in(x,y) /\ sPhi(x,z))))) val i1 = () |- comprehensionSchema val i2 = thm"russelParadox" // forall(x1, in(x1,y) <=> !in(x1, x1)) |- () diff --git a/src/main/scala/lisa/proven/peano_example/Peano.scala b/src/main/scala/lisa/proven/peano_example/Peano.scala index ca74d815..90479702 100644 --- a/src/main/scala/lisa/proven/peano_example/Peano.scala +++ b/src/main/scala/lisa/proven/peano_example/Peano.scala @@ -1,16 +1,16 @@ package lisa.proven.peano_example +import lisa.automation.kernel.ProofTactics.* import lisa.kernel.fol.FOL.* import lisa.kernel.proof.RunningTheory import lisa.kernel.proof.SCProof import lisa.kernel.proof.SequentCalculus.* -import lisa.proven.tactics.ProofTactics.* import lisa.utils.Helpers.{_, given} import lisa.utils.Library import lisa.utils.Printer object Peano { - export PeanoArithmeticsLibrary.{*, given} + export PeanoArithmeticsLibrary.{_, given} /////////////////////////// OUTPUT CONTROL ////////////////////////// given output: (String => Unit) = println @@ -43,7 +43,7 @@ object Peano { val (premise, conclusion) = (inductionInstance.bot.right.head match { case ConnectorFormula(Implies, Seq(premise, conclusion)) => (premise, conclusion) case _ => require(false, s"induction instance should be of the form A => B, got ${Printer.prettyFormula(inductionInstance.bot.right.head)}") - }) + }): @unchecked val baseFormula = baseProof.bot.right.head val stepFormula = inductionStepProof.bot.right.head require( @@ -66,7 +66,7 @@ object Peano { val (y1, z1) = (VariableLabel("y1"), VariableLabel("z1")) - THEOREM("x + 0 = 0 + x") of "∀x. plus(x, zero) === plus(zero, x)" PROOF { + THEOREM("x + 0 = 0 + x") of "?x. plus(x, zero) === plus(zero, x)" PROOF { val refl0: SCProofStep = RightRefl(() |- s(x) === s(x), s(x) === s(x)) val subst1 = RightSubstEq((x === plus(zero, x)) |- s(x) === s(plus(zero, x)), 0, (x, plus(zero, x)) :: Nil, LambdaTermFormula(Seq(y), s(x) === s(y))) val implies2 = RightImplies(() |- (x === plus(zero, x)) ==> (s(x) === s(plus(zero, x))), 1, x === plus(zero, x), s(x) === s(plus(zero, x))) @@ -147,7 +147,7 @@ object Peano { } using (ax"ax4plusSuccessor", ax"ax3neutral", ax"ax7induction") show - THEOREM("switch successor") of "∀y. ∀x. plus(x, s(y)) === plus(s(x), y)" PROOF { + THEOREM("switch successor") of "?y. ?x. plus(x, s(y)) === plus(s(x), y)" PROOF { //////////////////////////////////// Base: x + S0 = Sx + 0 /////////////////////////////////////////////// val base0 = { // x + 0 = x @@ -184,7 +184,7 @@ object Peano { ) } - /////////////// Induction step: ∀y. (x + Sy === Sx + y) ==> (x + SSy === Sx + Sy) //////////////////// + /////////////// Induction step: ?y. (x + Sy === Sx + y) ==> (x + SSy === Sx + Sy) //////////////////// val inductionStep1 = { // x + SSy = S(x + Sy) val moveSuccessor0 = SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", x), s(y)), IndexedSeq(-2)) diff --git a/src/main/scala/lisa/proven/peano_example/PeanoArithmetics.scala b/src/main/scala/lisa/proven/peano_example/PeanoArithmetics.scala index 03797ff2..86fb9467 100644 --- a/src/main/scala/lisa/proven/peano_example/PeanoArithmetics.scala +++ b/src/main/scala/lisa/proven/peano_example/PeanoArithmetics.scala @@ -12,7 +12,7 @@ object PeanoArithmetics { final val s = ConstantFunctionLabel("S", 1) final val plus = ConstantFunctionLabel("+", 2) final val times = ConstantFunctionLabel("*", 2) - final val sPhi: SchematicPredicateLabel = SchematicNPredicateLabel("?p", 1) + final val sPhi: SchematicPredicateLabel = SchematicPredicateLabel("?p", 1) final val ax1ZeroSuccessor: Formula = forall(x, !(s(x) === zero)) final val ax2Injectivity: Formula = forall(x, forall(y, (s(x) === s(y)) ==> (x === y))) diff --git a/src/test/scala/lisa/automation/ProofTests.scala b/src/test/scala/lisa/automation/ProofTests.scala new file mode 100644 index 00000000..4bc569ba --- /dev/null +++ b/src/test/scala/lisa/automation/ProofTests.scala @@ -0,0 +1,392 @@ +package lisa.automation + +import lisa.automation.Proof2.* +import lisa.front.fol.FOL.* +import lisa.kernel.proof.SCProofChecker +import lisa.utils.Printer +import org.scalatest.Ignore +import org.scalatest.funsuite.AnyFunSuite + +import scala.language.adhocExtensions + +// Not working while the front's unifier is not repaired. +@Ignore +class ProofTests extends AnyFunSuite { + + val (a, b, c) = (SchematicPredicateLabel[0]("a"), SchematicPredicateLabel[0]("b"), SchematicPredicateLabel[0]("c")) + val (s, t, u) = (SchematicTermLabel[0]("s"), SchematicTermLabel[0]("t"), SchematicTermLabel[0]("u")) + val (x, y) = (VariableLabel("x"), VariableLabel("y")) + + private def checkProof(proof: Proof): Unit = { + val emptyEnvironment: ProofEnvironment = newEmptyEnvironment() + val result = evaluateProof(proof)(emptyEnvironment).map(reconstructSCProof) + assert(result.nonEmpty, s"kernel proof was empty for $proof") + val scProof = result.get._1 + val judgement = SCProofChecker.checkSCProof(scProof) + assert(judgement.isValid, Printer.prettySCProof(judgement)) + assert(scProof.imports.isEmpty, s"proof unexpectedly uses imports: ${Printer.prettySCProof(judgement)}") + assert( + lisa.kernel.proof.SequentCalculus.isSameSequent(scProof.conclusion, proof.initialState.goals.head), + s"proof does not prove ${Printer.prettySequent(proof.initialState.goals.head)}:\nPrinter.prettySCProof(judgement)" + ) + } + + test("hypothesis") { + checkProof( + Proof( + (a, b /\ c) |- (b /\ c, b) + )( + RuleHypothesis(RuleParameters().withIndices(0)(1)(0)) + ) + ) + + } + + test("left and") { + checkProof( + Proof( + (a /\ b) |- a + )( + RuleIntroductionLeftAnd(), + RuleHypothesis(RuleParameters().withIndices(0)(0)(0)) + ) + ) + } + + test("right and") { + checkProof( + Proof( + (a, b) |- (a /\ b) + )( + RuleIntroductionRightAnd(RuleParameters().withIndices(0)()(0)), + RuleHypothesis(RuleParameters().withIndices(0)(0)(0)), + RuleHypothesis(RuleParameters().withIndices(0)(1)(0)) + ) + ) + } + + test("left or") { + checkProof( + Proof( + (a \/ b) |- (a, b) + )( + RuleIntroductionLeftOr(RuleParameters().withIndices(0)(0)()), + RuleHypothesis(RuleParameters().withIndices(0)(0)(0)), + RuleHypothesis(RuleParameters().withIndices(0)(0)(1)) + ) + ) + } + + test("right or") { + checkProof( + Proof( + a |- (a \/ b) + )( + RuleIntroductionRightOr(RuleParameters().withIndices(0)()(0)), + RuleHypothesis(RuleParameters().withIndices(0)(0)(0)) + ) + ) + } + + test("left implies") { + checkProof( + Proof( + (a ==> b, a) |- b + )( + RuleIntroductionLeftImplies(RuleParameters().withIndices(0)(0)()), + RuleHypothesis(RuleParameters().withIndices(0)(0)(1)), + RuleHypothesis(RuleParameters().withIndices(0)(1)(0)) + ) + ) + } + + test("right implies") { + checkProof( + Proof( + () |- (a ==> a) + )( + RuleIntroductionRightImplies(RuleParameters().withIndices(0)()(0)), + RuleHypothesis(RuleParameters().withIndices(0)(0)(0)) + ) + ) + } + + test("left iff") { + checkProof( + Proof( + (a <=> b) |- (b ==> a) + )( + RuleIntroductionLeftIff(RuleParameters().withIndices(0)(0)()), + RuleHypothesis(RuleParameters().withIndices(0)(1)(0)) + ) + ) + } + + test("right iff") { + checkProof( + Proof( + (a ==> b, b ==> a) |- (a <=> b) + )( + RuleIntroductionRightIff(RuleParameters().withIndices(0)()(0)), + RuleHypothesis(RuleParameters().withIndices(0)(0)(0)), + RuleHypothesis(RuleParameters().withIndices(0)(1)(0)) + ) + ) + } + + test("left not") { + checkProof( + Proof( + (a, !a) |- b + )( + RuleIntroductionLeftNot(RuleParameters().withIndices(0)(1)()), + RuleHypothesis(RuleParameters().withIndices(0)(0)(1)) // FIXME shouldn't it be 0? + ) + ) + } + + test("right not") { + checkProof( + Proof( + () |- (!a, a) + )( + RuleIntroductionRightNot(RuleParameters().withIndices(0)()(0)), + RuleHypothesis(RuleParameters().withIndices(0)(0)(0)) + ) + ) + } + + test("left =") { + checkProof( + Proof( + () |- (t === t) + )( + RuleEliminationLeftRefl(RuleParameters().withFunction(Notations.s, t)), + RuleHypothesis() + ) + ) + } + + test("right =") { + checkProof( + Proof( + () |- (t === t) + )( + RuleIntroductionRightRefl(RuleParameters().withFunction(Notations.s, t)) + ) + ) + } + + test("introduction higher order") { + checkProof( + Proof( + forall(x, u === x) |- (u === s) + )( + RuleIntroductionLeftForall( + RuleParameters() + .withPredicate(Notations.p, x => u === x) + .withFunction(Notations.t, s) + ), + RuleHypothesis() + ) + ) + } + + test("right forall right or") { + checkProof( + Proof( + a |- forall(x, (u === x) \/ a) + )( + RuleIntroductionRightForall( + RuleParameters() + .withPredicate(Notations.p, x => (u === x) \/ a) + ), + RuleIntroductionRightOr(), + RuleHypothesis() + ) + ) + } + + test("left exists left and") { + checkProof( + Proof( + exists(x, (s === x) /\ a) |- a + )( + RuleIntroductionLeftExists( + RuleParameters() + .withPredicate(Notations.p, x => (s === x) /\ a) + ), + RuleIntroductionLeftAnd(), + RuleHypothesis() + ) + ) + } + + test("right exists") { + checkProof( + Proof( + (s === t) |- exists(x, s === x) + )( + RuleIntroductionRightExists( + RuleParameters() + .withPredicate(Notations.p, s === _) + .withFunction(Notations.t, t) + ), + RuleHypothesis() + ) + ) + } + + test("left subst =") { + checkProof( + Proof( + (s === t, u === t) |- (u === s) + )( + RuleIntroductionLeftSubstEq( + RuleParameters() + .withPredicate(Notations.p, u === _) + ), + RuleHypothesis() + ) + ) + } + + test("right subst =") { + checkProof( + Proof( + (s === t, u === s) |- (u === t) + )( + RuleIntroductionRightSubstEq( + RuleParameters() + .withPredicate(Notations.p, u === _) + ), + RuleHypothesis() + ) + ) + } + + test("left subst iff") { + checkProof( + Proof( + (a <=> b, c <=> b) |- (c <=> a) + )( + RuleIntroductionLeftSubstIff( + RuleParameters() + .withConnector(Notations.f, c <=> _) + ), + RuleHypothesis() + ) + ) + } + + test("right subst iff") { + checkProof( + Proof( + (a <=> b, c <=> a) |- (c <=> b) + )( + RuleIntroductionRightSubstIff( + RuleParameters() + .withConnector(Notations.f, c <=> _) + ), + RuleHypothesis() + ) + ) + } + + test("elimination left subst iff") { + checkProof( + Proof( + (s === t) |- (t === s) + )( + RuleEliminationLeftSubstIff( + RuleParameters() + .withConnector(Notations.f, identity) + .withPredicate(Notations.a, t === s) + .withPredicate(Notations.b, s === t) + ), + RuleHypothesis(), + TacticalRewrite((s === t) |- (s === t)), + RuleHypothesis() + ) + ) + } + + test("elimination right subst iff") { + checkProof( + Proof( + (s === t) |- (t === s) + )( + RuleEliminationRightSubstIff( + RuleParameters() + .withConnector(Notations.f, identity) + .withPredicate(Notations.a, s === t) + .withPredicate(Notations.b, t === s) + ), + RuleHypothesis(), + TacticalRewrite((s === t) |- (s === t)), + RuleHypothesis() + ) + ) + } + + test("elimination left subst =") { + checkProof( + Proof( + (s === t, t === u) |- (s === u) + )( + RuleEliminationLeftSubstEq( + RuleParameters() + .withPredicate(Notations.p, _ === u) + .withFunction(Notations.s, s) + .withFunction(Notations.t, t) + ), + RuleHypothesis(), + RuleHypothesis() + ) + ) + } + + test("elimination right subst =") { + checkProof( + Proof( + (s === t, t === u) |- (s === u) + )( + RuleEliminationRightSubstEq( + RuleParameters() + .withPredicate(Notations.p, _ === u) + .withFunction(Notations.s, t) + .withFunction(Notations.t, s) + ), + RuleHypothesis(), + TacticalRewrite((s === t, t === u) |- (s === t)), + RuleHypothesis() + ) + ) + } + + test("environment") { + val ctx = newEmptyEnvironment() + + val thm1 = ctx.mkTheorem( + Proof( + () |- ((a /\ b) ==> (b /\ a)) + )( + TacticSolverNative + ) + ) + + val thm2 = ctx.mkTheorem( + Proof( + () |- ((b /\ a) ==> (a /\ b)) + )( + TacticSolverNative + ) + ) + + val thm3 = RuleIntroductionRightIff(thm1, thm2).get + + val reconstructed = reconstructSCProofForTheorem(thm3) + + assert(SCProofChecker.checkSCProof(reconstructed).isValid, Printer.prettySCProof(reconstructed)) + } +} diff --git a/src/test/scala/lisa/examples/ExampleTests.scala b/src/test/scala/lisa/examples/ExampleTests.scala new file mode 100644 index 00000000..95057eba --- /dev/null +++ b/src/test/scala/lisa/examples/ExampleTests.scala @@ -0,0 +1,29 @@ +package lisa.examples + +import org.scalatest.funsuite.AnyFunSuite + +import scala.language.adhocExtensions + +class ExampleTests extends AnyFunSuite { + /* + test("front interactive proof (1)") { + frontInteractiveProof1() + } + + test("front interactive proof (2)") { + frontInteractiveProof2() + } + + test("front matching") { + frontMatching() + } + + test("front parsing & printing") { + frontParsingPrinting() + } + + test("front solver") { + frontSolver() + } + */ +} diff --git a/src/test/scala/lisa/proven/InitialProofsTests.scala b/src/test/scala/lisa/proven/InitialProofsTests.scala index 8872b041..09cfd943 100644 --- a/src/test/scala/lisa/proven/InitialProofsTests.scala +++ b/src/test/scala/lisa/proven/InitialProofsTests.scala @@ -4,7 +4,7 @@ import lisa.test.ProofCheckerSuite import lisa.utils.Printer class InitialProofsTests extends ProofCheckerSuite { - import lisa.proven.SetTheoryLibrary.* + import lisa.settheory.SetTheoryLibrary.* test("File SetTheory initialize well") { lisa.proven.mathematics.SetTheory diff --git a/src/test/scala/lisa/proven/SimpleProverTests.scala b/src/test/scala/lisa/proven/SimpleProverTests.scala index e6ba8d3f..db01086d 100644 --- a/src/test/scala/lisa/proven/SimpleProverTests.scala +++ b/src/test/scala/lisa/proven/SimpleProverTests.scala @@ -1,10 +1,10 @@ package lisa.proven +import lisa.automation.kernel.SimplePropositionalSolver as SPS import lisa.kernel.fol.FOL.* import lisa.kernel.proof.RunningTheory import lisa.kernel.proof.RunningTheory.PredicateLogic import lisa.kernel.proof.SCProofChecker -import lisa.proven.tactics.SimplePropositionalSolver as SPS import lisa.tptp.KernelParser.* import lisa.tptp.ProblemGatherer.getPRPproblems import lisa.utils.Helpers.* -- GitLab