From 3f6c0c153888229f3958c79af90610a48ac057b0 Mon Sep 17 00:00:00 2001
From: Ekaterina Goltsova <cache-nez@yandex.ru>
Date: Tue, 7 Jun 2022 17:02:18 +0200
Subject: [PATCH] Allow adding only constant (as opposed to schematic) symbols
 to a running theory

This commit introduces the separation between Function/Predicate labels and
theory symbols. Both constant and schematic function labels behave as functions,
both constant and schematic predicate labels behave as predicates, but only
constant function and predicate labels can be symbols in a theory. This is now
expressed in the project by a marker trait TheorySymbol.
---
 src/main/scala/lisa/kernel/Printer.scala      |  2 +-
 .../lisa/kernel/fol/CommonDefinitions.scala   |  5 ++++
 .../kernel/fol/FormulaLabelDefinitions.scala  |  4 +--
 .../kernel/fol/TermLabelDefinitions.scala     |  2 +-
 .../lisa/kernel/proof/RunningTheory.scala     | 27 +++++++------------
 .../lisa/settheory/SetTheoryDefinitions.scala | 18 ++++++-------
 6 files changed, 28 insertions(+), 30 deletions(-)

diff --git a/src/main/scala/lisa/kernel/Printer.scala b/src/main/scala/lisa/kernel/Printer.scala
index 3bac6470..044c181a 100644
--- a/src/main/scala/lisa/kernel/Printer.scala
+++ b/src/main/scala/lisa/kernel/Printer.scala
@@ -37,7 +37,7 @@ object Printer {
     ConstantFunctionLabel("power_set", 1),
     ConstantFunctionLabel("union", 1)
   )
-  private val nonAtomicPredicates = Set(equality, membership, subsetOf, sameCardinality) // Predicates which require parentheses (for readability)
+  private val nonAtomicPredicates: Set[PredicateLabel] = Set(equality, membership, subsetOf, sameCardinality) // Predicates which require parentheses (for readability)
 
   private def prettyFormulaInternal(formula: Formula, isRightMost: Boolean, compact: Boolean): String = formula match {
     case PredicateFormula(label, args) =>
diff --git a/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala b/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala
index 3e34019f..65906e98 100644
--- a/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala
+++ b/src/main/scala/lisa/kernel/fol/CommonDefinitions.scala
@@ -20,6 +20,11 @@ private[fol] trait CommonDefinitions {
     val id: String
   }
 
+  /**
+   * Marks classes that can represent symbols in a theory
+   */
+  trait TheorySymbol
+
   def freshId(taken: Set[String], base: String): String = {
     var i = 0;
     while (taken contains base + "_" + i) i += 1
diff --git a/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala b/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala
index 81de8601..3439bc0a 100644
--- a/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala
+++ b/src/main/scala/lisa/kernel/fol/FormulaLabelDefinitions.scala
@@ -41,12 +41,12 @@ private[fol] trait FormulaLabelDefinitions extends CommonDefinitions {
   /**
    * A standard predicate symbol. Typical example are equality (=) and membership (∈)
    */
-  final case class ConstantPredicateLabel(id: String, arity: Int) extends PredicateLabel
+  final case class ConstantPredicateLabel(id: String, arity: Int) extends PredicateLabel with TheorySymbol
 
   /**
    * The equality symbol (=) for first order logic.
    */
-  val equality: PredicateLabel = ConstantPredicateLabel("=", 2)
+  val equality: ConstantPredicateLabel = ConstantPredicateLabel("=", 2)
 
   /**
    * A predicate symbol that can be instantiated with any formula.
diff --git a/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala b/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala
index a7ca8288..24c501dc 100644
--- a/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala
+++ b/src/main/scala/lisa/kernel/fol/TermLabelDefinitions.scala
@@ -54,7 +54,7 @@ private[fol] trait TermLabelDefinitions extends CommonDefinitions {
    * @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
    */
-  final case class ConstantFunctionLabel(id: String, arity: Int) extends FunctionLabel
+  final case class ConstantFunctionLabel(id: String, arity: Int) extends FunctionLabel with TheorySymbol
 
   /**
    * A schematic function symbol that can be substituted.
diff --git a/src/main/scala/lisa/kernel/proof/RunningTheory.scala b/src/main/scala/lisa/kernel/proof/RunningTheory.scala
index c68e8993..15175569 100644
--- a/src/main/scala/lisa/kernel/proof/RunningTheory.scala
+++ b/src/main/scala/lisa/kernel/proof/RunningTheory.scala
@@ -62,18 +62,12 @@ class RunningTheory {
 
   private[proof] val theoryAxioms: mMap[String, Axiom] = mMap.empty
   private[proof] val theorems: mMap[String, Theorem] = mMap.empty
-  private[proof] val funDefinitions: mMap[FunctionLabel, Option[FunctionDefinition]] = mMap.empty
-  private[proof] val predDefinitions: mMap[PredicateLabel, Option[PredicateDefinition]] = mMap(equality -> None)
+  private[proof] val definitions: mMap[TheorySymbol, Option[Definition]] = mMap(equality -> None)
 
   /**
    * Check if a label is a symbol of the theory
    */
-  def isAcceptedFunctionLabel(label: FunctionLabel): Boolean = funDefinitions.contains(label)
-
-  /**
-   * Check if a label is a symbol of the theory
-   */
-  def isAcceptedPredicateLabel(label: PredicateLabel): Boolean = predDefinitions.contains(label)
+  def isSymbol(label: TheorySymbol): Boolean = definitions.contains(label)
 
   /**
    * From a given proof, if it is true in the Running theory, add that theorem to the theory and returns it.
@@ -108,10 +102,10 @@ class RunningTheory {
    */
   def makePredicateDefinition(label: ConstantPredicateLabel, args: Seq[VariableLabel], phi: Formula): RunningTheoryJudgement[this.PredicateDefinition] = {
     if (belongsToTheory(phi))
-      if (!isAcceptedPredicateLabel(label))
+      if (!isSymbol(label))
         if (phi.freeVariables.subsetOf(args.toSet) && phi.schematicFunctions.isEmpty && phi.schematicPredicates.isEmpty)
           val newDef = PredicateDefinition(label, args, phi)
-          predDefinitions.update(label, Some(newDef))
+          definitions.update(label, Some(newDef))
           RunningTheoryJudgement.ValidJustification(newDef)
         else InvalidJustification("The definition is not allowed to contain schematic symbols or free variables.", None)
       else InvalidJustification("The specified symbol is already part of the theory and can't be redefined.", None)
@@ -141,7 +135,7 @@ class RunningTheory {
       phi: Formula
   ): RunningTheoryJudgement[this.FunctionDefinition] = {
     if (belongsToTheory(phi))
-      if (!isAcceptedFunctionLabel(label))
+      if (!isSymbol(label))
         if (phi.freeVariables.subsetOf(args.toSet + out) && phi.schematicFunctions.isEmpty && phi.schematicPredicates.isEmpty)
           if (proof.imports.forall(i => justifications.exists(j => isSameSequent(i, sequentFromJustification(j)))))
             val r = SCProofChecker.checkSCProof(proof)
@@ -153,7 +147,7 @@ class RunningTheory {
                     val subst2 = bindAll(Forall, args.reverse, BinderFormula(ExistsOne, out, phi))
                     if (isSame(r.head, subst) || isSame(r.head, subst2)) {
                       val newDef = FunctionDefinition(label, args, out, phi)
-                      funDefinitions.update(label, Some(newDef))
+                      definitions.update(label, Some(newDef))
                       RunningTheoryJudgement.ValidJustification(newDef)
                     } else InvalidJustification("The proof is correct but its conclusion does not correspond to a definition for for the formula phi.", None)
                   case _ => InvalidJustification("The conclusion of the proof must have an empty left hand side, and a single formula on the right hand side.", None)
@@ -201,7 +195,7 @@ class RunningTheory {
   def belongsToTheory(phi: Formula): Boolean = phi match {
     case PredicateFormula(label, args) =>
       label match {
-        case _: ConstantPredicateLabel => isAcceptedPredicateLabel(label) && args.forall(belongsToTheory)
+        case l: ConstantPredicateLabel => isSymbol(l) && args.forall(belongsToTheory)
         case _: SchematicPredicateLabel => args.forall(belongsToTheory)
       }
     case ConnectorFormula(label, args) => args.forall(belongsToTheory)
@@ -222,7 +216,7 @@ class RunningTheory {
     case VariableTerm(label) => true
     case FunctionTerm(label, args) =>
       label match {
-        case _: ConstantFunctionLabel => isAcceptedFunctionLabel(label) && args.forall(belongsToTheory(_))
+        case l: ConstantFunctionLabel => isSymbol(l) && args.forall(belongsToTheory(_))
         case _: SchematicFunctionLabel => args.forall(belongsToTheory(_))
       }
 
@@ -261,14 +255,13 @@ class RunningTheory {
    * 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.
    */
-  def addSymbol(l: FunctionLabel): Unit = funDefinitions.update(l, None)
-  def addSymbol(l: PredicateLabel): Unit = predDefinitions.update(l, None)
+  def addSymbol(s: TheorySymbol): Unit = definitions.update(s, None)
 
   /**
    * Public accessor to the set of symbol currently in the theory's language.
    * @return the set of symbol currently in the theory's language.
    */
-  def language: (List[(FunctionLabel, Option[FunctionDefinition])], List[(PredicateLabel, Option[PredicateDefinition])]) = (funDefinitions.toList, predDefinitions.toList)
+  def language: List[(TheorySymbol, Option[Definition])] = definitions.toList
 
   /**
    * Public accessor to the current set of axioms of the theory
diff --git a/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala b/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala
index 337e67fe..a2f3b2b7 100644
--- a/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala
+++ b/src/main/scala/lisa/settheory/SetTheoryDefinitions.scala
@@ -16,20 +16,20 @@ private[settheory] trait SetTheoryDefinitions {
   final val sPhi = SchematicPredicateLabel("P", 2)
   final val sPsi = SchematicPredicateLabel("P", 3)
   // Predicates
-  final val in: PredicateLabel = ConstantPredicateLabel("set_membership", 2)
-  final val subset: PredicateLabel = ConstantPredicateLabel("subset_of", 2)
-  final val sim: PredicateLabel = ConstantPredicateLabel("same_cardinality", 2) // Equicardinality
+  final val in = ConstantPredicateLabel("set_membership", 2)
+  final val subset = ConstantPredicateLabel("subset_of", 2)
+  final val sim = ConstantPredicateLabel("same_cardinality", 2) // Equicardinality
   final val predicates = Set(in, subset, sim)
   // val application
   // val pick
 
   // Functions
-  final val emptySet: FunctionLabel = ConstantFunctionLabel("empty_set", 0)
-  final val pair: FunctionLabel = ConstantFunctionLabel("unordered_pair", 2)
-  final val singleton: FunctionLabel = ConstantFunctionLabel("singleton", 1)
-  final val powerSet: FunctionLabel = ConstantFunctionLabel("power_set", 1)
-  final val union: FunctionLabel = ConstantFunctionLabel("union", 1)
-  final val universe: FunctionLabel = ConstantFunctionLabel("universe", 1)
+  final val emptySet = ConstantFunctionLabel("empty_set", 0)
+  final val pair = ConstantFunctionLabel("unordered_pair", 2)
+  final val singleton = ConstantFunctionLabel("singleton", 1)
+  final val powerSet = ConstantFunctionLabel("power_set", 1)
+  final val union = ConstantFunctionLabel("union", 1)
+  final val universe = ConstantFunctionLabel("universe", 1)
   final val functions = Set(emptySet, pair, singleton, powerSet, union, universe)
 
   val runningSetTheory: RunningTheory = new RunningTheory()
-- 
GitLab