From 5089212e8e2188451e452b32fa64459f5fdda85a Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Tue, 12 Nov 2024 15:41:43 +0100 Subject: [PATCH 01/38] copied lisa-set --- lisa-topology/src/main/scala/lisa/Main.scala | 37 + .../main/scala/lisa/SetTheoryLibrary.scala | 256 ++ .../main/scala/lisa/automation/Apply.scala | 274 ++ .../scala/lisa/automation/CommonTactics.scala | 262 ++ .../scala/lisa/automation/Congruence.scala | 622 ++++ .../scala/lisa/automation/Substitution.scala | 641 ++++ .../main/scala/lisa/automation/Tableau.scala | 491 ++++ .../scala/lisa/automation/Tautology.scala | 257 ++ .../scala/lisa/automation/atp/Goeland.scala | 121 + .../settheory/SetTheoryTactics.scala | 200 ++ .../main/scala/lisa/maths/Quantifiers.scala | 254 ++ .../lisa/maths/settheory/Comprehensions.scala | 201 ++ .../lisa/maths/settheory/InductiveSets.scala | 159 + .../lisa/maths/settheory/SetTheory.scala | 2614 +++++++++++++++++ .../lisa/maths/settheory/SetTheory2.scala | 108 + .../functions/FunctionProperties.scala | 428 +++ .../settheory/functions/Functionals.scala | 1365 +++++++++ .../maths/settheory/functions/package.scala | 74 + .../settheory/orderings/InclusionOrders.scala | 207 ++ .../maths/settheory/orderings/Induction.scala | 325 ++ .../maths/settheory/orderings/Orderings.scala | 5 + .../maths/settheory/orderings/Ordinals.scala | 338 +++ .../settheory/orderings/PartialOrders.scala | 356 +++ .../maths/settheory/orderings/Recursion.scala | 1943 ++++++++++++ .../maths/settheory/orderings/Segments.scala | 225 ++ .../settheory/orderings/WellOrders.scala | 140 + .../maths/settheory/types/TypeSystem.scala | 530 ++++ .../maths/settheory/types/adt/Frontend.scala | 566 ++++ .../maths/settheory/types/adt/Functions.scala | 133 + .../maths/settheory/types/adt/Helpers.scala | 1032 +++++++ .../maths/settheory/types/adt/Tactics.scala | 174 ++ .../maths/settheory/types/adt/Typed.scala | 285 ++ .../maths/settheory/types/adt/Untyped.scala | 1747 +++++++++++ .../maths/settheory/types/adt/package.scala | 200 ++ .../lisa/automation/CongruenceTest.scala | 913 ++++++ .../scala/lisa/automation/TableauTest.scala | 158 + .../lisa/examples/peano_example/Peano.scala | 344 +++ .../peano_example/PeanoArithmetics.scala | 39 + .../PeanoArithmeticsLibrary.scala | 7 + .../lisa/proven/InitialProofsTests.scala | 20 + .../lisa/utilities/ComprehensionsTests.scala | 83 + .../lisa/utilities/SerializationTest.scala | 188 ++ .../utilities/SubstitutionTacticTest.scala | 119 + .../test/scala/lisa/utilities/TestMain.scala | 16 + 44 files changed, 18457 insertions(+) create mode 100644 lisa-topology/src/main/scala/lisa/Main.scala create mode 100644 lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/Apply.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/Congruence.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/Substitution.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/Tableau.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/Tautology.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala create mode 100644 lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Recursion.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala create mode 100644 lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala create mode 100644 lisa-topology/src/test/scala/lisa/automation/TableauTest.scala create mode 100644 lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala create mode 100644 lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala create mode 100644 lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala create mode 100644 lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala create mode 100644 lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala create mode 100644 lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala create mode 100644 lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala create mode 100644 lisa-topology/src/test/scala/lisa/utilities/TestMain.scala diff --git a/lisa-topology/src/main/scala/lisa/Main.scala b/lisa-topology/src/main/scala/lisa/Main.scala new file mode 100644 index 000000000..c96ac042b --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/Main.scala @@ -0,0 +1,37 @@ +package lisa + +import lisa.SetTheoryLibrary +import lisa.prooflib.BasicMain + +/** + * The parent trait of all theory files containing mathematical development + */ +trait Main extends BasicMain { + + export lisa.fol.FOL.{*, given} + export SetTheoryLibrary.{given, _} + export lisa.prooflib.BasicStepTactic.* + export lisa.prooflib.SimpleDeducedSteps.* + + export lisa.automation.Tautology + export lisa.automation.Substitution + export lisa.automation.Tableau + export lisa.automation.Apply + export lisa.automation.Exact + + knownDefs.update(emptySet, Some(emptySetAxiom)) + knownDefs.update(unorderedPair, Some(pairAxiom)) + knownDefs.update(union, Some(unionAxiom)) + knownDefs.update(powerSet, Some(powerAxiom)) + knownDefs.update(subset, Some(subsetAxiom)) + + extension (symbol: ConstantLabel[?]) { + def definition: JUSTIFICATION = { + getDefinition(symbol).get + } + def shortDefinition: JUSTIFICATION = { + getShortDefinition(symbol).get + } + } + +} diff --git a/lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala b/lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala new file mode 100644 index 000000000..addae4c89 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala @@ -0,0 +1,256 @@ +package lisa + +import lisa.fol.FOL.{_, given} +import lisa.kernel.proof.RunningTheory +import lisa.prooflib.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.prooflib.Library { + + val theory = new RunningTheory() + + // Predicates + /** + * The symbol for the set membership predicate. + */ + final val in = ConstantPredicateLabel("elem", 2) + + /** + * The symbol for the subset predicate. + */ + final val subset = ConstantPredicateLabel("subsetOf", 2) + + /** + * The symbol for the equicardinality predicate. Needed for Tarski's axiom. + */ + final val sim = ConstantPredicateLabel("sameCardinality", 2) // Equicardinality + /** + * Set Theory basic predicates + */ + final val predicates = Set(in, subset, sim) + // val choice + + // Functions + /** + * The symbol for the empty set constant. + */ + final val emptySet = Constant("emptySet") + + /** + * The symbol for the unordered pair function. + */ + final val unorderedPair = ConstantFunctionLabel("unorderedPair", 2) + + /** + * The symbol for the powerset function. + */ + final val powerSet = ConstantFunctionLabel("powerSet", 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) + + /** + * Set Theory basic functions. + */ + final val functions = Set(unorderedPair, powerSet, union, universe) + + /** + * The kernel theory loaded with Set Theory symbols and axioms. + */ + // val runningSetTheory: RunningTheory = new RunningTheory() + // given RunningTheory = runningSetTheory + + predicates.foreach(s => addSymbol(s)) + functions.foreach(s => addSymbol(s)) + addSymbol(emptySet) + + private val x = variable + private val y = variable + private val z = variable + final val φ = predicate[1] + private val A = variable + private val B = variable + private val P = predicate[2] + + //////////// + // Axioms // + //////////// + + // Z + //////// + + /** + * Extensionality Axiom --- Two sets are equal iff they have the same + * elements. + * + * `() |- (x = y) ⇔ ∀ z. z ∈ x ⇔ z ∈ y` + */ + final val extensionalityAxiom: this.AXIOM = Axiom(forall(z, in(z, x) <=> in(z, y)) <=> (x === y)) + + /** + * Pairing Axiom --- For any sets `x` and `y`, there is a set that contains + * exactly `x` and `y`. This set is denoted mathematically as `{x, y}` and + * here as `unorderedPair(x, y)`. + * + * `() |- z ∈ {x, y} ⇔ (z === x ∨ z === y)` + * + * This axiom defines [[unorderedPair]] as the function symbol representing + * this set. + */ + final val pairAxiom: AXIOM = Axiom(in(z, unorderedPair(x, y)) <=> (x === z) \/ (y === z)) + + /** + * Comprehension/Separation Schema --- For a formula `ϕ(_, _)` and a set `z`, + * there exists a set `y` which contains only the elements `x` of `z` that + * satisfy `ϕ(x, z)`. This is represented mathematically as `y = {x ∈ z | ϕ(x, + * z)}`. + * + * `() |- ∃ y. ∀ x. x ∈ y ⇔ (x ∈ z ∧ ϕ(x, z))` + * + * This schema represents an infinite collection of axioms, one for each + * formula `ϕ(x, z)`. + */ + final val comprehensionSchema: AXIOM = Axiom(exists(y, forall(x, in(x, y) <=> (in(x, z) /\ φ(x))))) + + /** + * Empty Set Axiom --- From the Comprehension Schema follows the existence of + * a set containing no elements, the empty set. + * + * `∅ = {x ∈ X | x != x}`. + * + * This axiom defines [[emptySet]] as the constant symbol representing this set. + * + * `() |- !(x ∈ ∅)` + */ + final val emptySetAxiom: AXIOM = Axiom(!in(x, emptySet)) + + /** + * Union Axiom --- For any set `x`, there exists a set `union(x)` which is the + * union of its elements. For every element of `union(x)`, there is an element + * `y` of `x` which contains it. + * + * `() |- z ∈ union(x) ⇔ ∃ y. y ∈ x ∧ z ∈ y` + * + * Mathematically, we write `union(x)` as `∪ x`. + * + * This axiom defines [[union]] as the function symbol representing this set. + */ + final val unionAxiom: AXIOM = Axiom(in(z, union(x)) <=> exists(y, in(y, x) /\ in(z, y))) + + /** + * Subset Axiom --- For sets `x` and `y`, `x` is a subset of `y` iff every + * element of `x` is in `y`. Denoted `x ⊆ y`. + * + * `() |- x ⊆ y ⇔ (z ∈ x ⇒ z ∈ y)` + * + * This axiom defines the [[subset]] symbol as this predicate. + */ + final val subsetAxiom: AXIOM = Axiom(subset(x, y) <=> forall(z, in(z, x) ==> in(z, y))) + + /** + * Power Set Axiom --- For a set `x`, there exists a power set of `x`, denoted + * `PP(x)` or `power(x)` which contains every subset of x. + * + * `() |- z ∈ power(x) ⇔ z ⊆ x` + * + * This axiom defines [[powerSet]] as the function symbol representing this + * set. + */ + final val powerAxiom: AXIOM = Axiom(in(x, powerSet(y)) <=> subset(x, y)) + + /** + * Infinity Axiom --- There exists an infinite set. + * + * The definition requires a notion of finiteness, which generally corresponds + * to natural numbers. Since the naturals have not yet been defined, their + * definition and structure is imitated in the definition of an inductive set. + * + * `inductive(x) ⇔ (∅ ∈ x ∧ ∀ y. y ∈ x ⇒ y ∪ {y} ∈ x)` + * + * This axiom postulates that there exists an inductive set. + * + * `() |- ∃ x. inductive(x)` + */ + final val infinityAxiom: AXIOM = Axiom(exists(x, in(emptySet, x) /\ forall(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)))) + + /** + * Foundation/Regularity Axiom --- Every non-empty set `x` has an `∈`-minimal + * element. Equivalently, the relation `∈` on any family of sets is + * well-founded. + * + * `() |- (x != ∅) ==> ∃ y ∈ x. ∀ z. z ∈ x ⇒ ! z ∈ y` + */ + final val foundationAxiom: AXIOM = Axiom(!(x === emptySet) ==> exists(y, in(y, x) /\ forall(z, in(z, x) ==> !in(z, y)))) + + // ZF + ///////// + + /** + * Replacement Schema --- If a predicate `P` is 'functional' over `x`, i.e., + * given `a ∈ x`, there is a unique `b` such that `P(x, a, b)`, then the + * 'image' of `x` in P exists and is a set. It contains exactly the `b`'s that + * satisfy `P` for each `a ∈ x`. + */ + final val replacementSchema: AXIOM = Axiom( + forall(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))) ==> + exists(B, forall(y, in(y, B) <=> exists(x, in(x, A) /\ P(x, y)))) + ) + + final val tarskiAxiom: AXIOM = Axiom( + forall( + x, + in(x, universe(x)) /\ + forall( + y, + in(y, universe(x)) ==> (in(powerSet(y), universe(x)) /\ subset(powerSet(y), universe(x))) /\ + forall(z, subset(z, universe(x)) ==> (sim(y, universe(x)) /\ in(y, universe(x)))) + ) + ) + ) + + /** + * The set of all axioms of Tarski-Grothedick (TG) set theory. + * + * @return + */ + def axioms: Set[(String, AXIOM)] = Set( + ("EmptySet", emptySetAxiom), + ("extensionalityAxiom", extensionalityAxiom), + ("pairAxiom", pairAxiom), + ("unionAxiom", unionAxiom), + ("subsetAxiom", subsetAxiom), + ("powerAxiom", powerAxiom), + ("foundationAxiom", foundationAxiom), + ("infinityAxiom", infinityAxiom), + ("comprehensionSchema", comprehensionSchema), + ("replacementSchema", replacementSchema), + ("TarskiAxiom", tarskiAxiom) + ) + + ///////////// + // Aliases // + ///////////// + + // Unicode symbols + + val ∅ = emptySet + val ∈ = in + + extension (thi: Term) { + def ∈(that: Term): Formula = in(thi, that) + def ⊆(that: Term): Formula = subset(thi, that) + + def =/=(that: Term): Formula = !(thi === that) + + } + +} diff --git a/lisa-topology/src/main/scala/lisa/automation/Apply.scala b/lisa-topology/src/main/scala/lisa/automation/Apply.scala new file mode 100644 index 000000000..eb3896284 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/Apply.scala @@ -0,0 +1,274 @@ +package lisa.automation + +import lisa.fol.FOL.* +import lisa.prooflib.BasicStepTactic.* +import lisa.prooflib.ProofTacticLib.* +import lisa.prooflib.SimpleDeducedSteps.* +import lisa.prooflib.* +import lisa.utils.unification.UnificationUtils.* + +import scala.util.boundary + +import boundary.break + + +/** + * A tactic object that applies given arguments to a theorem by guessing its good instantiation. + * + * '''Example usage:''' + *{{{ + * val thm = have(transitive(z) |- x ∈ y ==> (y ∈ z ==> x ∈ z)) by ... + * val fact1 = have(a ∈ b) by ... + * val fact2 = have(b ∈ c) by ... + * have(transitive(c) |- a ∈ c) by (Apply(thm) on (fact1, fact2)) + * }}} + * + * where `x`, `y`, `z` are variables and `a`, `b`, `c` are constants. + * + * In some cases, this tactic also guesses the good instantiation of the facts. This feature + * however is not reliable at the moment. + * + * @param lib the library that is being used + * @param proof the ongoing proof object in which the tactic is called + * @param thm the reference theorem on which the arguments are applied. + * + * TODO: reimplement with a Horn clause solver + */ +class Apply(using val lib: Library, val proof: lib.Proof)(thm: proof.Fact) extends ProofTactic { + + /** + * Converts a substitution map to a sequence of [[SubstPair]] + */ + extension (subst: TermSubstitution) def toTSubstPair: Seq[SubstPair] = subst.map((k, v) => SubstPair(k, v)).toSeq + + extension (subst: FormulaSubstitution) def toFSubstPair: Seq[SubstPair] = subst.map((k, v) => SubstPair(k, v)).toSeq + + + /** + * Converts a sequent to a normal form where implications are passed to the left and where conjuctions are split in the premises. + * + * @param seq the sequent to be reduced in normal form + */ + private def normalForm(seq: Sequent): Sequent = + def removeImplies(s: Sequent): Sequent = + if s.right.isEmpty then + s + else + s.right.head match + case AppliedConnector(Implies, Seq(phi, psi)) => removeImplies(s.left + phi |- psi) + case _ => s + + + + def removeConjunctions(s: Sequent): Sequent = + def rec(f: Formula): Set[Formula] = + f match + case AppliedConnector(And, Seq(phi, psi)) => rec(phi) ++ rec(psi) + case _ => Set(f) + s.left.flatMap(rec) |- s.right + + removeConjunctions(removeImplies(seq)) + + /** + * Search the premises of a sequent to find one that is matched by a given formula. + * Performing the resulting substitution inside this premise gives the formula passed as argument. + * + * @param seq the sequent whose premises are references + * @param f the formula to match + * @param takenTVars any term variable in the template which cannot be substituted, i.e., treated as constant + * @param takenFVars any formula variable in the template which cannot be substituted, i.e., treated as constant + * @return a variable assignment such that substituting the variables of seq makes f appear in one of its premises. If no such substitution exists return None. + */ + private def searchPremises(seq: Sequent, f: Formula, takenTVars: Iterable[Variable] = Iterable.empty, takenFVars: Iterable[VariableFormula] = Iterable.empty): Option[(FormulaSubstitution, TermSubstitution)] = + seq.left.foldLeft[Option[(FormulaSubstitution, TermSubstitution)]](None)((assignment, cclPrem) => + assignment match + case None => matchFormula(f, cclPrem, takenTVars, takenFVars) + case _ => assignment + ) + + /** + * Search the premises of a sequent to find one that matches a given formula. + * Performing the resulting substitution inside this formula gives the premise. + * + * @param seq the sequent whose premises are being searched + * @param f the reference formula + * @param takenTVars any term variable in the template which cannot be substituted, i.e., treated as constant + * @param takenFVars any formula variable in the template which cannot be substituted, i.e., treated as constant + * @return a variable assignment such that substituting the variables of f makes f appear in one of seq's premises. If no such substitution exists return None. + */ + private def searchPremisesRev(seq: Sequent, f: Formula, takenTVars: Iterable[Variable] = Iterable.empty, takenFVars: Iterable[VariableFormula] = Iterable.empty): Option[(FormulaSubstitution, TermSubstitution)] = + seq.left.foldLeft[Option[(FormulaSubstitution, TermSubstitution)]](None)((assignment, cclPrem) => + assignment match + case None => matchFormula(cclPrem, f, takenTVars, takenFVars) + case _ => assignment + ) + + /** + * Substitute the variables of a fact with given assignments. + * + * @param proof the ongoing proof object in which the substitution happens + * @param fact the fact whose variables are being substituted + * @param fSubst the assignment for formula variables + * @param tSubst the assignment for term variables + */ + private def substitute(using _proof: lib.Proof)(fact: _proof.Fact, fSubst: FormulaSubstitution, tSubst: TermSubstitution): _proof.Fact = + fact.of(fSubst.toFSubstPair*).of(tSubst.toTSubstPair*) + + /** + * Applies on method with a varargs instead of a sequence. + */ + infix def on(premises: proof.Fact*)(bot: Sequent): proof.ProofTacticJudgement = on(premises.toSeq)(bot) + + + /** + * Executes the tactic. See class description for use cases. + * + * @param premises the facts that are applied to the theorem + * @param excluding the variables that are not to be substituted + * @param bot the sequent the user want to prove + */ + infix def on(premises: Seq[proof.Fact], excluding: Iterable[Variable | VariableFormula] = Set())(bot: Sequent): proof.ProofTacticJudgement = + + if thm == null then + proof.InvalidProofTactic("The theorem is null. Please check that it has been defined earlier in the proof.") + else if premises.exists(_ == null) then + proof.InvalidProofTactic("One of the premises is null. Please check that they all have been defined earlier in the proof.") + else + // STEP 0: Separate the variables and the variable formula in their respective sets + val (excludingTVars, excludingFVars) = excluding.foldLeft[(Set[Variable], Set[VariableFormula])](Set(), Set())((acc, v) => v match + case v: Variable => (acc._1 + v, acc._2) + case v: VariableFormula => (acc._1, acc._2 + v) + ) + + boundary{ + TacticSubproof { sp ?=> + + // STEP 1: Convert the given theorem, facts and sequent to their normal forms + val botNormalized: Sequent = normalForm(bot) + val thmNormalized: sp.Fact = lib.have(normalForm(thm.statement)) by Restate.from(thm) + val premisesNormalized = premises.map(p => lib.have(normalForm(p.statement)) by Restate.from(p)) + + // STEP 2: Unify the conclusion of the sequent the user want to prove and the conclusion + val thmAfterCclUnification: sp.Fact = + (botNormalized.right.isEmpty, thmNormalized.statement.right.isEmpty) match + case (true, true) => thmNormalized + case (true, false) => break(proof.InvalidProofTactic(s"The given theorem could not prove the sequent.")) + case (false, true) => break(proof.InvalidProofTactic(s"The given theorem could not prove the sequent.")) + case (false, false) => + val botCcl: Formula = botNormalized.right.head + val thmCcl: Formula = thmNormalized.statement.right.head + + matchFormula(botCcl, thmCcl, excludingTVars, excludingFVars) match + //Unification succeeded, subtitution can be performed + case Some((formulaCclAssignment, termCclAssignment)) => substitute(thmNormalized, formulaCclAssignment, termCclAssignment) + // STEP 2 failed + case None => break(proof.InvalidProofTactic(s"The conclusion of the goal and the theorem could not be unified.")) + + // STEP 3: Process each fact + val thmAfterPrems: sp.Fact = { + + premisesNormalized.foldLeft(lib.have(thmAfterCclUnification))((updatedThm, premNormalized) => { + + + val premCcl: Formula = premNormalized.statement.right.head + + // STEP 3.1: Unify the conclusion of the fact with a premise of the theorem + // Three possibilities: + // - the unification succeeded and the variables in the theorem are subtituted to match the conclusion of the fact; + // - if the unification did not succeeded, try the unification in the other direction, i.e. try to specify the fact + // instead of the theorem. If this works, make the susbtitution inside the fact; + // - both unifications do not succeed and the fact is dropped. + // TODO: add a warning when the fact is dropped + val conclusionsUnification: Option[(sp.Fact, sp.Fact)] = + searchPremises(updatedThm.statement, premCcl, excludingTVars, excludingFVars) match + case Some((fSubstAfterPrem, tSubstAfterPrem)) => Some((substitute(updatedThm, fSubstAfterPrem, tSubstAfterPrem), premNormalized)) + case None => + searchPremisesRev(updatedThm.statement, premCcl, excludingTVars, excludingFVars) match + case Some((fSubstAfterPrem, tSubstAfterPrem)) => Some((updatedThm, substitute(premNormalized, fSubstAfterPrem, tSubstAfterPrem))) + case None => None + + conclusionsUnification match + case Some(updatedThmAfterCclUnification, premAfterCclUnification) => + + // STEP 3.2: For each premise of the fact: + // - check if it is in the sequent the user want to prove. If yes, add it to the preconditions of the theorem + // using weakening; + // - else if it matches one of the premises of the theorem specify the theorem by using the appropriate sustitution. + // When performing this operation, the conclusion of the fact must be temporarily removed from the premises of the theorem + // to avoid buggy situations in case the fact is of the form p |- p; + // - else add the premise to the premises of the theorem using weakening. + val premCclAfterCclUnification: Formula = premAfterCclUnification.statement.right.head + + val updatedThmAfterWeakening: sp.Fact = + premAfterCclUnification.statement.left.foldLeft(updatedThmAfterCclUnification)((updatedThmDuringWeakening, premPrem) => { + if botNormalized.left.contains(premPrem) then + lib.have(updatedThmDuringWeakening.statement +<< premPrem) by Weakening(updatedThmDuringWeakening) + else + searchPremises(updatedThmDuringWeakening.statement - + substitute(updatedThmDuringWeakening, fSubstDuringWeakening, tSubstDuringWeakening) + case None => + lib.have(updatedThmDuringWeakening.statement +<< premPrem) by Weakening(updatedThmDuringWeakening) + }) + + + // STEP 3.3 Use cut to apply the conclusion of the fact to the theorem + // TODO: maybe throw a warning when the fact cannot be applied instead of making the proof crash + val cutStep: sp.ProofTacticJudgement = Cut(premAfterCclUnification, updatedThmAfterWeakening)(updatedThmAfterWeakening.statement - updatedThm + + }) + } + + // STEP 4: If some cases, after all these operations, the theorem can remain too general to prove the sequent. + // To avoid such situation, perform a last unification between the premises of the sequent and those + // of the theorem. + val thmAfterLastUnification: sp.Fact = botNormalized.left.foldLeft(thmAfterPrems)((updatedThm, premBot) => { + searchPremises(updatedThm.statement, premBot, excludingTVars, excludingFVars) match + case Some(fSubst, tSubst) => substitute(updatedThm, fSubst, tSubst) + case None => updatedThm + }) + + // STEP 5: Prove the sequent using the theorem obtained with the previous steps. Weakening is necessary in case + // additional preconditions that do not appear in the theorem are present in the sequent. + val finalStep: sp.ProofTacticJudgement = Weakening(thmAfterLastUnification)(bot) + if finalStep.isValid then + lib.have(finalStep) + else + break(proof.InvalidProofTactic(s"The given theorem could not prove the sequent.\n Deduced theorem: ${thmAfterLastUnification.statement}\n Expected: ${bot}")) + + + } + } +} + +/** + * Helper that creates an [[Apply]] object. + */ +object Apply { + def apply(using lib: Library, _proof: lib.Proof)(thm: _proof.Fact): Apply{val proof: _proof.type} = (new Apply(using lib, _proof)(thm)).asInstanceOf +} + +/** + * Helper to call the Apply tactic without arguments. When no facts are provided by the user, they can call + * {{{ + * Exact(thm) + * }}} + * as a shortcut for + * {{{ + * Apply(thm).on() + * }}} + * + * TODO: find an appropriate name for this tactic or replace it with Apply + */ +object Exact { + def apply(using lib: Library, _proof: lib.Proof)(thm: _proof.Fact)(bot: Sequent): _proof.ProofTacticJudgement = Apply(thm).on()(bot) +} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala b/lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala new file mode 100644 index 000000000..2d2b8219d --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala @@ -0,0 +1,262 @@ +package lisa.automation.kernel + +import lisa.automation.Tautology +import lisa.fol.FOLHelpers.* +import lisa.fol.FOL as F +import lisa.prooflib.BasicStepTactic.* +import lisa.prooflib.ProofTacticLib.{_, given} +import lisa.prooflib.SimpleDeducedSteps.* +import lisa.prooflib.* +import lisa.utils.K + +object CommonTactics { + + /** + *
+   * Γ |- ∃x. φ, Δ   Γ', φ, φ[y/x] |- x = y, Δ'
+   * -------------------------------------------
+   * Γ, Γ' |- ∃!x. φ, Δ, Δ'
+   * 
+ * + * This tactic separates the existence and the uniqueness proofs, which are often easier to prove independently, at + * the expense of brevity. + * + * @see [[RightExistsOne]]. + */ + object ExistenceAndUniqueness extends ProofTactic { + def withParameters(using lib: Library, proof: lib.Proof, om: OutputManager)(phi: F.Formula, x: F.Variable, y: F.Variable)(existence: proof.Fact, uniqueness: proof.Fact)( + bot: F.Sequent + ): proof.ProofTacticJudgement = { + val existenceSeq = proof.getSequent(existence) + val uniquenessSeq = proof.getSequent(uniqueness) + + lazy val substPhi = phi.substitute(x := y) + lazy val existenceFormula = F.∃(x, phi) + lazy val uniqueExistenceFormula = F.∃!(x, phi) + + // Checking that all formulas are present + if (x == y) { + proof.InvalidProofTactic("x and y can not be equal.") + } else if (!F.contains(existenceSeq.right, existenceFormula)) { + proof.InvalidProofTactic(s"Existence sequent conclusion does not contain ∃x. φ.") + } else if (!F.contains(uniquenessSeq.left, phi)) { + proof.InvalidProofTactic("Uniqueness sequent premises do not contain φ.") + } else if (!F.contains(uniquenessSeq.left, substPhi)) { + proof.InvalidProofTactic(s"Uniqueness sequent premises do not contain φ[y/x].") + } else if (!F.contains(uniquenessSeq.right, x === y) && !F.contains(uniquenessSeq.right, y === x)) { + proof.InvalidProofTactic(s"Uniqueness sequent conclusion does not contain x = y") + } else if (!F.contains(bot.right, uniqueExistenceFormula)) { + proof.InvalidProofTactic(s"Bottom sequent conclusion does not contain ∃!x. φ") + } + + // Checking pivots + else if (!F.isSameSet(existenceSeq.left ++ uniquenessSeq.left, bot.left + phi + substPhi)) { + proof.InvalidProofTactic("Could not infer correct left pivots.") + } else if (!F.isSameSet(existenceSeq.right ++ uniquenessSeq.right + uniqueExistenceFormula, bot.right + existenceFormula + (x === y))) { + proof.InvalidProofTactic("Could not infer correct right pivots.") + } else { + val gammaPrime = uniquenessSeq.left.filter(f => !F.isSame(f, phi) && !F.isSame(f, substPhi)) + + TacticSubproof { + // There's got to be a better way of importing have/thenHave/assume methods + // but I did not find one + + val forward = lib.have(phi |- ((x === y) ==> substPhi)) subproof { + lib.assume(phi) + lib.thenHave((x === y) |- substPhi) by RightSubstEq.withParametersSimple(List((x, y)), F.lambda(x, phi)) + lib.thenHave((x === y) ==> substPhi) by Restate + } + + for (f <- gammaPrime) { + lib.assume(f) + } + + val backward = lib.have(phi |- (substPhi ==> (x === y))) by Restate.from(uniqueness) + + lib.have(phi |- ((x === y) <=> substPhi)) by RightIff(forward, backward) + lib.thenHave(phi |- F.∀(y, (x === y) <=> substPhi)) by RightForall + lib.thenHave(phi |- F.∃(x, F.∀(y, (x === y) <=> substPhi))) by RightExists + lib.thenHave(F.∃(x, phi) |- F.∃(x, F.∀(y, (x === y) <=> substPhi))) by LeftExists + lib.thenHave(F.∃(x, phi) |- F.∃!(x, phi)) by RightExistsOne + + lib.have(bot) by Cut(existence, lib.lastStep) + } + } + } + + def apply(using lib: Library, proof: lib.Proof, om: OutputManager)(phi: F.Formula)(existence: proof.Fact, uniqueness: proof.Fact)(bot: F.Sequent): proof.ProofTacticJudgement = { + val existenceSeq = proof.getSequent(existence) + val uniquenessSeq = proof.getSequent(uniqueness) + + // Try to infer x from the premises + // Specifically, find variables in the correct quantifiers, common to all three sequents + val existsVars: Set[F.Variable] = existenceSeq.right.collect { + case F.BinderFormula(F.Exists, x, f) if F.isSame(f, phi) => x + } + if (existsVars.isEmpty) { + return proof.InvalidProofTactic("Missing existential quantifier in the existence sequent.") + } + + val commonVars = bot.right.collect { + case F.BinderFormula(F.ExistsOne, x, f) if F.isSame(f, phi) && existsVars.contains(x) => x + } + if (commonVars.size != 1) { + return proof.InvalidProofTactic("Could not infer correct variable x in quantifiers.") + } + + val x = commonVars.head + + // Infer y from the equalities in the uniqueness sequent + uniquenessSeq.right.collectFirst { + case F.AppliedPredicate(F.`equality`, Seq(`x`, (y: F.Variable))) if x != y && F.contains(uniquenessSeq.left, phi.substitute(x := y)) => + y + + case F.AppliedPredicate(F.`equality`, List(F.AppliedFunctional(y: F.Variable, _), F.AppliedFunctional(`x`, _))) if x != y && F.contains(uniquenessSeq.left, phi.substitute(x := y)) => + y + } match { + case Some(y) => ExistenceAndUniqueness.withParameters(phi, x, y)(existence, uniqueness)(bot) + case None => proof.InvalidProofTactic("Could not infer correct variable y in uniqueness sequent.") + } + } + } + + /** + *
+   *
+   * -------------  if f(xs) = The(y, P(y)) is a function definition
+   * |- P(f(xs))
+   * 
+ * Here `xs` is an arbitrary list of parameters. + * + * If `f(xs) = The(y, (φ ==> Q(y)) /\ (!φ ==> (y === t)))` is a conditional function definition, then: + *
+   *
+   * --------------
+   * φ |- Q(f(xs))
+   * 
+ */ + object Definition extends ProofTactic { + def apply(using lib: Library, proof: lib.Proof)(f: F.ConstantFunctionLabel[?], uniqueness: proof.Fact)(xs: F.Term*)(bot: F.Sequent): proof.ProofTacticJudgement = { + val expr = lib.getDefinition(f) match { + case Some(value: lib.FunctionDefinition[?]) => value + case _ => return proof.InvalidProofTactic("Could not get definition of function.") + } + val method: (F.ConstantFunctionLabel[?], proof.Fact) => Seq[F.Term] => F.Sequent => proof.ProofTacticJudgement = + expr.f.substituteUnsafe(expr.vars.zip(xs).toMap) match { + case F.AppliedConnector( + F.And, + Seq( + F.AppliedConnector(F.Implies, Seq(a, _)), + F.AppliedConnector(F.Implies, Seq(b, _)) + ) + ) if F.isSame(F.Neg(a), b) => + conditional(using lib, proof) + + case _ => unconditional(using lib, proof) + } + method(f, uniqueness)(xs)(bot) + } + + /** + *
+     *
+     * -------------  if f(xs) = The(y, P(y)) is a function definition
+     * |- P(f(xs))
+     * 
+ */ + def unconditional(using lib: Library, proof: lib.Proof)(f: F.ConstantFunctionLabel[?], uniqueness: proof.Fact)(xs: F.Term*)(bot: F.Sequent): proof.ProofTacticJudgement = { + lib.getDefinition(f) match { + case Some(definition: lib.FunctionDefinition[?]) => + if (bot.right.size != 1) { + return proof.InvalidProofTactic("Right-hand side of bottom sequent should contain only 1 formula.") + } + val y = definition.out + val vars = definition.vars + val fxs = f.applyUnsafe(xs) + + // Instantiate terms in the definition + val subst = vars.zip(xs).map(tup => tup._1 := tup._2) + val P = definition.f.substitute(subst*) + val expected = P.substitute(y := fxs) + if (!F.isSame(expected, bot.right.head)) { + return proof.InvalidProofTactic("Right-hand side of bottom sequent should be of the form P(f(xs)).") + } + + TacticSubproof { + lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst*)) + lib.thenHave((y === fxs) <=> P) by InstantiateForall(y) + lib.thenHave((fxs === fxs) <=> P.substitute(y := fxs)) by InstFunSchema(Map(y -> fxs)) + lib.thenHave(P.substitute(y := fxs)) by Restate + } + + case _ => proof.InvalidProofTactic("Could not get definition of function.") + } + } + + /** + *
+     *
+     * -------------- if f(xs) = The(y, (φ ==> Q(y)) /\ (!φ ==> R(y)))
+     * φ |- Q(f(xs))
+     * 
+ */ + def conditional(using lib: Library, proof: lib.Proof)(f: F.ConstantFunctionLabel[?], uniqueness: proof.Fact)(xs: F.Term*)(bot: F.Sequent): proof.ProofTacticJudgement = { + lib.getDefinition(f) match { + case Some(definition: lib.FunctionDefinition[?]) => + if (bot.right.size != 1) { + return proof.InvalidProofTactic("Right-hand side of bottom sequent should contain exactly 1 formula.") + } else if (bot.left.isEmpty) { + return proof.InvalidProofTactic("Left-hand side of bottom sequent should not be empty.") + } + val y = definition.out + val vars = definition.vars + + // Extract variable labels to instantiate them later in the proof + // val F.LambdaTermFormula(vars, _) = expr + // val instantiations: Seq[(F.SchematicTermLabel, F.LambdaTermTerm)] = vars.zip(xs.map(x => F.LambdaTermTerm(Seq(), x))) + + val subst = vars.zip(xs).map(tup => tup._1 := tup._2) + val P = definition.f.substitute(subst*) + // Instantiate terms in the definition + // val P = F.LambdaTermFormula(Seq(y), expr(xs)) + + // Unfold the conditional definition to find Q + val phi = F.And(bot.left.toSeq) + val Q: F.LambdaExpression[F.Term, F.Formula, 1] = P.body match { + case F.AppliedConnector( + F.And, + Seq( + F.AppliedConnector(F.Implies, Seq(a, f)), + F.AppliedConnector(F.Implies, Seq(b, g)) + ) + ) if F.isSame(F.Neg(a), b) => + if (F.isSame(a, phi)) F.lambda(y, f) + else if (F.isSame(b, phi)) F.lambda(y, g) + else return proof.InvalidProofTactic("Condition of definition is not satisfied.") + + case _ => + return proof.InvalidProofTactic("Definition is not conditional.") + } + + val fxs = f.applyUnsafe(xs) + + val expected = P.substitute(y := fxs) + if (!F.isSame(expected, bot.right.head)) { + return proof.InvalidProofTactic("Right-hand side of bottom sequent should be of the form Q(fxs).") + } + + TacticSubproof { + lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst*)) + lib.thenHave((y === fxs) <=> P) by InstantiateForall(y) + lib.thenHave((fxs === fxs) <=> P.substitute(y := fxs)) by InstFunSchema(Map(y -> fxs)) + lib.thenHave(P.substitute(y := fxs)) by Restate + lib.thenHave(phi ==> Q(fxs)) by Tautology + lib.thenHave(phi |- Q(fxs)) by Restate + } + + case _ => proof.InvalidProofTactic("Could not get definition of function.") + } + } + } + +} diff --git a/lisa-topology/src/main/scala/lisa/automation/Congruence.scala b/lisa-topology/src/main/scala/lisa/automation/Congruence.scala new file mode 100644 index 000000000..8c60de410 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/Congruence.scala @@ -0,0 +1,622 @@ +package lisa.automation +import lisa.fol.FOL.{*, given} +import lisa.prooflib.BasicStepTactic.* +import lisa.prooflib.ProofTacticLib.* +import lisa.prooflib.SimpleDeducedSteps.* +import lisa.prooflib.* +import lisa.utils.parsing.UnreachableException +import leo.datastructures.TPTP.CNF.AtomicFormula + +/** + * This tactic tries to prove a sequent by congruence. + * Consider the congruence closure of all terms and formulas in the sequent, with respect to all === and <=> left of the sequent. + * The sequent is provable by congruence if one of the following conditions is met: + * - The right side contains an equality s === t or equivalence a <=> b provable in the congruence closure. + * - The left side contains an negated equality !(s === t) or equivalence !(a <=> b) provable in the congruence closure. + * - There is a formula a on the left and b on the right such that a and b are congruent. + * - There are two formulas a and !b on the left such that a and b are congruent. + * - There are two formulas a and !b on the right such that a and b are congruent. + * - The sequent is Ol-valid without equality reasoning + * Note that complete congruence closure modulo OL is an open problem. + * + * The tactic uses an egraph datastructure to compute the congruence closure. + * The egraph itselfs relies on two underlying union-find datastructure, one for terms and one for formulas. + * The union-finds are equiped with an `explain` method that produces a path between any two elements in the same equivalence class. + * Each edge of the path can come from an external equality, or be the consequence of congruence. + * The tactic uses uses this path to produce needed proofs. + * + */ +object Congruence extends ProofTactic with ProofSequentTactic { + def apply(using lib: Library, proof: lib.Proof)(bot: Sequent): proof.ProofTacticJudgement = TacticSubproof { + import lib.* + + val egraph = new EGraphTerms() + egraph.addAll(bot.left) + egraph.addAll(bot.right) + + bot.left.foreach{ + case (left === right) => egraph.merge(left, right) + case (left <=> right) => egraph.merge(left, right) + case _ => () + } + + if isSameSequent(bot, ⊤) then + have(bot) by Restate + else if bot.left.exists { lf => + bot.right.exists { rf => + if egraph.idEq(lf, rf) then + val base = have(bot.left |- (bot.right + lf) ) by Restate + val eq = have(egraph.proveFormula(lf, rf, bot)) + val a = formulaVariable + have((bot.left + (lf <=> rf)) |- (bot.right) ) by RightSubstIff.withParametersSimple(List((lf, rf)), lambda(a, a))(base) + have(bot) by Cut(eq, lastStep) + true + else false + } || + bot.left.exists{ + case rf2 @ Neg(rf) if egraph.idEq(lf, rf)=> + val base = have((bot.left + !lf) |- bot.right ) by Restate + val eq = have(egraph.proveFormula(lf, rf, bot)) + val a = formulaVariable + have((bot.left + (lf <=> rf)) |- (bot.right) ) by LeftSubstIff.withParametersSimple(List((lf, rf)), lambda(a, !a))(base) + have(bot) by Cut(eq, lastStep) + true + case _ => false + } || { + lf match + case !(a === b) if egraph.idEq(a, b) => + have(egraph.proveTerm(a, b, bot)) + true + case !(a <=> b) if egraph.idEq(a, b) => + have(egraph.proveFormula(a, b, bot)) + true + case _ => false + } + + } then () + else if bot.right.exists { rf => + bot.right.exists{ + case lf2 @ Neg(lf) if egraph.idEq(lf, rf)=> + val base = have((bot.left) |- (bot.right + !rf) ) by Restate + val eq = have(egraph.proveFormula(lf, rf, bot)) + val a = formulaVariable + have((bot.left + (lf <=> rf)) |- (bot.right) ) by RightSubstIff.withParametersSimple(List((lf, rf)), lambda(a, !a))(base) + have(bot) by Cut(eq, lastStep) + true + case _ => false + } || { + rf match + case (a === b) if egraph.idEq(a, b) => + have(egraph.proveTerm(a, b, bot)) + true + case (a <=> b) if egraph.idEq(a, b) => + have(egraph.proveFormula(a, b, bot)) + true + case _ => false + } + } then () + else + return proof.InvalidProofTactic(s"No congruence found to show sequent\n $bot") + } + + +} + + +class UnionFind[T] { + // parent of each element, leading to its root. Uses path compression + val parent = scala.collection.mutable.Map[T, T]() + // original parent of each element, leading to its root. Does not use path compression. Used for explain. + val realParent = scala.collection.mutable.Map[T, (T, ((T, T), Boolean, Int))]() + //keep track of the rank (i.e. number of elements bellow it) of each element. Necessary to optimize union. + val rank = scala.collection.mutable.Map[T, Int]() + //tracks order of ancientness of unions. + var unionCounter = 0 + + /** + * add a new element to the union-find. + */ + def add(x: T): Unit = { + parent(x) = x + realParent(x) = (x, ((x, x), true, 0)) + rank(x) = 0 + } + + /** + * + * @param x the element whose parent we want to find + * @return the root of x + */ + def find(x: T): T = { + if parent(x) == x then + x + else + var root = x + while parent(root) != root do + root = parent(root) + var y = x + while parent(y) != root do + parent(y) = root + y = parent(y) + root + } + + /** + * Merges the classes of x and y + */ + def union(x: T, y: T): Unit = { + unionCounter += 1 + val xRoot = find(x) + val yRoot = find(y) + if (xRoot == yRoot) return + if (rank(xRoot) < rank(yRoot)) { + parent(xRoot) = yRoot + realParent(xRoot) = (yRoot, ((x, y), true, unionCounter)) + } else if (rank(xRoot) > rank(yRoot)) { + parent(yRoot) = xRoot + realParent(yRoot) = (xRoot, ((x, y), false, unionCounter)) + } else { + parent(yRoot) = xRoot + realParent(yRoot) = (xRoot, ((x, y), false, unionCounter)) + rank(xRoot) = rank(xRoot) + 1 + } + } + + private def getPathToRoot(x: T): List[T] = { + if x == find(x) then + List(x) + else + val next = realParent(x) + x :: getPathToRoot(next._1) + + } + + private def getExplanationFromTo(x:T, c: T): List[(T, ((T, T), Boolean, Int))] = { + if x == c then + List() + else + val next = realParent(x) + next :: getExplanationFromTo(next._1, c)} + + private def lowestCommonAncestor(x: T, y: T): Option[T] = { + val pathX = getPathToRoot(x) + val pathY = getPathToRoot(y) + pathX.find(pathY.contains) + } + + /** + * Returns a path from x to y made of pairs of elements (u, v) + * such that union(u, v) was called. + */ + def explain(x:T, y:T): Option[List[(T, T)]]= { + + if (x == y) then return Some(List()) + val lca = lowestCommonAncestor(x, y) + lca match + case None => None + case Some(lca) => + var max :((T, T), Boolean, Int) = ((x, x), true, 0) + var itX = x + while itX != lca do + val (next, ((u1, u2), b, c)) = realParent(itX) + if c > max._3 then + max = ((u1, u2), b, c) + itX = next + + var itY = y + while itY != lca do + val (next, ((u1, u2), b, c)) = realParent(itY) + if c > max._3 then + max = ((u1, u2), !b, c) + itY = next + + val u1 = max._1._1 + val u2 = max._1._2 + if max._2 then + Some(explain(x, u1).get ++ List((u1, u2)) ++ explain(u2, y).get) + else + Some(explain(x, u2).get ++ List((u1, u2)) ++ explain(u1, y).get) + } + + + /** + * Returns the set of all roots of all classes + */ + def getClasses: Set[T] = parent.keys.map(find).toSet + + /** + * Add all elements in the collection to the union-find + */ + def addAll(xs: Iterable[T]): Unit = xs.foreach(add) + +} + + +/////////////////////////////// +///////// E-graph ///////////// +/////////////////////////////// + +import scala.collection.mutable + +class EGraphTerms() { + + val termParentsT = mutable.Map[Term, mutable.Set[AppliedFunctional]]() + val termParentsF = mutable.Map[Term, mutable.Set[AppliedPredicate]]() + val termUF = new UnionFind[Term]() + + + val formulaParents = mutable.Map[Formula, mutable.Set[AppliedConnector]]() + val formulaUF = new UnionFind[Formula]() + + + def find(id: Term): Term = termUF.find(id) + def find(id: Formula): Formula = formulaUF.find(id) + + trait TermStep + case class TermExternal(between: (Term, Term)) extends TermStep + case class TermCongruence(between: (Term, Term)) extends TermStep + + trait FormulaStep + case class FormulaExternal(between: (Formula, Formula)) extends FormulaStep + case class FormulaCongruence(between: (Formula, Formula)) extends FormulaStep + + val termProofMap = mutable.Map[(Term, Term), TermStep]() + val formulaProofMap = mutable.Map[(Formula, Formula), FormulaStep]() + + def explain(id1: Term, id2: Term): Option[List[TermStep]] = { + val steps = termUF.explain(id1, id2) + steps.map(_.foldLeft((id1, List[TermStep]())) { + case ((prev, acc), step) => + termProofMap(step) match + case s @ TermExternal((l, r)) => + if l == prev then + (r, s :: acc) + else if r == prev then + (l, TermExternal(r, l) :: acc) + else throw new Exception("Invalid proof recovered: It is not a chain") + case s @ TermCongruence((l, r)) => + if l == prev then + (r, s :: acc) + else if r == prev then + (l, TermCongruence(r, l) :: acc) + else throw new Exception("Invalid proof recovered: It is not a chain") + + }._2.reverse) + } + + def explain(id1: Formula, id2: Formula): Option[List[FormulaStep]] = { + val steps = formulaUF.explain(id1, id2) + steps.map(_.foldLeft((id1, List[FormulaStep]())) { + case ((prev, acc), step) => + formulaProofMap(step) match + case s @ FormulaExternal((l, r)) => + if l == prev then + (r, s :: acc) + else if r == prev then + (l, FormulaExternal(r, l) :: acc) + else throw new Exception("Invalid proof recovered: It is not a chain") + case s @ FormulaCongruence((l, r)) => + if l == prev then + (r, s :: acc) + else if r == prev then + (l, FormulaCongruence(r, l) :: acc) + else throw new Exception("Invalid proof recovered: It is not a chain") + + }._2.reverse) + } + + + def makeSingletonEClass(node:Term): Term = { + termUF.add(node) + termParentsT(node) = mutable.Set() + termParentsF(node) = mutable.Set() + node + } + def makeSingletonEClass(node:Formula): Formula = { + formulaUF.add(node) + formulaParents(node) = mutable.Set() + node + } + + def idEq(id1: Term, id2: Term): Boolean = find(id1) == find(id2) + def idEq(id1: Formula, id2: Formula): Boolean = find(id1) == find(id2) + + + + def canonicalize(node: Term): Term = node match + case AppliedFunctional(label, args) => + AppliedFunctional(label, args.map(t => find(t))) + case _ => node + + + def canonicalize(node: Formula): Formula = { + node match + case AppliedPredicate(label, args) => AppliedPredicate(label, args.map(find)) + case AppliedConnector(label, args) => AppliedConnector(label, args.map(find)) + case node => node + } + + def add(node: Term): Term = + if termUF.parent.contains(node) then return node + makeSingletonEClass(node) + codes(node) = codes.size + node match + case node @ AppliedFunctional(_, args) => + args.foreach(child => + add(child) + termParentsT(find(child)).add(node) + ) + case _ => () + termSigs(canSig(node)) = node + node + + def add(node: Formula): Formula = + if formulaUF.parent.contains(node) then return node + makeSingletonEClass(node) + node match + case node @ AppliedPredicate(_, args) => + args.foreach(child => + add(child) + termParentsF(find(child)).add(node) + ) + node + case node @ AppliedConnector(_, args) => + args.foreach(child => + add(child) + formulaParents(find(child)).add(node) + ) + node + case _ => node + + def addAll(nodes: Iterable[Term|Formula]): Unit = + nodes.foreach{ + case node: Term => add(node) + case node: Formula => add(node) + } + + + + + def merge(id1: Term, id2: Term): Unit = { + mergeWithStep(id1, id2, TermExternal((id1, id2))) + } + def merge(id1: Formula, id2: Formula): Unit = { + mergeWithStep(id1, id2, FormulaExternal((id1, id2))) + } + + type Sig = (TermLabel[?]|Term, List[Int]) + val termSigs = mutable.Map[Sig, Term]() + val codes = mutable.Map[Term, Int]() + + def canSig(node: Term): Sig = node match + case AppliedFunctional(label, args) => + (label, args.map(a => codes(find(a))).toList) + case _ => (node, List()) + + protected def mergeWithStep(id1: Term, id2: Term, step: TermStep): Unit = { + if find(id1) == find(id2) then () + else + termProofMap((id1, id2)) = step + val parentsT1 = termParentsT(find(id1)) + val parentsF1 = termParentsF(find(id1)) + + val parentsT2 = termParentsT(find(id2)) + val parentsF2 = termParentsF(find(id2)) + val preSigs : Map[Term, Sig] = parentsT1.map(t => (t, canSig(t))).toMap + codes(find(id2)) = codes(find(id1)) //assume parents(find(id1)) >= parents(find(id2)) + termUF.union(id1, id2) + val newId = find(id1) + + val formulaSeen = mutable.Map[Formula, AppliedPredicate]() + var formWorklist = List[(Formula, Formula, FormulaStep)]() + var termWorklist = List[(Term, Term, TermStep)]() + + parentsT2.foreach { + case pTerm: AppliedFunctional => + val canonicalPTerm = canSig(pTerm) + if termSigs.contains(canonicalPTerm) then + val qTerm = termSigs(canonicalPTerm) + termWorklist = (pTerm, qTerm, TermCongruence((pTerm, qTerm))) :: termWorklist + else + termSigs(canonicalPTerm) = pTerm + } + (parentsF2 ++ parentsF1).foreach { + case pFormula: AppliedPredicate => + val canonicalPFormula = canonicalize(pFormula) + if formulaSeen.contains(canonicalPFormula) then + val qFormula = formulaSeen(canonicalPFormula) + formWorklist = (pFormula, qFormula, FormulaCongruence((pFormula, qFormula))) :: formWorklist + else + formulaSeen(canonicalPFormula) = pFormula + } + termParentsT(newId) = termParentsT(id1) + termParentsT(newId).addAll(termParentsT(id2)) + termParentsF(newId) = formulaSeen.values.to(mutable.Set) + formWorklist.foreach { case (l, r, step) => mergeWithStep(l, r, step) } + termWorklist.foreach { case (l, r, step) => mergeWithStep(l, r, step) } + } + + protected def mergeWithStep(id1: Formula, id2: Formula, step: FormulaStep): Unit = + if find(id1) == find(id2) then () + else + formulaProofMap((id1, id2)) = step + val newparents = formulaParents(find(id1)) ++ formulaParents(find(id2)) + formulaUF.union(id1, id2) + val newId = find(id1) + + val formulaSeen = mutable.Map[Formula, AppliedConnector]() + var formWorklist = List[(Formula, Formula, FormulaStep)]() + + newparents.foreach { + case pFormula: AppliedConnector => + val canonicalPFormula = canonicalize(pFormula) + if formulaSeen.contains(canonicalPFormula) then + val qFormula = formulaSeen(canonicalPFormula) + formWorklist = (pFormula, qFormula, FormulaCongruence((pFormula, qFormula))) :: formWorklist + //mergeWithStep(pFormula, qFormula, FormulaCongruence((pFormula, qFormula))) + else + formulaSeen(canonicalPFormula) = pFormula + } + formulaParents(newId) = formulaSeen.values.to(mutable.Set) + formWorklist.foreach { case (l, r, step) => mergeWithStep(l, r, step) } + + + def proveTerm(using lib: Library, proof: lib.Proof)(id1: Term, id2:Term, base: Sequent): proof.ProofTacticJudgement = + TacticSubproof { proveInnerTerm(id1, id2, base) } + + def proveInnerTerm(using lib: Library, proof: lib.Proof)(id1: Term, id2:Term, base: Sequent): Unit = { + import lib.* + val steps = explain(id1, id2) + steps match { + case None => throw new Exception("No proof found in the egraph") + case Some(steps) => + if steps.isEmpty then have(base.left |- (base.right + (id1 === id2))) by Restate + steps.foreach { + case TermExternal((l, r)) => + val goalSequent = base.left |- (base.right + (id1 === r)) + if l == id1 then + have(goalSequent) by Restate + else + val x = freshVariable(id1) + have(goalSequent) by RightSubstEq.withParametersSimple(List((l, r)), lambda(x, id1 === x))(lastStep) + case TermCongruence((l, r)) => + val prev = if id1 != l then lastStep else null + val leqr = have(base.left |- (base.right + (l === r))) subproof { sp ?=> + (l, r) match + case (AppliedFunctional(labell, argsl), AppliedFunctional(labelr, argsr)) if labell == labelr && argsl.size == argsr.size => + var freshn = freshId((l.freeVariables ++ r.freeVariables).map(_.id), "n").no + val ziped = (argsl zip argsr) + var zip = List[(Term, Term)]() + var children = List[Term]() + var vars = List[Variable]() + var steps = List[(Formula, sp.ProofStep)]() + ziped.reverse.foreach { (al, ar) => + if al == ar then children = al :: children + else { + val x = Variable(Identifier("n", freshn)) + freshn = freshn + 1 + children = x :: children + vars = x :: vars + steps = (al === ar, have(proveTerm(al, ar, base))) :: steps + zip = (al, ar) :: zip + } + } + have(base.left |- (base.right + (l === l))) by Restate + val eqs = zip.map((l, r) => l === r) + val goal = have((base.left ++ eqs) |- (base.right + (l === r))).by.bot + have((base.left ++ eqs) |- (base.right + (l === r))) by RightSubstEq.withParametersSimple(zip, lambda(vars, l === labelr.applyUnsafe(children)))(lastStep) + steps.foreach { s => + have( + if s._2.bot.left.contains(s._1) then lastStep.bot else lastStep.bot -<< s._1 + ) by Cut(s._2, lastStep) + } + case _ => + println(s"l: $l") + println(s"r: $r") + throw UnreachableException + + } + if id1 != l then + val goalSequent = base.left |- (base.right + (id1 === r)) + val x = freshVariable(id1) + have(goalSequent +<< (l === r)) by RightSubstEq.withParametersSimple(List((l, r)), lambda(x, id1 === x))(prev) + have(goalSequent) by Cut(leqr, lastStep) + } + } + } + + def proveFormula(using lib: Library, proof: lib.Proof)(id1: Formula, id2:Formula, base: Sequent): proof.ProofTacticJudgement = + TacticSubproof { proveInnerFormula(id1, id2, base) } + + def proveInnerFormula(using lib: Library, proof: lib.Proof)(id1: Formula, id2:Formula, base: Sequent): Unit = { + import lib.* + val steps = explain(id1, id2) + steps match { + case None => throw new Exception("No proof found in the egraph") + case Some(steps) => + if steps.isEmpty then have(base.left |- (base.right + (id1 <=> id2))) by Restate + steps.foreach { + case FormulaExternal((l, r)) => + val goalSequent = base.left |- (base.right + (id1 <=> r)) + if l == id1 then + have(goalSequent) by Restate + else + val x = freshVariableFormula(id1) + have(goalSequent) by RightSubstIff.withParametersSimple(List((l, r)), lambda(x, id1 <=> x))(lastStep) + case FormulaCongruence((l, r)) => + val prev = if id1 != l then lastStep else null + val leqr = have(base.left |- (base.right + (l <=> r))) subproof { sp ?=> + (l, r) match + case (AppliedConnector(labell, argsl), AppliedConnector(labelr, argsr)) if labell == labelr && argsl.size == argsr.size => + var freshn = freshId((l.freeVariableFormulas ++ r.freeVariableFormulas).map(_.id), "n").no + val ziped = (argsl zip argsr) + var zip = List[(Formula, Formula)]() + var children = List[Formula]() + var vars = List[VariableFormula]() + var steps = List[(Formula, sp.ProofStep)]() + ziped.reverse.foreach { (al, ar) => + if al == ar then children = al :: children + else { + val x = VariableFormula(Identifier("n", freshn)) + freshn = freshn + 1 + children = x :: children + vars = x :: vars + steps = (al <=> ar, have(proveFormula(al, ar, base))) :: steps + zip = (al, ar) :: zip + } + } + have(base.left |- (base.right + (l <=> l))) by Restate + val eqs = zip.map((l, r) => l <=> r) + val goal = have((base.left ++ eqs) |- (base.right + (l <=> r))).by.bot + have((base.left ++ eqs) |- (base.right + (l <=> r))) by RightSubstIff.withParametersSimple(zip, lambda(vars, l <=> labelr.applyUnsafe(children)))(lastStep) + steps.foreach { s => + have( + if s._2.bot.left.contains(s._1) then lastStep.bot else lastStep.bot -<< s._1 + ) by Cut(s._2, lastStep) + } + + case (AppliedPredicate(labell, argsl), AppliedPredicate(labelr, argsr)) if labell == labelr && argsl.size == argsr.size => + var freshn = freshId((l.freeVariableFormulas ++ r.freeVariableFormulas).map(_.id), "n").no + val ziped = (argsl zip argsr) + var zip = List[(Term, Term)]() + var children = List[Term]() + var vars = List[Variable]() + var steps = List[(Formula, sp.ProofStep)]() + ziped.reverse.foreach { (al, ar) => + if al == ar then children = al :: children + else { + val x = Variable(Identifier("n", freshn)) + freshn = freshn + 1 + children = x :: children + vars = x :: vars + steps = (al === ar, have(proveTerm(al, ar, base))) :: steps + zip = (al, ar) :: zip + } + } + have(base.left |- (base.right + (l <=> l))) by Restate + val eqs = zip.map((l, r) => l === r) + val goal = have((base.left ++ eqs) |- (base.right + (l <=> r))).by.bot + have((base.left ++ eqs) |- (base.right + (l <=> r))) by RightSubstEq.withParametersSimple(zip, lambda(vars, l <=> labelr.applyUnsafe(children)))(lastStep) + steps.foreach { s => + have( + if s._2.bot.left.contains(s._1) then lastStep.bot else lastStep.bot -<< s._1 + ) by Cut(s._2, lastStep) + } + case _ => + println(s"l: $l") + println(s"r: $r") + throw UnreachableException + + } + if id1 != l then + val goalSequent = base.left |- (base.right + (id1 <=> r)) + val x = freshVariableFormula(id1) + have(goalSequent +<< (l <=> r)) by RightSubstIff.withParametersSimple(List((l, r)), lambda(x, id1 <=> x))(prev) + have(goalSequent) by Cut(leqr, lastStep) + + } + } + } + + +} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/automation/Substitution.scala b/lisa-topology/src/main/scala/lisa/automation/Substitution.scala new file mode 100644 index 000000000..87eab43cd --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/Substitution.scala @@ -0,0 +1,641 @@ +package lisa.automation +import lisa.fol.FOL as F +import lisa.kernel.proof.RunningTheory +import lisa.kernel.proof.SCProof +import lisa.kernel.proof.SequentCalculus +import lisa.prooflib.BasicStepTactic.* +import lisa.prooflib.ProofTacticLib.{_, given} +import lisa.prooflib.* +import lisa.utils.FOLPrinter +import lisa.utils.K +import lisa.utils.UserLisaException +import lisa.utils.parsing.FOLPrinter +import lisa.utils.unification.UnificationUtils +import lisa.utils.unification.UnificationUtils.getContextFormulaSet + +import scala.annotation.nowarn +import scala.collection.mutable.{Map as MMap} + +import F.{*, given} +import F.|- + +object Substitution { + def validRule(using lib: lisa.prooflib.Library, proof: lib.Proof)(r: (proof.Fact | F.Formula | lib.JUSTIFICATION)): Boolean = + r match { + case F.equality(_, _) => true + case F.Iff(_, _) => true + case _: Formula => false + case j: lib.JUSTIFICATION => j.statement.right.size == 1 && validRule(j.statement.right.head) + case f: proof.Fact @unchecked => proof.sequentOfFact(f).right.size == 1 && validRule(proof.sequentOfFact(f).right.head) + // case j: RunningTheory#Justification => + // proof.sequentOfFact(j.asInstanceOf[lib.theory.Justification]).right.size == 1 && validRule(proof.sequentOfFact(j.asInstanceOf[lib.theory.Justification]).right.head) + } + + object ApplyRules extends ProofTactic { + + def apply(using lib: lisa.prooflib.Library, proof: lib.Proof)(substitutions: (proof.Fact | F.Formula | lib.JUSTIFICATION)*)( + premise: proof.Fact + )(bot: F.Sequent): proof.ProofTacticJudgement = { + // figure out instantiations for rules + // takes a premise + val premiseSequent: F.Sequent = proof.getSequent(premise) + + // make sure substitutions are all valid + val violatingSubstitutions = substitutions.collect { + case f : proof.Fact @unchecked if !validRule(f) => proof.sequentOfFact(f) + case j: lib.JUSTIFICATION if !validRule(j) => j.statement + } + + val violatingFormulas = substitutions.collect { + case f: F.Formula if !validRule(f) => f + } + + if (!violatingSubstitutions.isEmpty) + // return error + proof.InvalidProofTactic("Substitution rules must have a single equality or equivalence on the right-hand side. Violating sequents passed:\n" + violatingSubstitutions.zipWithIndex.map { + (s, i) => + s"${i + 1}. ${s.toString}" + }) + else if (!violatingFormulas.isEmpty) + proof.InvalidProofTactic("Substitution rules must be equalities or equivalences. Violating formulas passed:\n" + violatingFormulas.zipWithIndex.map { (s, i) => + s"${i + 1}. ${s.toString}" + }) + else { + // proceed as usual + + // maintain a list of where subtitutions come from + val sourceOf: MMap[(F.Formula, F.Formula) | (F.Term, F.Term), proof.Fact] = MMap() + val takenTermVars: Set[lisa.fol.FOL.Variable] = + premiseSequent.left.flatMap(_.freeVariables).toSet union substitutions.collect { case f: F.Formula => f.freeVariables.toSet }.foldLeft(Set.empty)(_.union(_)) + val takenFormulaVars: Set[lisa.fol.FOL.VariableFormula] = premiseSequent.left.flatMap(_.freeVariableFormulas).toSet union substitutions + .collect { case f: F.Formula => f.freeVariableFormulas.toSet } + .foldLeft(Set.empty)(_.union(_)) // TODO: should this just be the LHS of the premise sequent instead? + + var freeEqualitiesPre = List[(F.Term, F.Term)]() + var confinedEqualitiesPre = List[(F.Term, F.Term)]() + var freeIffsPre = List[(F.Formula, F.Formula)]() + var confinedIffsPre = List[(F.Formula, F.Formula)]() + + def updateSource(t: (F.Formula, F.Formula) | (F.Term, F.Term), f: proof.Fact) = { + sourceOf.update(t, f) + sourceOf.update(t.swap.asInstanceOf[(F.Formula, F.Formula) | (F.Term, F.Term)], f) + } + + // collect substitutions into the right buckets + substitutions.foreach { + case f: F.Formula => + f match { + case F.AppliedPredicate(F.equality, Seq(l, r)) => + confinedEqualitiesPre = (l, r) :: confinedEqualitiesPre + case F.AppliedConnector(F.Iff, Seq(l, r)) => + confinedIffsPre = (l, r) :: confinedIffsPre + case _ => () + } + case j: lib.JUSTIFICATION => + j.statement.right.head match { + case F.AppliedPredicate(F.equality, Seq(l, r)) => + updateSource((l, r), j) + freeEqualitiesPre = (l, r) :: freeEqualitiesPre + case F.AppliedConnector(F.Iff, Seq(l, r)) => + updateSource((l, r), j) + freeIffsPre = (l, r) :: freeIffsPre + case _ => () + } + case f: proof.Fact @unchecked => + proof.sequentOfFact(f).right.head match { + case F.AppliedPredicate(F.equality, Seq(l, r)) => + updateSource((l, r), f) + confinedEqualitiesPre = (l, r) :: confinedEqualitiesPre + case F.AppliedConnector(F.Iff, Seq(l, r)) => + updateSource((l, r), f) + confinedIffsPre = (l, r) :: confinedIffsPre + case _ => () + } + } + + // get the original and swapped versions + val freeEqualities: List[(F.Term, F.Term)] = freeEqualitiesPre ++ freeEqualitiesPre.map(_.swap) + val confinedEqualities: List[(F.Term, F.Term)] = confinedEqualitiesPre ++ confinedEqualitiesPre.map(_.swap) + val freeIffs: List[(F.Formula, F.Formula)] = freeIffsPre ++ freeIffsPre.map(_.swap) + val confinedIffs: List[(F.Formula, F.Formula)] = confinedIffsPre ++ confinedIffsPre.map(_.swap) + + val filteredPrem: Seq[F.Formula] = (premiseSequent.left filter { + case F.AppliedPredicate(F.equality, Seq(l, r)) if freeEqualities.contains((l, r)) || confinedEqualities.contains((l, r)) => false + case F.AppliedConnector(F.Iff, Seq(l, r)) if freeIffs.contains((l, r)) || confinedIffs.contains((l, r)) => false + case _ => true + }).toSeq + + val filteredBot: Seq[F.Formula] = (bot.left filter { + case F.AppliedPredicate(F.equality, Seq(l, r)) if freeEqualities.contains((l, r)) || confinedEqualities.contains((l, r)) => false + case F.AppliedConnector(F.Iff, Seq(l, r)) if freeIffs.contains((l, r)) || confinedIffs.contains((l, r)) => false + case _ => true + }).toSeq + + // construct the right instantiations + lazy val leftContextsOpt: Option[Seq[UnificationUtils.FormulaRewriteLambda]] = getContextFormulaSet( + filteredPrem, + filteredBot, + freeEqualities, + freeIffs, + confinedEqualities, + takenTermVars, + confinedIffs, + takenFormulaVars + ) + + lazy val rightContextsOpt: Option[Seq[UnificationUtils.FormulaRewriteLambda]] = getContextFormulaSet( + premiseSequent.right.toSeq, + bot.right.toSeq, + freeEqualities, + freeIffs, + confinedEqualities, + takenTermVars, + confinedIffs, + takenFormulaVars + ) + + lazy val rightPairs = premiseSequent.right zip premiseSequent.right.map(x => + bot.right.find(y => + UnificationUtils + .getContextFormula( + x, + y, + freeEqualities, + freeIffs, + confinedEqualities, + takenTermVars, + confinedIffs, + takenFormulaVars + ) + .isDefined + ) + ) + + lazy val leftPairs = filteredPrem zip filteredPrem.map(x => + filteredBot.find(y => + UnificationUtils + .getContextFormula( + x, + y, + freeEqualities, + freeIffs, + confinedEqualities, + takenTermVars, + confinedIffs, + takenFormulaVars + ) + .isDefined + ) + ) + + lazy val violatingFormulaLeft = leftPairs.find(_._2.isEmpty) + lazy val violatingFormulaRight = rightPairs.find(_._2.isEmpty) + + if (leftContextsOpt.isEmpty) + proof.InvalidProofTactic(s"Could not rewrite LHS of premise into conclusion with given substitutions.\nViolating Formula: ${violatingFormulaLeft.get}") + else if (rightContextsOpt.isEmpty) + proof.InvalidProofTactic(s"Could not rewrite RHS of premise into conclusion with given substitutions.\nViolating Formula: ${violatingFormulaRight.get}") + else { + // actually construct proof + TacticSubproof { + + def eq(rule: (Term, Term)) = AppliedPredicate(equality, Seq(rule._1, rule._2)) + def iff(rule: (Formula, Formula)) = AppliedConnector(Iff, Seq(rule._1, rule._2)) + + def eqSource(rule: (Term, Term)) = lib.have(eq(rule) |- eq(rule)) by SimpleDeducedSteps.Restate + def iffSource(rule: (Formula, Formula)) = lib.have(iff(rule) |- iff(rule)) by SimpleDeducedSteps.Restate + val leftContexts: Seq[UnificationUtils.FormulaRewriteLambda] = leftContextsOpt.get // remove the options + val rightContexts: Seq[UnificationUtils.FormulaRewriteLambda] = rightContextsOpt.get // remove the options + + val leftBody = AppliedConnector(And, leftContexts.map(f => f.body)) + + val defaultLeft = UnificationUtils.FormulaRewriteLambda(body = leftBody) + + val leftContextReduced = leftContexts.foldLeft(defaultLeft) { (f, s) => + UnificationUtils.FormulaRewriteLambda( + termRules = f.termRules ++ s.termRules, + formulaRules = f.formulaRules ++ s.formulaRules, + leftBody + ) + } + + val rightBody = AppliedConnector(Or, rightContexts.map(f => f.body)) + + val defaultRight = UnificationUtils.FormulaRewriteLambda(body = rightBody) + + val rightContextReduced = rightContexts.foldLeft(defaultRight) { (f, s) => + UnificationUtils.FormulaRewriteLambda( + termRules = f.termRules ++ s.termRules, + formulaRules = f.formulaRules ++ s.formulaRules, + rightBody + ) + } + + // find the justifications for each rule, or generate them, as required + val leftDischarges = + leftContextReduced.termRules.map { case (_, (rule, subst)) => + sourceOf.get(rule) match { + case Some(f: proof.Fact) => + f.of(subst.toSeq.map((l, r) => (l := r))*) + // case Some(j: lib.theory.Justification) => + // j.of(subst.toSeq.map((l, r) => (l, lambda(Seq(), r))): _*) + case _ => + eqSource(rule).of() + } + } ++ + leftContextReduced.formulaRules.map { case (_, (rule, subst)) => + sourceOf.get(rule) match { + case Some(f: proof.Fact) => + f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r))*) + // case Some(j: lib.theory.Justification) => + // j.of(subst._1.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))) ++ subst._2.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))): _*) + case _ => + iffSource(rule).of() + } + } + val rightDischarges = + rightContextReduced.termRules.map { case (_, (rule, subst)) => + sourceOf.get(rule) match { + case Some(f: proof.Fact) => + f.of(subst.toSeq.map((l, r) => (l := r))*) + // case Some(j: lib.theory.Justification) => + // j.of(subst.toSeq.map((l, r) => (l, lambda(Seq(), r))): _*) + case None => + eqSource(rule).of() + } + } ++ + rightContextReduced.formulaRules.map { case (_, (rule, subst)) => + sourceOf.get(rule) match { + case Some(f: proof.Fact) => + f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r))*) + // case Some(j: lib.theory.Justification) => + // j.of(subst._1.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))) ++ subst._2.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))): _*) + case None => + iffSource(rule).of() + } + } + + val discharges = leftDischarges ++ rightDischarges + // ------------------- + // LEFT SUBSTITUTIONS + // ------------------- + val nextSequent = { + // we have a lambda like λx. Λp. body + // where the p are formula variables, and the x are term variables + val ctx = leftContextReduced + + val termVars = ctx.termRules.map(_._1) + + val termInputs = ctx.termRules.map { case (_, (rule: (Term, Term), subst: UnificationUtils.TermSubstitution)) => + ( + rule._1.substituteUnsafe2(subst), + rule._2.substituteUnsafe2(subst) + ) + } + + lazy val (termInputsL, termInputsR) = (termInputs.map(_._1), termInputs.map(_._2)) + + val formulaVars = ctx.formulaRules.map(_._1) + + val formulaInputs = ctx.formulaRules.map { case (_, (rule, subst)) => + ( + rule._1.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1), + rule._2.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1) + ) + } + val (formulaInputsL, formulaInputsR) = (formulaInputs.map(_._1), formulaInputs.map(_._2)) + + // get premise into the right form + val prem = AppliedConnector(And, filteredPrem.toSeq) |- AppliedConnector(Or, premiseSequent.right.toSeq) + val eqs = termInputs.map(eq(_)) + val iffs = formulaInputs.map(iff(_)) + val premiseWithSubst = prem ++<< (eqs |- ()) ++<< (iffs |- ()) + lib.have(premiseWithSubst) by BasicStepTactic.Weakening(premise) + + // left === + val eqSubst = Map((termVars zip termInputsR)*) + val formSubstL = Map((formulaVars zip formulaInputsL)*) + val lhsAfterEq = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstL) + val sequentAfterEqPre = lhsAfterEq |- premiseWithSubst.right + val sequentAfterEq = sequentAfterEqPre ++<< (eqs |- ()) ++<< (iffs |- ()) + + // this uses the "lambda" (λx. Λp. body) (p = left formulas) + lib.thenHave(sequentAfterEq) by BasicStepTactic.LeftSubstEq.withParametersSimple(termInputs.toList, lambda(termVars, ctx.body.substituteUnsafe2(formSubstL))) + + // left <=> + val formSubstR = Map((formulaVars zip formulaInputsR)*) + val lhsAfterIff = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstR) + val sequentAfterIffPre = lhsAfterIff |- sequentAfterEq.right + val sequentAfterIff = sequentAfterIffPre ++<< (eqs |- ()) ++<< (iffs |- ()) + + // this uses the "lambda" (λx. Λp. body) (x = right terms) + lib.thenHave(sequentAfterIff) by BasicStepTactic.LeftSubstIff.withParametersSimple(formulaInputs.toList, lambda(formulaVars, ctx.body.substituteUnsafe2(eqSubst))) + sequentAfterIff + } + + // ------------------- + // RIGHT SUBSTITUTIONS + // ------------------- + val finalSequent = { + // we have a lambda like λx. Λp. body + // where the p are formula variables, and the x are term variables + val ctx = rightContextReduced + + val termVars = ctx.termRules.map(_._1) + + val termInputs = ctx.termRules.map { case (_, (rule, subst)) => + ( + rule._1.substituteUnsafe2(subst), + rule._2.substituteUnsafe2(subst) + ) + } + + lazy val (termInputsL, termInputsR) = (termInputs.map(_._1), termInputs.map(_._2)) + + val formulaVars = ctx.formulaRules.map(_._1) + + val formulaInputs = ctx.formulaRules.map { case (_, (rule, subst)) => + ( + rule._1.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1), + rule._2.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1) + ) + } + val (formulaInputsL, formulaInputsR) = (formulaInputs.map(_._1), formulaInputs.map(_._2)) + + // get premise into the right form + val prem = nextSequent + val eqs = termInputs.map(eq(_)) + val iffs = formulaInputs.map(iff(_)) + val premiseWithSubst = prem ++<< (eqs |- ()) ++<< (iffs |- ()) + lib.thenHave(premiseWithSubst) by BasicStepTactic.Weakening + + // right === + val eqSubst = Map((termVars zip termInputsR)*) + val formSubstL = Map((formulaVars zip formulaInputsL)*) + val rhsAfterEq = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstL) + val sequentAfterEqPre = premiseWithSubst.left |- rhsAfterEq + val sequentAfterEq = sequentAfterEqPre ++<< (eqs |- ()) ++<< (iffs |- ()) + + // this uses the "lambda" (λx. Λp. body) (p = left formulas) + lib.thenHave(sequentAfterEq) by BasicStepTactic.RightSubstEq.withParametersSimple(termInputs.toList, lambda(termVars, ctx.body.substituteUnsafe2(formSubstL))) + + // right <=> + val formSubstR = Map((formulaVars zip formulaInputsR)*) + val rhsAfterIff = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstR) + val sequentAfterIffPre = sequentAfterEq.left |- rhsAfterIff + val sequentAfterIff = sequentAfterIffPre ++<< (eqs |- ()) ++<< (iffs |- ()) + + // this uses the "lambda" (λx. Λp. body) (x = right terms) + lib.thenHave(sequentAfterIff) by BasicStepTactic.RightSubstIff.withParametersSimple(formulaInputs.toList, lambda(formulaVars, ctx.body.substituteUnsafe2(eqSubst))) + + } + // discharge any assumptions + + // custom discharge + // invariant: all facts are known to have only one formula in their RHS + discharges.foreach { f => + lib.thenHave(lib.lastStep.bot +<< f.result.right.head) by BasicStepTactic.Weakening // in case of double discharges, add the formula back in + lib.have(lib.lastStep.bot - isSameTerm(t, s._2)) + if (eq.nonEmpty) (eq.get._1, true) + else { + val induct = condflat(t.args.map(te => findSubterm2(te, subs))) + if (!induct._2) (t, false) + else + (t.label.applySeq(induct._1), true) + + } + + } + private def findSubterm2(f: Formula, subs: Seq[(Variable, Term)]): (Formula, Boolean) = { + f match { + case f: VariableFormula => (f, false) + case f: ConstantFormula => (f, false) + case AppliedPredicate(label, args) => + val induct = condflat(args.map(findSubterm2(_, subs))) + if (!induct._2) (f, false) + else (AppliedPredicate(label, induct._1), true) + case AppliedConnector(label, args) => + val induct = condflat(args.map(findSubterm2(_, subs))) + if (!induct._2) (f, false) + else (AppliedConnector(label, induct._1), true) + case BinderFormula(label, bound, inner) => + val fv_in_f = subs.flatMap(e => e._2.freeVariables + e._1) + if (!fv_in_f.contains(bound)) { + val induct = findSubterm2(inner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, bound, induct._1), true) + } else { + val newv = Variable(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) + val newInner = inner.substitute(bound := newv) + val induct = findSubterm2(newInner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, newv, induct._1), true) + } + } + } + + private def findSubformula2(f: Formula, subs: Seq[(VariableFormula, Formula)]): (Formula, Boolean) = { + val eq = subs.find(s => isSame(f, s._2)) + if (eq.nonEmpty) (eq.get._1, true) + else + f match { + case f: AtomicFormula => (f, false) + case AppliedConnector(label, args) => + val induct = condflat(args.map(findSubformula2(_, subs))) + if (!induct._2) (f, false) + else (AppliedConnector(label, induct._1), true) + case BinderFormula(label, bound, inner) => + val fv_in_f = subs.flatMap(_._2.freeVariables) + if (!fv_in_f.contains(bound)) { + val induct = findSubformula2(inner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, bound, induct._1), true) + } else { + val newv = Variable(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) + val newInner = inner.substitute(bound := newv) + val induct = findSubformula2(newInner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, newv, induct._1), true) + } + } + } + + def findSubterm(t: Term, subs: Seq[(Variable, Term)]): Option[LambdaExpression[Term, Term, ?]] = { + val vars = subs.map(_._1) + val r = findSubterm2(t, subs) + if (r._2) Some(LambdaExpression(vars, r._1, vars.size)) + else None + } + + def findSubterm(f: Formula, subs: Seq[(Variable, Term)]): Option[LambdaExpression[Term, Formula, ?]] = { + val vars = subs.map(_._1) + val r = findSubterm2(f, subs) + if (r._2) Some(LambdaExpression(vars, r._1, vars.size)) + else None + } + + def findSubformula(f: Formula, subs: Seq[(VariableFormula, Formula)]): Option[LambdaExpression[Formula, Formula, ?]] = { + val vars = subs.map(_._1) + val r = findSubformula2(f, subs) + if (r._2) Some(LambdaExpression(vars, r._1, vars.size)) + else None + } + + def applyLeftRight(using lib: lisa.prooflib.Library, proof: lib.Proof)( + phi: Formula + )(premise: proof.Fact)(rightLeft: Boolean = false, toLeft: Boolean = true, toRight: Boolean = true): proof.ProofTacticJudgement = { + import lisa.utils.K + val originSequent = proof.getSequent(premise) + val leftOrigin = AppliedConnector(And, originSequent.left.toSeq) + val rightOrigin = AppliedConnector(Or, originSequent.right.toSeq) + + if (!toLeft && !toRight) return proof.InvalidProofTactic("applyLeftRight called with no substitution selected (toLeft or toRight).") + + phi match { + case AppliedPredicate(label, args) if label == equality => + val left = args(0) + val right = args(1) + val fv_in_phi = (originSequent.left ++ originSequent.right).flatMap(_.allSchematicLabels).map(_.id) + val v = Variable(nFreshId(fv_in_phi, 1).head) + lazy val isolatedLeft = originSequent.left.filterNot(f => isSame(f, phi)).map(f => (f, findSubterm(f, IndexedSeq(v -> left)))) + lazy val isolatedRight = originSequent.right.map(f => (f, findSubterm(f, IndexedSeq(v -> left)))) + if ((!toLeft || isolatedLeft.forall(_._2.isEmpty)) && (!toRight || isolatedRight.forall(_._2.isEmpty))) + if (rightLeft) + return proof.InvalidProofTactic(s"There is no instance of ${right} to replace.") + else + applyLeftRight(equality(right, left))(premise)(true, toLeft, toRight) match { + case proof.InvalidProofTactic(m) => return proof.InvalidProofTactic(s"There is no instance of ${left} to replace.") + case v: proof.ValidProofTactic => return v + } + + val leftForm = AppliedConnector(And, isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) + val rightForm = AppliedConnector(Or, isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) + val newleft = if (toLeft) isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.left + val newright = if (toRight) isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.right + val result1: Sequent = (AppliedConnector(And, newleft.toSeq), phi) |- rightOrigin + val result2: Sequent = result1.left |- AppliedConnector(Or, newright.toSeq) + var scproof: Seq[K.SCProofStep] = Seq(K.Restate((leftOrigin |- rightOrigin).underlying, -1)) + if (toLeft) + scproof = scproof :+ K.LeftSubstEq( + result1.underlying, + scproof.length - 1, + List(K.LambdaTermTerm(Seq(), left.underlying) -> (K.LambdaTermTerm(Seq(), right.underlying))), + (Seq(v.underlyingLabel), leftForm.underlying) + ) + if (toRight) + scproof = scproof :+ K.RightSubstEq( + result2.underlying, + scproof.length - 1, + List(K.LambdaTermTerm(Seq(), left.underlying) -> (K.LambdaTermTerm(Seq(), right.underlying))), + (Seq(v.underlyingLabel), rightForm.underlying) + ) + val bot = newleft + phi |- newright + scproof = scproof :+ K.Restate(bot.underlying, scproof.length - 1) + + proof.ValidProofTactic( + bot, + scproof, + Seq(premise) + ) + + case AppliedConnector(label, args) if label == Iff => + val left = args(0) + val right = args(1) + val fv_in_phi = (originSequent.left ++ originSequent.right).flatMap(_.allSchematicLabels).map(_.id) + val H = VariableFormula(nFreshId(fv_in_phi, 1).head) + lazy val isolatedLeft = originSequent.left.filterNot(f => isSame(f, phi)).map(f => (f, findSubformula(f, IndexedSeq(H -> left)))) + lazy val isolatedRight = originSequent.right.map(f => (f, findSubformula(f, IndexedSeq(H -> left)))) + if ((!toLeft || isolatedLeft.forall(_._2.isEmpty)) && (!toRight || isolatedRight.forall(_._2.isEmpty))) + if (rightLeft) + return proof.InvalidProofTactic(s"There is no instance of ${right} to replace.") + else + applyLeftRight(Iff(right, left))(premise)(true, toLeft, toRight) match { + case proof.InvalidProofTactic(m) => return proof.InvalidProofTactic(s"There is no instance of ${left} to replace.") + case v: proof.ValidProofTactic => return v + } + + val leftForm = AppliedConnector(And, isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) + val rightForm = AppliedConnector(Or, isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) + val newleft = if (toLeft) isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.left + val newright = if (toRight) isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.right + val result1: Sequent = (AppliedConnector(And, newleft.toSeq), phi) |- rightOrigin + val result2: Sequent = result1.left |- AppliedConnector(Or, newright.toSeq) + + var scproof: Seq[K.SCProofStep] = Seq(K.Restate((leftOrigin |- rightOrigin).underlying, -1)) + if (toLeft) + scproof = scproof :+ K.LeftSubstIff( + result1.underlying, + scproof.length - 1, + List(K.LambdaTermFormula(Seq(), left.underlying) -> (K.LambdaTermFormula(Seq(), right.underlying))), + (Seq(H.underlyingLabel), leftForm.underlying) + ) + if (toRight) + scproof = scproof :+ K.RightSubstIff( + result2.underlying, + scproof.length - 1, + List(K.LambdaTermFormula(Seq(), left.underlying) -> (K.LambdaTermFormula(Seq(), right.underlying))), + (Seq(H.underlyingLabel), rightForm.underlying) + ) + + val bot = newleft + phi |- newright + scproof = scproof :+ K.Restate(bot.underlying, scproof.length - 1) + + proof.ValidProofTactic( + bot, + scproof, + Seq(premise) + ) + case _ => proof.InvalidProofTactic(s"Formula in applySingleSimp need to be of the form a=b or q<=>p and not ${phi}") + } + } + + @nowarn("msg=.*the type test for proof.Fact cannot be checked at runtime*") + def apply(using + lib: lisa.prooflib.Library, + proof: lib.Proof, + line: sourcecode.Line, + file: sourcecode.File + )(f: proof.Fact | Formula, rightLeft: Boolean = false, toLeft: Boolean = true, toRight: Boolean = true)( + premise: proof.Fact + ): proof.ProofTacticJudgement = { + f match { + case phi: Formula => applyLeftRight(phi)(premise)(rightLeft, toLeft, toRight) + case f: proof.Fact => + val seq = proof.getSequent(f) + val phi = seq.right.head + val sp = TacticSubproof { + val x = applyLeftRight(phi)(premise)(rightLeft, toLeft, toRight) + proof.library.have(x) + proof.library.andThen(SimpleDeducedSteps.Discharge(f)) + } + + BasicStepTactic.unwrapTactic(sp)("Subproof substitution fail.") + } + + } + + def toLeft(using lib: lisa.prooflib.Library, proof: lib.Proof, line: sourcecode.Line, file: sourcecode.File)(f: proof.Fact | Formula, rightLeft: Boolean = false)( + premise: proof.Fact + ): proof.ProofTacticJudgement = apply(f, rightLeft, toLeft = true, toRight = false)(premise) + + def toRight(using lib: lisa.prooflib.Library, proof: lib.Proof, line: sourcecode.Line, file: sourcecode.File)(f: proof.Fact | Formula, rightLeft: Boolean = false)( + premise: proof.Fact + ): proof.ProofTacticJudgement = apply(f, rightLeft, toLeft = false, toRight = true)(premise) + + } +} diff --git a/lisa-topology/src/main/scala/lisa/automation/Tableau.scala b/lisa-topology/src/main/scala/lisa/automation/Tableau.scala new file mode 100644 index 000000000..de6f6bcbd --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/Tableau.scala @@ -0,0 +1,491 @@ +package lisa.automation +import lisa.fol.FOL as F +import lisa.prooflib.Library +import lisa.prooflib.OutputManager.* +import lisa.prooflib.ProofTacticLib.* +import lisa.utils.K +import lisa.utils.K.{_, given} +import lisa.utils.parsing.FOLPrinter.prettyFormula +import lisa.utils.parsing.FOLPrinter.prettySCProof +import lisa.utils.parsing.FOLPrinter.prettySequent +import lisa.utils.parsing.FOLPrinter.prettyTerm + +import scala.collection.immutable.HashMap +import scala.collection.immutable.HashSet + +/** + * Now need to deal with variables unifying with terms containing themselves + * optimiye list siye computation + * Then, optimize unification check by not checking all pairs all the time + * Then, shortcut branches by checking if they are OL-true or OL-false + * + * Next test: No quantifiers but actual terms with variables + */ + +object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequentTactic { + + var debug = true + def pr(s: Object) = if debug then println(s) + + def apply(using lib: Library, proof: lib.Proof)(bot: F.Sequent): proof.ProofTacticJudgement = { + solve(bot) match { + case Some(value) => proof.ValidProofTactic(bot, value.steps, Seq()) + case None => proof.InvalidProofTactic("Could not prove the statement.") + } + } + + /** + * Given a targeted conclusion sequent, try to prove it using laws of propositional logic and reflexivity and symmetry of equality. + * Uses the given already proven facts as assumptions to reach the desired goal. + * + * @param proof The ongoing proof object in which the step happens. + * @param premise A previously proven step necessary to reach the conclusion. + * @param bot The desired conclusion. + */ + def apply(using lib: Library, proof: lib.Proof)(premise: proof.Fact)(bot: F.Sequent): proof.ProofTacticJudgement = + from(using lib, proof)(Seq(premise)*)(bot) + + def from(using lib: Library, proof: lib.Proof)(premises: proof.Fact*)(bot: F.Sequent): proof.ProofTacticJudgement = { + val botK = bot.underlying + val premsFormulas: Seq[((proof.Fact, Formula), Int)] = premises.map(p => (p, sequentToFormula(proof.getSequent(p).underlying))).zipWithIndex + val initProof = premsFormulas.map(s => Restate(() |- s._1._2, -(1 + s._2))).toList + val sqToProve = botK ++<< (premsFormulas.map(s => s._1._2).toSet |- ()) + + solve(sqToProve) match { + case Some(value) => + val subpr = SCSubproof(value) + val stepsList = premsFormulas.foldLeft[List[SCProofStep]](List(subpr))((prev: List[SCProofStep], cur) => { + val ((prem, form), position) = cur + Cut(prev.head.bot -<< form, position, initProof.length + prev.length - 1, form) :: prev + }) + val steps = (initProof ++ stepsList.reverse).toIndexedSeq + proof.ValidProofTactic(bot, steps, premises) + case None => + proof.InvalidProofTactic("Could not prove the statement.") + } + } + + inline def solve(sequent: F.Sequent): Option[SCProof] = solve(sequent.underlying) + + def solve(sequent: K.Sequent): Option[SCProof] = { + + val f = K.ConnectorFormula(K.And, (sequent.left.toSeq ++ sequent.right.map(f => K.ConnectorFormula(K.Neg, List(f))))) + val taken = f.schematicTermLabels + val nextIdNow = if taken.isEmpty then 0 else taken.maxBy(_.id.no).id.no + 1 + val (fnamed, nextId, _) = makeVariableNamesUnique(f, nextIdNow, f.freeVariables) + + val nf = reducedNNFForm(fnamed) + val uv = VariableLabel(Identifier("§", nextId)) + val proof = decide(Branch.empty(nextId + 1, uv).prepended(nf)) + proof match + case None => None + case Some((p, _)) => Some(SCProof((Restate(sequent, p.length) :: Weakening(nf |- (), p.length - 1) :: p).reverse.toIndexedSeq, IndexedSeq.empty)) + + } + + /** + * A branch represent a sequent (whose right hand side is empty) that is being proved. + * It is assumed that the sequent is in negation normal form, negations are only applied to atoms. + * Formulas are sorted according to their shape : + * Conjunctions are in alpha + * Disjunctions are in beta + * Existential quantifiers are in delta + * Universal quantifiers are in gamma + * Atoms are in atoms (split into positive and negative) + * At each step of the procedure, a formula is deconstructed in accordance with the rules of the tableau calculus. + * Then that formula is removed from the branch as it is no longer needed. + * Variables coming from universal quantifiers are marked as suitable for unification in unifiable + * Instantiations that have been done already are stored in triedInstantiation, to avoid infinite loops. + * When a quantifier Q1 is below a universal quantifier Q2, Q2 can be instantiated multiple times. + * Then, Q1 may also need to be instantiated multiple versions, requiring fresh variable names. + * maxIndex stores an index that is used to generate fresh variable names. + */ + case class Branch( + alpha: List[ConnectorFormula], // label = And + beta: List[ConnectorFormula], // label = Or + delta: List[BinderFormula], // Exists(...)) + gamma: List[BinderFormula], // Forall(...) + atoms: (List[AtomicFormula], List[AtomicFormula]), // split into positive and negatives! + unifiable: Map[VariableLabel, (BinderFormula, Int)], // map between metavariables and the original formula they came from, with the penalty associated to the complexity of the formula. + numberInstantiated: Map[VariableLabel, Int], // map between variables and the number of times they have been instantiated + + skolemized: Set[VariableLabel], // set of variables that have been skolemized + triedInstantiation: Map[VariableLabel, Set[Term]], // map between metavariables and the term they were already instantiated with + maxIndex: Int, // the maximum index used for skolemization and metavariables + varsOrder: Map[VariableLabel, Int], // the order in which variables were instantiated. In particular, if the branch contained the formula ∀x. ∀y. ... then x > y. + unusedVar: VariableLabel // a variable the is neither free nor bound in the original formula. + ) { + def pop(f: Formula): Branch = f match + case f @ ConnectorFormula(Or, args) => + if (beta.nonEmpty && beta.head.uniqueNumber == f.uniqueNumber) copy(beta = beta.tail) else throw Exception("First formula of beta is not f") + case f @ BinderFormula(Exists, x, inner) => + if (delta.nonEmpty && delta.head.uniqueNumber == f.uniqueNumber) copy(delta = delta.tail) else throw Exception("First formula of delta is not f") + case f @ BinderFormula(Forall, x, inner) => + if (gamma.nonEmpty && gamma.head.uniqueNumber == f.uniqueNumber) copy(gamma = gamma.tail) else throw Exception("First formula of gamma is not f") + case ConnectorFormula(And, args) => + if (alpha.nonEmpty && alpha.head.uniqueNumber == f.uniqueNumber) copy(alpha = alpha.tail) else throw Exception("First formula of alpha is not f") + case f @ AtomicFormula(id, args) => + throw Exception("Should not pop Atoms") + case f @ ConnectorFormula(Neg, List(AtomicFormula(id, args))) => + throw Exception("Should not pop Atoms") + case _ => ??? + + def prepended(f: Formula): Branch = f match + case f @ ConnectorFormula(And, args) => this.copy(alpha = f :: alpha) + case f @ ConnectorFormula(Or, args) => this.copy(beta = f :: beta) + case f @ BinderFormula(Exists, x, inner) => this.copy(delta = f :: delta) + case f @ BinderFormula(Forall, x, inner) => this.copy(gamma = f :: gamma) + case f @ AtomicFormula(id, args) => + this.copy(atoms = (f :: atoms._1, atoms._2)) + case ConnectorFormula(Neg, List(f @ AtomicFormula(id, args))) => + this.copy(atoms = (atoms._1, f :: atoms._2)) + case _ => ??? + + def prependedAll(l: Seq[Formula]): Branch = l.foldLeft(this)((a, b) => a.prepended(b)) + + def asSequent: Sequent = (beta ++ delta ++ gamma ++ atoms._1 ++ atoms._2.map(a => !a)).toSet |- Set() // inefficient, not used + + import Branch.* + override def toString(): String = + val pretUnif = unifiable.map((x, f) => x.id + " -> " + prettyFormula(f._1) + " : " + f._2).mkString("Unif(", ", ", ")") + // val pretTried = triedInstantiation.map((x, t) => x.id + " -> " + prettyTerm(t, true)).mkString("Tried(", ", ", ")") + (s"Branch(" + + s"${RED(prettyIte(alpha, "alpha"))}, " + + s"${GREEN(prettyIte(beta, "beta"))}, " + + s"${BLUE(prettyIte(delta, "delta"))}, " + + s"${YELLOW(prettyIte(gamma, "gamma"))}, " + + s"${MAGENTA(prettyIte(atoms._1, "+"))}, ${CYAN(prettyIte(atoms._2, "-"))}, " + + s"$pretUnif, _, _)").split("'").mkString("").split("_").mkString("") + + } + object Branch { + def empty = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Map.empty, Set.empty, Map.empty, 1, Map.empty, VariableLabel(Identifier("§uv", 0))) + def empty(n: Int, uv: VariableLabel) = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Map.empty, Set.empty, Map.empty, n, Map.empty, uv) + def prettyIte(l: Iterable[Formula], head: String): String = l match + case Nil => "Nil" + case _ => l.map(prettyFormula(_, true)).mkString(head + "(", ", ", ")") + + } + + def makeVariableNamesUnique(f: Formula, nextId: Int, seen: Set[VariableLabel]): (Formula, Int, Set[VariableLabel]) = f match + case ConnectorFormula(label, args) => + val (nArgs, nnId, nSeen) = args.foldLeft((List(): Seq[Formula], nextId, seen))((prev, next) => + val (l, n, s) = prev + val (nf, nn, ns) = makeVariableNamesUnique(next, n, s) + (l :+ nf, nn, ns) + ) + (ConnectorFormula(label, nArgs), nnId, nSeen) + case pf: AtomicFormula => (pf, nextId, seen) + case BinderFormula(label, x, inner) => + if (seen.contains(x)) + val (nInner, nnId, nSeen) = makeVariableNamesUnique(inner, nextId + 1, seen) + val newX = VariableLabel(Identifier(x.id, nextId)) + (BinderFormula(label, newX, substituteVariablesInFormula(nInner, Map(x -> newX), Seq())), nnId, nSeen) + else + val (nInner, nnId, nSeen) = makeVariableNamesUnique(inner, nextId, seen + x) + (BinderFormula(label, x, nInner), nnId, nSeen) + + type Substitution = Map[VariableLabel, Term] + val Substitution = HashMap + def prettySubst(s: Substitution): String = s.map((x, t) => x.id + " -> " + prettyTerm(t, true)).mkString("Subst(", ", ", ")") + + /** + * Detect if two terms can be unified, and if so, return a substitution that unifies them. + */ + def unify(t1: Term, t2: Term, current: Substitution, br: Branch): Option[Substitution] = (t1, t2) match + case (VariableTerm(x), VariableTerm(y)) if (br.unifiable.contains(x) || x.id.no > br.maxIndex) && (br.unifiable.contains(y) || y.id.no > br.maxIndex) => + if x == y then Some(current) + else if current.contains(x) then unify(current(x), t2, current, br) + else if current.contains(y) then unify(t1, current(y), current, br) + else Some(current + (x -> y)) + case (VariableTerm(x), t2: Term) if br.unifiable.contains(x) || x.id.no > br.maxIndex => + val newt2 = substituteVariablesInTerm(t2, current) + if newt2.freeVariables.contains(x) then None + else if (current.contains(x)) unify(current(x), newt2, current, br) + else Some(current + (x -> newt2)) + case (t1: Term, VariableTerm(y)) if br.unifiable.contains(y) || y.id.no > br.maxIndex => + val newt1 = substituteVariablesInTerm(t1, current) + if newt1.freeVariables.contains(y) then None + else if (current.contains(y)) unify(newt1, current(y), current, br) + else Some(current + (y -> newt1)) + case (Term(label1, args1), Term(label2, args2)) => + if label1 == label2 && args1.size == args2.size then + args1 + .zip(args2) + .foldLeft(Some(current): Option[Substitution])((prev, next) => + prev match + case None => None + case Some(s) => unify(next._1, next._2, s, br) + ) + else None + + /** + * Detect if two atoms can be unified, and if so, return a substitution that unifies them. + */ + def unifyPred(pos: AtomicFormula, neg: AtomicFormula, br: Branch): Option[Substitution] = { + (pos, neg) match + case (AtomicFormula(id1, args1), AtomicFormula(id2, args2)) if (id1 == id2 && args1.size == args2.size) => + args1 + .zip(args2) + .foldLeft(Some(Substitution.empty): Option[Substitution])((prev, next) => + prev match + case None => None + case Some(s) => unify(next._1, next._2, s, br) + ) + case _ => None + + } + + /** + * Detect if a branch can be closed, and if so, return a list of substitutions that closes it along with the formulas used to close it + * If it can't be closed, returns None + * The substitution cannot do substitutions that were already done in branch.triedInstantiation. + * When multiple substitutions are possible, the one with the smallest size is returned. (Maybe there is a better heuristic, like distance from the root?) + */ + def close(branch: Branch): Option[(Substitution, Set[Formula])] = { + val newMap = branch.atoms._1 + .flatMap(pred => pred.freeVariables.filter(v => branch.unifiable.contains(v))) + .map(v => v -> VariableLabel(Identifier(v.id.name, v.id.no + branch.maxIndex + 1))) + .toMap + val newMapTerm = newMap.map((k, v) => k -> VariableTerm(v)) + val inverseNewMap = newMap.map((k, v) => v -> k).toMap + val inverseNewMapTerm = inverseNewMap.map((k, v) => k -> VariableTerm(v)) + val pos = branch.atoms._1.map(pred => substituteVariablesInFormula(pred, newMapTerm, Seq())).asInstanceOf[List[AtomicFormula]].iterator + var substitutions: List[(Substitution, Set[Formula])] = Nil + + while (pos.hasNext) { + val p = pos.next() + if (p.label == bot) return Some((Substitution.empty, Set(bot))) + val neg = branch.atoms._2.iterator + while (neg.hasNext) { + val n = neg.next() + unifyPred(p, n, branch) match + case None => () + case Some(unif) => + substitutions = (unif, Set(p, !n)) :: substitutions + } + } + + val cr1 = substitutions.map((sub, set) => + ( + sub.flatMap((v, t) => + if v.id.no > branch.maxIndex then + if t == inverseNewMapTerm(v) then None + else Some(inverseNewMap(v) -> substituteVariablesInTerm(t, inverseNewMapTerm.map((v, t) => v -> substituteVariablesInTerm(t, sub)))) + else if newMap.contains(v) && t == newMap(v) then None + else Some(v -> substituteVariablesInTerm(t, inverseNewMapTerm)) + ), + set.map(f => substituteVariablesInFormula(f, inverseNewMapTerm, Seq())) + ) + ) + + val cr = cr1.filterNot(s => + s._1.exists((x, t) => + val v = branch.triedInstantiation.contains(x) && branch.triedInstantiation(x).contains(t) + v + ) + ) + + bestSubst(cr, branch) + + } + + def bestSubst(substs: List[(Substitution, Set[Formula])], branch: Branch): Option[(Substitution, Set[Formula])] = { + if substs.isEmpty then return None + val minSize = substs.minBy(_._1.size) + val smallSubst = substs.filter(_._1.size == minSize._1.size) + // Up to this, it is necessary for completeness. From this, it is heuristic. + + val best = smallSubst.minBy(s => substitutionScore(s._1, branch)) + Some(best) + } + def formulaPenalty(f: Formula, branch: Branch): Int = f match + case ConnectorFormula(And, args) => 10 + args.map(formulaPenalty(_, branch)).sum + case ConnectorFormula(Or, args) => 40 + args.map(formulaPenalty(_, branch)).sum + case BinderFormula(Exists, x, inner) => 30 + formulaPenalty(inner, branch) + case BinderFormula(Forall, x, inner) => 200 + formulaPenalty(inner, branch) + case AtomicFormula(id, args) => 0 + case ConnectorFormula(Neg, List(AtomicFormula(id, args))) => 0 + case _ => ??? + + def substitutionScore(subst: Substitution, branch: Branch): Int = { + def pairPenalty(v: VariableLabel, t: Term) = { + val variablePenalty = branch.unifiable(v)._2 + branch.numberInstantiated(v) * 20 + def termPenalty(t: Term): Int = t match + case VariableTerm(x) => if branch.unifiable.contains(x) then branch.unifiable(x)._2 * 1 else 0 + case Term(label, args) => 100 + args.map(termPenalty).sum + variablePenalty + termPenalty(t) + } + subst.map((v, t) => pairPenalty(v, t)).sum + } + + /** + * Explodes one And formula + * The alpha list of the branch must not be empty + */ + def alpha(branch: Branch): Branch = { + val f = branch.alpha.head + branch.copy(alpha = branch.alpha.tail).prependedAll(f.args) + } + + /** + * Explodes one Or formula, and alpha-simplifies it + * Add the exploded formula to the used list, if one beta formula is found + * The beta list of the branch must not be empty + */ + def beta(branch: Branch): List[(Branch, Formula)] = { + val f = branch.beta.head + val b1 = branch.copy(beta = branch.beta.tail) + val resList = f.args.toList.map(disjunct => { + ((b1.prepended(disjunct), disjunct)) + }) + resList + } + + /** + * Explodes one Exists formula + * Add the unquantified formula to the branch + * Since the bound variable is not marked as suitable for instantiation, it behaves as a constant symbol (skolem) + */ + def delta(branch: Branch): (Branch, VariableLabel, Formula) = { + val f = branch.delta.head + if branch.skolemized.contains(branch.delta.head.bound) then + val newX = VariableLabel(Identifier(f.bound.id.name, branch.maxIndex)) + val newInner = substituteVariablesInFormula(f.inner, Map(f.bound -> newX), Seq()) + (branch.copy(delta = branch.delta.tail, maxIndex = branch.maxIndex + 1).prepended(newInner), newX, newInner) + else (branch.copy(delta = branch.delta.tail, skolemized = branch.skolemized + f.bound).prepended(f.inner), f.bound, f.inner) + } + + /** + * Explodes one Forall formula + * Add the unquantified formula to the branch and mark the bound variable as suitable for unification + * This step will most of the time be cancelled when building the proof, unless any arbitrary instantiation is sufficient to get a proof. + */ + def gamma(branch: Branch): (Branch, VariableLabel, Formula) = { + val f = branch.gamma.head + val (ni, nb) = branch.unifiable.get(f.bound) match + case None => + (f.inner, f.bound) + case Some(value) => + val newBound = VariableLabel(Identifier(f.bound.id.name, branch.maxIndex)) + val newInner = substituteVariablesInFormula(f.inner, Map(f.bound -> newBound), Seq()) + (newInner, newBound) + val b1 = branch.copy( + gamma = branch.gamma.tail, + unifiable = branch.unifiable + (nb -> (f, formulaPenalty(f.inner, branch))), + numberInstantiated = branch.numberInstantiated + (nb -> (branch.numberInstantiated.getOrElse(f.bound, 0))), + maxIndex = branch.maxIndex + 1, + varsOrder = branch.varsOrder + (nb -> branch.varsOrder.size) + ) + (b1.prepended(ni), nb, ni) + } + + /** + * When a closing unification has been found, apply it to the branch + * This does not backtracking: The metavariable remains available if it needs further instantiation. + */ + def applyInst(branch: Branch, x: VariableLabel, t: Term): (Branch, Formula) = { + val f = branch.unifiable(x)._1 + val newTried = branch.triedInstantiation.get(x) match + case None => branch.triedInstantiation + (x -> Set(t)) + case Some(s) => branch.triedInstantiation + (x -> (s + t)) + + val inst = instantiate(f.inner, f.bound, t) + val r = branch + .prepended(inst) + .copy( + triedInstantiation = newTried, + numberInstantiated = branch.numberInstantiated + (x -> (branch.numberInstantiated(x) + 1)) + ) + (r, inst) + } + + /** + * Decide if a branch can be closed, and if not, explode it. + * Main routine of the decision procedure. If it succeeds, return a proof of the branch. + * Note that the proof actually proves a subset of a branch when possible, to cut short on unneeded steps and formulas. + * The return integer is the size of the proof: Used to avoid computing the size every time in linear time. + */ + def decide(branch: Branch): Option[(List[SCProofStep], Int)] = { + + val closeSubst = close(branch) + if (closeSubst.nonEmpty && closeSubst.get._1.isEmpty) // If branch can be closed without Instantiation (Hyp) + Some((List(RestateTrue(Sequent(closeSubst.get._2, Set()))), 0)) + else if (branch.alpha.nonEmpty) // If branch contains an Alpha formula (LeftAnd) + val rec = alpha(branch) + decide(rec).map((proof, step) => + if branch.alpha.head.args.exists(proof.head.bot.left.contains) then + val sequent = proof.head.bot.copy(left = (proof.head.bot.left -- branch.alpha.head.args) + branch.alpha.head) + (Weakening(sequent, proof.size - 1) :: proof, step + 1) + else (proof, step) + ) + else if (branch.delta.nonEmpty) // If branch contains a Delta formula (LeftExists) + val rec = delta(branch) + val upperProof = decide(rec._1) + upperProof.map((proof, step) => + if proof.head.bot.left.contains(rec._3) then + val sequent = (proof.head.bot -<< rec._3) +<< branch.delta.head + (LeftExists(sequent, step, rec._3, rec._2) :: proof, step + 1) + else (proof, step) + ) + else if (branch.beta.nonEmpty) // If branch contains a Beta formula (LeftOr) + val list = beta(branch) + val (proof, treversed, needed) = list.foldLeft((Some(Nil): Option[List[SCProofStep]], Nil: List[Int], true: Boolean))((prev, next) => + prev match + case (None, _, _) => prev // proof failed + case (_, _, false) => + prev // proof succeded early + case (Some(prevProof), t, true) => + val res = decide(next._1) + res match + case None => (None, t, true) + case Some((nextProof, step)) => + if nextProof.head.bot.left.contains(next._2) then // If the disjunct was used, encapsulate the subbranch in a Subproof + val subproofDisj = + if nextProof.size == 1 then nextProof.head + else SCSubproof(SCProof(nextProof.toIndexedSeq.reverse, IndexedSeq.empty), IndexedSeq.empty) + (Some(subproofDisj :: prevProof), prevProof.size :: t, true) + else + // If the disjunct was not used, then the subbranch is a proof of the whole statement and the split is not necessary. + (res.map(_._1), List(nextProof.size - 1), false) + ) + proof.map(proo => + if needed == true then + val sequent = ((proo.reverse.zip(list).flatMap((proof, bf) => proof.bot.left - bf._2).toSet + branch.beta.head) |- ()) + (LeftOr(sequent, treversed.reverse, branch.beta.head.args) :: proo, treversed.size) + else (proo, proo.size - 1) + ) + else if (branch.gamma.nonEmpty) // If branch contains a Gamma formula (LeftForall) + val rec = gamma(branch) + val upperProof = decide(rec._1) + // LeftForall(bot: Sequent, t1: Int, phi: Formula, x: VariableLabel, t: Term) + upperProof.map((proof, step) => + if proof.head.bot.left.contains(rec._3) then + val sequent = (proof.head.bot -<< rec._3) +<< branch.gamma.head + (LeftForall(sequent, step, branch.gamma.head.inner, branch.gamma.head.bound, rec._2()) :: proof, step + 1) + else (proof, step) + ) + else if (closeSubst.nonEmpty && closeSubst.get._1.nonEmpty) // If branch can be closed with Instantiation (LeftForall) + val (x, t) = closeSubst.get._1.minBy((x, t) => branch.varsOrder(x)) + val (recBranch, instantiated) = applyInst(branch, x, t) + val upperProof = decide(recBranch) + upperProof.map((proof, step) => + if proof.head.bot.left.contains(instantiated) then + val sequent = (proof.head.bot -<< instantiated) +<< branch.unifiable(x)._1 + (LeftForall(sequent, step, branch.unifiable(x)._1.inner, branch.unifiable(x)._1.bound, t) :: proof, step + 1) + else (proof, step) + ) + else None + // End of decide + } + + def containsAlpha(set: Set[Formula], f: Formula) = f match { + case ConnectorFormula(And, args) => args.exists(set.contains) + case _ => set.contains(f) + } + + def instantiate(f: Formula, x: VariableLabel, t: Term): Formula = f match + case ConnectorFormula(label, args) => ConnectorFormula(label, args.map(instantiate(_, x, t))) + case AtomicFormula(id, args) => AtomicFormula(id, args.map(substituteVariablesInTerm(_, Substitution(x -> t)))) + case BinderFormula(label, y, inner) => if (x == y) f else BinderFormula(label, y, instantiate(inner, x, t)) +} diff --git a/lisa-topology/src/main/scala/lisa/automation/Tautology.scala b/lisa-topology/src/main/scala/lisa/automation/Tautology.scala new file mode 100644 index 000000000..d1367feeb --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/Tautology.scala @@ -0,0 +1,257 @@ +package lisa.automation + +import lisa.automation.Substitution +import lisa.fol.FOL as F +import lisa.prooflib.Library +import lisa.prooflib.ProofTacticLib.* +import lisa.utils.K.{_, given} + +/** + * A tactic object dedicated to solve any propositionaly provable sequent (possibly in exponential time). Can be used with arbitrary many premises. + * Leverages the OL algorithm for scalafmpropositional logic. + */ +object Tautology extends ProofTactic with ProofSequentTactic with ProofFactSequentTactic { + + /** + * Given a targeted conclusion sequent, try to prove it using laws of propositional logic and reflexivity and symmetry of equality. + * + * @param proof The ongoing proof object in which the step happens. + * @param bot The desired conclusion. + */ + def apply(using lib: Library, proof: lib.Proof)(bot: F.Sequent): proof.ProofTacticJudgement = { + val botK = bot.underlying + solveSequent(botK) match { + case Left(value) => proof.ValidProofTactic(bot, value.steps, Seq()) + case Right((msg, seq)) => proof.InvalidProofTactic(msg) + } + } + + /** + * Given a targeted conclusion sequent, try to prove it using laws of propositional logic and reflexivity and symmetry of equality. + * Uses the given already proven facts as assumptions to reach the desired goal. + * + * @param proof The ongoing proof object in which the step happens. + * @param premise A previously proven step necessary to reach the conclusion. + * @param bot The desired conclusion. + */ + def apply(using lib: Library, proof: lib.Proof)(premise: proof.Fact)(bot: F.Sequent): proof.ProofTacticJudgement = + from(using lib, proof)(Seq(premise)*)(bot) + + def from(using lib: Library, proof: lib.Proof)(premises: proof.Fact*)(bot: F.Sequent): proof.ProofTacticJudgement = { + val botK = bot.underlying + val premsFormulas: Seq[((proof.Fact, Formula), Int)] = premises.map(p => (p, sequentToFormula(proof.getSequent(p).underlying))).zipWithIndex + val initProof = premsFormulas.map(s => Restate(() |- s._1._2, -(1 + s._2))).toList + val sqToProve = botK ++<< (premsFormulas.map(s => s._1._2).toSet |- ()) + + solveSequent(sqToProve) match { + case Left(value) => + val subpr = SCSubproof(value) + val stepsList = premsFormulas.foldLeft[List[SCProofStep]](List(subpr))((prev: List[SCProofStep], cur) => { + val ((prem, form), position) = cur + if prev.head.bot.left.contains(form) then Cut(prev.head.bot -<< form, position, initProof.length + prev.length - 1, form) :: prev + else prev + }) + val steps = (initProof ++ stepsList.reverse).toIndexedSeq + proof.ValidProofTactic(bot, steps, premises) + case Right((msg, seq)) => + proof.InvalidProofTactic(msg) + } + } + + /** + * This function returns a proof of the given sequent if such a proof exists using only the rules of propositional logic and reflexivity and symmetry of equality. + * Be aware that the runtime and size of the proof may be exponential in the number of atoms (i.e. number of non-propositional subformulas of the input). + * The strategy consists in leveraging OL formula reduction by alternating between branching on an atom and reducing the formula. + * @param s A sequent that should be a propositional logic tautology. It can contain binders and schematic connector symbols, but they will be treated as atoms. + * @return A proof of the given sequent, if it exists + */ + def solveSequent(s: Sequent): Either[SCProof, (String, Sequent)] = { + val augSeq = augmentSequent(s) + val MaRvIn = VariableFormulaLabel(freshId(augSeq.formula.schematicFormulaLabels.map(_.id), "MaRvIn")) // arbitrary name that is unlikely to already exist in the formula + + try { + val steps = solveAugSequent(augSeq, 0)(using MaRvIn) + Left(SCProof((Restate(s, steps.length - 1) :: steps).reverse.toIndexedSeq)) + } catch + case e: NoProofFoundException => + Right( + ( + "The statement may be incorrect or not provable within propositional logic.\n" + + "The proof search failed because it needed the truth of the following sequent:\n" + + s"${lisa.utils.FOLPrinter.prettySequent(e.unsolvable)}", + e.unsolvable + ) + ) + + } + + // From there, private code. + + // Augmented Sequent + private case class AugSequent(decisions: (List[Formula], List[Formula]), formula: Formula) + + // Transform a sequent into a format more adequate for solving + private def augmentSequent(s: Sequent): AugSequent = { + val f = reducedForm(sequentToFormula(s)) + val atoms: scala.collection.mutable.Map[Formula, Int] = scala.collection.mutable.Map.empty + AugSequent((Nil, Nil), f) + } + + def reduceSequent(s: Sequent): Formula = { + val p = simplify(sequentToFormula(s)) + val nf = computeNormalForm(p) + val fln = fromLocallyNameless(nf, Map.empty, 0) + val res = toFormulaAIG(fln) + res + } + + // Find all "atoms" of the formula. + // We mean atom in the propositional logic sense, so any formula starting with a predicate symbol, a binder or a schematic connector is an atom here. + def findBestAtom(f: Formula): Option[Formula] = { + val atoms: scala.collection.mutable.Map[Formula, Int] = scala.collection.mutable.Map.empty + def findAtoms2(f: Formula, add: Formula => Unit): Unit = f match { + case AtomicFormula(label, _) if label != top && label != bot => add(f) + case AtomicFormula(_, _) => () + case ConnectorFormula(label, args) => + label match { + case label: ConstantConnectorLabel => args.foreach(c => findAtoms2(c, add)) + case SchematicConnectorLabel(id, arity) => add(f) + } + case BinderFormula(label, bound, inner) => add(f) + } + findAtoms2(f, a => atoms.update(a, { val g = atoms.get(a); if (g.isEmpty) 1 else g.get + 1 })) + if (atoms.isEmpty) None else Some(atoms.toList.maxBy(_._2)._1) + } + + private class NoProofFoundException(val unsolvable: Sequent) extends Exception + + // Given a sequent, return a proof of that sequent if on exists that only uses propositional logic rules and reflexivity of equality. + // Alternates between reducing the formulas using the OL algorithm for propositional logic and branching on an atom using excluded middle. + // An atom is a subformula of the input that is either a predicate, a binder or a schematic connector, i.e. a subformula that has not meaning in propositional logic. + private def solveAugSequent(s: AugSequent, offset: Int)(using MaRvIn: VariableFormulaLabel): List[SCProofStep] = { + val bestAtom = findBestAtom(s.formula) + val redF = reducedForm(s.formula) + if (redF == top()) { + List(RestateTrue(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- s.formula)) + } else if (bestAtom.isEmpty) { + assert(redF == bot()) // sanity check; If the formula has no atom left in it and is reduced, it should be either ⊤ or ⊥. + val res = s.decisions._1 |- redF :: s.decisions._2 // the branch that can't be closed + throw new NoProofFoundException(res) + } else { + val atom = bestAtom.get + val optLambda = findSubformula(redF, Seq((MaRvIn, atom))) + if (optLambda.isEmpty) return solveAugSequent(AugSequent(s.decisions, redF), offset) + val lambdaF = optLambda.get + + val seq1 = AugSequent((atom :: s.decisions._1, s.decisions._2), lambdaF(Seq(top()))) + val proof1 = solveAugSequent(seq1, offset) + val subst1 = RightSubstIff( + atom :: s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- redF, + offset + proof1.length - 1, + List((LambdaTermFormula(Seq(), atom), LambdaTermFormula(Seq(), top()))), + (lambdaF.vars, lambdaF.body) + ) + val seq2 = AugSequent((s.decisions._1, atom :: s.decisions._2), lambdaF(Seq(bot()))) + val proof2 = solveAugSequent(seq2, offset + proof1.length + 1) + val subst2 = RightSubstIff( + Neg(atom) :: s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- redF, + offset + proof1.length + proof2.length - 1 + 1, + List((LambdaTermFormula(Seq(), atom), LambdaTermFormula(Seq(), bot()))), + (lambdaF.vars, lambdaF.body) + ) + val red2 = Restate(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- (redF, atom), offset + proof1.length + proof2.length + 2 - 1) + val cutStep = Cut(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- redF, offset + proof1.length + proof2.length + 3 - 1, offset + proof1.length + 1 - 1, atom) + val redStep = Restate(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- s.formula, offset + proof1.length + proof2.length + 4 - 1) + redStep :: cutStep :: red2 :: subst2 :: proof2 ++ (subst1 :: proof1) + + } + } + + private def condflat[T](s: Seq[(T, Boolean)]): (Seq[T], Boolean) = (s.map(_._1), s.exists(_._2)) + + private def findSubterm2(t: Term, subs: Seq[(VariableLabel, Term)]): (Term, Boolean) = { + val eq = subs.find(s => isSameTerm(t, s._2)) + if (eq.nonEmpty) (eq.get._1(), true) + else { + val induct = condflat(t.args.map(te => findSubterm2(te, subs))) + if (!induct._2) (t, false) + else (Term(t.label, induct._1), true) + + } + + } + + private def findSubterm2(f: Formula, subs: Seq[(VariableLabel, Term)]): (Formula, Boolean) = { + f match { + case AtomicFormula(label, args) => + val induct = condflat(args.map(findSubterm2(_, subs))) + if (!induct._2) (f, false) + else (AtomicFormula(label, induct._1), true) + case ConnectorFormula(label, args) => + val induct = condflat(args.map(findSubterm2(_, subs))) + if (!induct._2) (f, false) + else (ConnectorFormula(label, induct._1), true) + case BinderFormula(label, bound, inner) => + val fv_in_f = subs.flatMap(e => e._2.freeVariables + e._1) + if (!fv_in_f.contains(bound)) { + val induct = findSubterm2(inner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, bound, induct._1), true) + } else { + val newv = VariableLabel(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) + val newInner = substituteVariablesInFormula(inner, Map(bound -> newv()), Seq.empty) + val induct = findSubterm2(newInner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, newv, induct._1), true) + } + } + } + + private def findSubformula2(f: Formula, subs: Seq[(VariableFormulaLabel, Formula)]): (Formula, Boolean) = { + val eq = subs.find(s => isSame(f, s._2)) + if (eq.nonEmpty) (eq.get._1(), true) + else + f match { + case AtomicFormula(label, args) => + (f, false) + case ConnectorFormula(label, args) => + val induct = condflat(args.map(findSubformula2(_, subs))) + if (!induct._2) (f, false) + else (ConnectorFormula(label, induct._1), true) + case BinderFormula(label, bound, inner) => + val fv_in_f = subs.flatMap(_._2.freeVariables) + if (!fv_in_f.contains(bound)) { + val induct = findSubformula2(inner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, bound, induct._1), true) + } else { + val newv = VariableLabel(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) + val newInner = substituteVariablesInFormula(inner, Map(bound -> newv()), Seq.empty) + val induct = findSubformula2(newInner, subs) + if (!induct._2) (f, false) + else (BinderFormula(label, newv, induct._1), true) + } + } + } + def findSubterm(t: Term, subs: Seq[(VariableLabel, Term)]): Option[LambdaTermTerm] = { + val vars = subs.map(_._1) + val r = findSubterm2(t, subs) + if (r._2) Some(LambdaTermTerm(vars, r._1)) + else None + } + + def findSubterm(f: Formula, subs: Seq[(VariableLabel, Term)]): Option[LambdaTermFormula] = { + val vars = subs.map(_._1) + val r = findSubterm2(f, subs) + if (r._2) Some(LambdaTermFormula(vars, r._1)) + else None + } + + def findSubformula(f: Formula, subs: Seq[(VariableFormulaLabel, Formula)]): Option[LambdaFormulaFormula] = { + val vars = subs.map(_._1) + val r = findSubformula2(f, subs) + if (r._2) Some(LambdaFormulaFormula(vars, r._1)) + else None + } + +} diff --git a/lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala b/lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala new file mode 100644 index 000000000..e8d50cf0b --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala @@ -0,0 +1,121 @@ +package lisa.automation.atp +import lisa.fol.FOL as F +import lisa.prooflib.Library +import lisa.prooflib.OutputManager +import lisa.prooflib.ProofTacticLib.* +import lisa.utils.K +import lisa.utils.tptp.* + +import java.io.* +import scala.io.Source +import scala.util.Failure +import scala.util.Success +import scala.util.Try + +import ProofParser.* +import KernelParser.* +import sys.process._ + +/** + * Goéland is an automated theorem prover. This tactic calls the Goéland prover to solve the current sequent. + * Goéland is only available on Linux yet, but proofs generated by Goéland should be kept in the library for future use. + * To ensure that proofs are published and can be replayed in any system, proofs from an ATPcan only be generated in draft mode. + * When in non-draft mode, the proof file should be given as an argument to the tactic (the exact file is provided by Lisa upon run without draft mode). + */ +object Goeland extends ProofTactic with ProofSequentTactic { + private var i : Int = 0 + + val goelandExec = "../bin/goeland_linux_release" + + class OsNotSupportedException(msg: String) extends Exception(msg) + + val foldername = "goeland/" + + /** + * Fetch a proof of a sequent that was previously proven by Goéland. + * The file must be in SC-TPTP format. + */ + def apply(using lib: Library, proof: lib.Proof)(file:String)(bot: F.Sequent): proof.ProofTacticJudgement = { + val outputname = proof.owningTheorem.fullName+"_sol" + try { + val scproof = reconstructProof(new File(foldername+outputname+".p"))(using ProofParser.mapAtom, ProofParser.mapTerm, ProofParser.mapVariable) + proof.ValidProofTactic(bot, scproof.steps, Seq()) + } catch { + case e: FileNotFoundException => + throw FileNotFoundException("The file "+foldername+outputname+".p was not found. To produce a proof, use `by Goeland`. ") + case e => throw e + } + } + + + /** + * Solve a sequent using the Goéland automated theorem prover. + * At the moment, this option is only available on Linux system. + * The proof is generated and saved in a file in the `Goeland` folder. + */ + def apply(using lib: Library, proof: lib.Proof)(bot: F.Sequent): proof.ProofTacticJudgement = { + + + solve(Seq(), bot, proof.owningTheorem.fullName, lib.isDraft) match { + case Success(value) => proof.ValidProofTactic(bot, value.steps, Seq()) + case Failure(e) => e match + case e: FileNotFoundException => throw new Exception("For compatibility reasons, external provers can't be called in non-draft mode" + + " unless all proofs have already been generated and be available in static files. You can enable draft mode by adding `draft()` at the top of your working file.") + case e: OsNotSupportedException => throw e + case e => + throw e + } + } + + inline def solve(axioms: Seq[F.Sequent], sequent: F.Sequent, source: String, generateProofs : Boolean): Try[K.SCProof] = + solveK(axioms.map(_.underlying), sequent.underlying, source, generateProofs) + + + /** + * Solve a sequent using the Goéland automated theorem prover, and return the kernel proof. + * At the moment, this option is only available on Linux systems. + */ + def solveK(using line: sourcecode.Line, file: sourcecode.File)(axioms: Seq[K.Sequent], sequent: K.Sequent, source:String, generateProofs : Boolean): Try[K.SCProof] = { + val filename = source + val outputname = source+"_sol" + val directory = File(foldername) + if (directory != null) && !directory.exists() then directory.mkdirs() + + val freevars = (sequent.left.flatMap(_.freeVariables) ++ sequent.right.flatMap(_.freeVariables) ).toSet.map(x => x -> K.Term(K.VariableLabel(K.Identifier("X"+x.id.name, x.id.no)), Seq())).toMap + + val backMap = freevars.map{ + case (x: K.VariableLabel, K.Term(xx: K.VariableLabel, _)) => xx -> K.LambdaTermTerm(Seq(), K.Term(x, Seq())) + case _ => throw new Exception("This should not happen") + } + val r = problemToFile(foldername, filename, "question"+i, axioms, sequent, source) + i += 1 + + if generateProofs then + val OS = System.getProperty("os.name") + if OS.contains("nix") || OS.contains("nux") || OS.contains("aix") then + val ret = s"chmod u+x \"$goelandExec\"".! + val cmd = (s"$goelandExec -otptp -wlogs -no_id -quoted_pred -proof_file=$foldername$outputname $foldername$filename.p") + val res = try { + cmd.!! + } catch { + case e: Exception => + throw e + } + val proof = reconstructProof(new File(foldername+outputname+".p"))(using ProofParser.mapAtom, ProofParser.mapTerm, ProofParser.mapVariable) + Success(proof) + else if OS.contains("win") then + Failure(OsNotSupportedException("The Goeland automated theorem prover is not yet supported on Windows.")) + else + Failure(OsNotSupportedException("The Goeland automated theorem prover is only supported on Linux for now.")) + else + if File(foldername+outputname+".p").exists() then + val proof = reconstructProof(new File(foldername+outputname+".p"))(using ProofParser.mapAtom, ProofParser.mapTerm, ProofParser.mapVariable) + println(OutputManager.WARNING(s"WARNING: in ${file.value}:$line, For compatibility reasons, replace `by Goeland` with `by Goeland(\"$foldername$outputname\")`.")) + Success(proof) + + else Failure(Exception("For compatibility reasons, external provers can't be called in non-draft mode. You can enable draft mode by adding `draft()` at the top of your working file.")) + + + } + +} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala b/lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala new file mode 100644 index 000000000..651a3f7a1 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala @@ -0,0 +1,200 @@ +package lisa.automation.settheory + +import lisa.SetTheoryLibrary.{_, given} +import lisa.automation.Tautology +import lisa.fol.FOL.{_, given} +import lisa.kernel.proof.SequentCalculus as SCunique +import lisa.maths.Quantifiers +import lisa.maths.settheory.SetTheory +import lisa.prooflib.BasicStepTactic.* +import lisa.prooflib.Library +import lisa.prooflib.ProofTacticLib.{_, given} +import lisa.prooflib.SimpleDeducedSteps.Restate +import lisa.prooflib.* +import lisa.utils.Printer +import lisa.utils.unification.UnificationUtils.FormulaSubstitution +import lisa.utils.unification.UnificationUtils.TermSubstitution +import lisa.utils.unification.UnificationUtils.matchFormula + +object SetTheoryTactics { + // var defs + private val x = variable + private val y = variable + private val z = variable + private val h = formulaVariable + private val P = predicate[1] + private val schemPred = predicate[1] + + /** + * Deduced Tactic --- Unique Comprehension + * + * Generates a unique existence proof. Given a set `x`, and a predicate `P(t, + * x)`, comprehension postulates there is a set containing the elements `t` of + * `x` satisfying `P(t, x)`, denoted `{t ∈ x | P(t, x)}`. This set is unique + * by extensionality. + * + * `() ⊢ ∃! z. ∀ t. t ∈ z ⇔ (t ∈ x ⋀ P(t, x))` + * + * @param originalSet the set to apply comprehension on + * @param separationPredicate the predicate to use for comprehension `(Term => + * Term => Boolean)` + * @return subproof for unique existence of the set defined by inputs + * + * @example + * Generates a subproof for the unique existence of the set `{t ∈ x | t ∈ y}`: + * {{{ + * have(() |- existsOne(z, forall(t, in(t, z) <=> (in(t, x) /\ in(t, y))))) by UniqueComprehension(x, lambda(Seq(t, x), in(t, y))) + * }}} + * See [[setIntersection]] or [[relationDomain]] for more usage. + */ + object UniqueComprehension extends ProofTactic { + def apply(using + proof: Proof, + line: sourcecode.Line, + file: sourcecode.File, + om: OutputManager + )(originalSet: Term, separationPredicate: LambdaTF[1])( // TODO dotty forgets that Term <:< LisaObject[Term] + bot: Sequent + ): proof.ProofTacticJudgement = { + require(separationPredicate.bounds.length == 1) // separationPredicate takes two args + given lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary + // fresh variable names to avoid conflicts + val botWithAssumptions = bot ++ (proof.getAssumptions |- ()) + val takenIDs = (botWithAssumptions.freeVariables ++ separationPredicate.body.freeVariables ++ originalSet.freeVariables).map(_.id) + val t1 = Variable(freshId(takenIDs, x.id)) + val t2 = Variable(freshId(takenIDs, y.id)) + + val prop = (in(t2, originalSet) /\ separationPredicate(t2)) // TODO (Seq(t2, originalSet) + def fprop(z: Term) = forall(t2, in(t2, z) <=> prop) + + /** + * Proof Summary: + * + * originalSet = x + * separationPredicate = \t x -> P(t, x) + * + * have () |- ∃ z. t ∈ z <=> (t ∈ x /\ P(t, x)) Comprehension Schema Instantiation + * import ∃ z. t ∈ z <=> (t ∈ x /\ P(t, x)) |- ∃! z. t ∈ z <=> (t ∈ x /\ P(t, x)) Unique by Extension [[uniqueByExtension]] Instantiation + * have () |- ∃! z. t ∈ z <=> (t ∈ x /\ P(t, x)) Cut + */ + val sp = TacticSubproof { // TODO check if isInstanceOf first + val existence = have(() |- exists(t1, fprop(t1))) by Weakening(comprehensionSchema of (z -> originalSet, φ -> separationPredicate)) + + val existsToUnique = have(exists(t1, fprop(t1)) |- existsOne(t1, fprop(t1))) by Weakening(SetTheory.uniqueByExtension of schemPred -> lambda(t2, prop)) + + // assumption elimination + have(() |- existsOne(t1, fprop(t1))) by Cut(existence, existsToUnique) + } + + // safely check, unwrap, and return the proof judgement + unwrapTactic(sp)("Subproof for unique comprehension failed.") + } + + /** + * Similarly to [[UniqueComprehension]], generates a unique existence proof + * but for a set comprehension that is not over some other set `x`. To do so, + * A proof that the predicate `P(t)` implies membership to the set `x` must be + * given. This then asserts the unique existence of the set `{t | P(t)}`. Useful + * when a definition includes a redundant membership condition. + * + * `P(t) ==> t ∈ x ⊢ ∃! z. ∀ t. t ∈ z ⇔ P(t)` + * + * @param originalSet the set to apply comprehension on + * @param separationPredicate the predicate to use for comprehension `(Term => + * Term => Boolean)` + * @param predicateImpliesInOriginalSet proof of the form `P(t) ==> t ∈ originalSet` + * @return subproof for unique existence of the set defined by inputs + * + * @example + * Generates a subproof for the unique existence of the set `{t | ∃x. x ∈ a ∧ t = {x}}`: + * {{{ + * val implicationProof = have(exists(x, in(x, a) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(a, a)))) subproof { + * // ... + * } + * have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, a) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( + * union(cartesianProduct(a, a)), + * lambda(t, exists(x, in(x, a) /\ (t === singleton(x)))), + * implicationProof + * ) + * }}} + * See [[UniqueComprehension]] for more usage. + */ + def fromOriginalSet(using + proof: Proof, + line: sourcecode.Line, + file: sourcecode.File, + om: OutputManager + )(originalSet: Term, separationPredicate: LambdaTF[1], predicateImpliesInOriginalSet: proof.Fact)( // TODO dotty forgets that Term <:< LisaObject[Term] + bot: Sequent + ): proof.ProofTacticJudgement = { + require(separationPredicate.bounds.length == 1) + given lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary + // fresh variable names to avoid conflicts + val botWithAssumptions = bot ++ (proof.getAssumptions |- ()) + val takenIDs = (botWithAssumptions.freeVariables ++ separationPredicate.body.freeVariables ++ originalSet.freeVariables).map(_.id) + val t1 = Variable(freshId(takenIDs, x.id)) + val t2 = Variable(freshId(takenIDs, y.id)) + + val separationCondition = separationPredicate(t2) + val targetDef = ∀(t2, in(t2, t1) <=> separationCondition) + val comprehension = ∀(t2, in(t2, t1) <=> in(t2, originalSet) /\ separationCondition) + + // prepare predicateImpliesInOriginalSet for usage in a proof: rename variables + val predicateImpliesInOriginalSetForm = separationCondition ==> in(t2, originalSet) + val predicateImpliesInOriginalSetReady = matchFormula( + separationCondition ==> in(t2, originalSet), + predicateImpliesInOriginalSet.statement.right.head + ) match + case None => + return proof.InvalidProofTactic(s"Unable to unify `predicateImpliesInOriginalSet` with the expected form: ${predicateImpliesInOriginalSetForm}") + case Some((formulaSubst, termSubst)) => + predicateImpliesInOriginalSet + .of(formulaSubst.map((k, v) => SubstPair(k, v)).toSeq*) + .of(termSubst.map((k, v) => SubstPair(k, v)).toSeq*) + + val sp = TacticSubproof { + // get uniqueness with the redundant original set membership + val uniq = have(∃!(t1, comprehension)) by UniqueComprehension( + originalSet, + lambda(t2, separationCondition) + ) + + // show that existence of the definition with the original set membership implies the + // existence of the definition without the original set membership + val transform = have( + ∃(t1, comprehension) |- ∃(t1, targetDef) + ) subproof { + // derive equivalence between t ∈ x /\ P(t) and P(t) from `predicateImpliesInOriginalSet` + val lhs = have(separationCondition ==> (in(t2, originalSet) /\ separationCondition)) by Tautology.from(predicateImpliesInOriginalSetReady) + val rhs = have(separationCondition /\ in(t2, originalSet) ==> separationCondition) by Restate + val subst = have(separationCondition <=> (in(t2, originalSet) /\ separationCondition)) by RightIff(lhs, rhs) + + // subtitute and introduce quantifiers + have((in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) by Hypothesis + val cutRhs = thenHave( + (in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition), separationCondition <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (separationCondition) + ) by RightSubstIff.withParametersSimple(List((separationCondition, in(t2, originalSet) /\ separationCondition)), lambda(h, in(t2, t1) <=> h)) + have((in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (separationCondition)) by Cut(subst, cutRhs) + thenHave(∀(t2, in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (separationCondition)) by LeftForall + thenHave(∀(t2, in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- ∀(t2, in(t2, t1) <=> (separationCondition))) by RightForall + thenHave(∀(t2, in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- ∃(t1, ∀(t2, in(t2, t1) <=> (separationCondition)))) by RightExists + thenHave(thesis) by LeftExists + } + + val cutL = have( + ∃!(t1, comprehension) |- ∃(t1, comprehension) + ) by Restate.from(Quantifiers.existsOneImpliesExists of (P -> lambda(t1, comprehension))) + val cutR = have(∃(t1, targetDef) |- ∃!(t1, targetDef)) by Restate.from( + SetTheory.uniqueByExtension of (schemPred -> lambda(t2, separationCondition)) + ) + + val trL = have(∃!(t1, comprehension) |- ∃(t1, targetDef)) by Cut(cutL, transform) + val trR = have(∃!(t1, comprehension) |- ∃!(t1, targetDef)) by Cut(trL, cutR) + have(∃!(t1, targetDef)) by Cut.withParameters(∃!(t1, comprehension))(uniq, trR) + } + + // safely check, unwrap, and return the proof judgement + unwrapTactic(sp)("Subproof for unique comprehension failed.") + } + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala b/lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala new file mode 100644 index 000000000..1aeaf55ac --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala @@ -0,0 +1,254 @@ +package lisa.maths + +/** + * Implements theorems about first-order logic. + */ +object Quantifiers extends lisa.Main { + + private val x = variable + private val y = variable + private val z = variable + private val a = variable + private val p = formulaVariable + private val P = predicate[1] + private val Q = predicate[1] + + /** + * Theorem --- A formula is equivalent to itself universally quantified if + * the bound variable is not free in it. + */ + val closedFormulaUniversal = Theorem( + () |- ∀(x, p) <=> p + ) { + have(thesis) by Tableau + } + + /** + * Theorem --- A formula is equivalent to itself existentially quantified if + * the bound variable is not free in it. + */ + val closedFormulaExistential = Theorem( + () |- ∃(x, p) <=> p + ) { + have(thesis) by Tableau + } + + /** + * Theorem --- If there exists a *unique* element satisfying a predicate, + * then we can say there *exists* an element satisfying it as well. + */ + val existsOneImpliesExists = Theorem( + ∃!(x, P(x)) |- ∃(x, P(x)) + ) { + have((x === y) <=> P(y) |- (x === y) <=> P(y)) by Hypothesis + thenHave(∀(y, (x === y) <=> P(y)) |- (x === y) <=> P(y)) by LeftForall + thenHave(∀(y, (x === y) <=> P(y)) |- P(x)) by InstFunSchema(Map(y -> x)) + thenHave(∀(y, (x === y) <=> P(y)) |- ∃(x, P(x))) by RightExists + thenHave(∃(x, ∀(y, (x === y) <=> P(y))) |- ∃(x, P(x))) by LeftExists + thenHave(thesis) by Restate + } + + /** + * Theorem --- Equality relation is transitive. + */ + val equalityTransitivity = Theorem( + (x === y) /\ (y === z) |- (x === z) + ) { + have((x === y) |- (x === y)) by Hypothesis + thenHave(((x === y), (y === z)) |- (x === z)) by RightSubstEq.withParametersSimple(List((y, z)), lambda(y, x === y)) + thenHave(thesis) by Restate + } + + /** + * Theorem --- Conjunction and universal quantification commute. + */ + val universalConjunctionCommutation = Theorem( + () |- forall(x, P(x) /\ Q(x)) <=> forall(x, P(x)) /\ forall(x, Q(x)) + ) { + have(thesis) by Tableau + } + + /** + * Theorem -- Existential quantification distributes conjunction. + */ + val existentialConjunctionDistribution = Theorem( + exists(x, P(x) /\ Q(x)) |- exists(x, P(x)) /\ exists(x, Q(x)) + ) { + have(thesis) by Tableau + } + + /** + * Theorem -- Existential quantification fully distributes when the conjunction involves one closed formula. + */ + val existentialConjunctionWithClosedFormula = Theorem( + exists(x, P(x) /\ p) <=> (exists(x, P(x)) /\ p) + ) { + have(thesis) by Tableau + } + + /** + * Theorem -- If there is an equality on the existential quantifier's bound variable inside its body, then we can reduce + * the existential quantifier to the satisfaction of the remaining body. + */ + val equalityInExistentialQuantifier = Theorem( + exists(x, P(x) /\ (y === x)) <=> P(y) + ) { + have(exists(x, P(x) /\ (y === x)) |- P(y)) subproof { + have(P(x) |- P(x)) by Hypothesis + thenHave((P(x), y === x) |- P(y)) by RightSubstEq.withParametersSimple(List((y, x)), lambda(y, P(y))) + thenHave(P(x) /\ (y === x) |- P(y)) by Restate + thenHave(thesis) by LeftExists + } + val forward = thenHave(exists(x, P(x) /\ (y === x)) ==> P(y)) by Restate + + have(P(y) |- exists(x, P(x) /\ (y === x))) subproof { + have(P(x) /\ (y === x) |- P(x) /\ (y === x)) by Hypothesis + thenHave(P(x) /\ (y === x) |- exists(x, P(x) /\ (y === x))) by RightExists + thenHave(P(y) /\ (y === y) |- exists(x, P(x) /\ (y === x))) by InstFunSchema(Map(x -> y)) + thenHave(thesis) by Restate + } + val backward = thenHave(P(y) ==> exists(x, P(x) /\ (y === x))) by Restate + + have(thesis) by RightIff(forward, backward) + } + + /** + * Theorem --- Disjunction and existential quantification commute. + */ + val existentialDisjunctionCommutation = Theorem( + () |- exists(x, P(x) \/ Q(x)) <=> exists(x, P(x)) \/ exists(x, Q(x)) + ) { + have(thesis) by Tableau + } + + /** + * Theorem --- Universal quantification distributes over equivalence + */ + val universalEquivalenceDistribution = Theorem( + forall(z, P(z) <=> Q(z)) |- (forall(z, P(z)) <=> forall(z, Q(z))) + ) { + have(thesis) by Tableau + } + + /** + * Theorem --- Universal quantification of equivalence implies equivalence + * of existential quantification. + */ + val existentialEquivalenceDistribution = Theorem( + forall(z, P(z) <=> Q(z)) |- (exists(z, P(z)) <=> exists(z, Q(z))) + ) { + have(thesis) by Tableau + + } + + /** + * Theorem --- Universal quantification distributes over implication + */ + val universalImplicationDistribution = Theorem( + forall(z, P(z) ==> Q(z)) |- (forall(z, P(z)) ==> forall(z, Q(z))) + ) { + have(thesis) by Tableau + } + + /** + * Theorem --- Universal quantification of implication implies implication + * of existential quantification. + */ + val existentialImplicationDistribution = Theorem( + forall(z, P(z) ==> Q(z)) |- (exists(z, P(z)) ==> exists(z, Q(z))) + ) { + have(thesis) by Tableau + } + + /** + * Theorem --- Universal quantification of equivalence implies equivalence + * of unique existential quantification. + */ + val uniqueExistentialEquivalenceDistribution = Theorem( + forall(z, P(z) <=> Q(z)) |- (existsOne(z, P(z)) <=> existsOne(z, Q(z))) + ) { + val yz = have(forall(z, P(z) <=> Q(z)) |- ((y === z) <=> P(y)) <=> ((y === z) <=> Q(y))) subproof { + have(forall(z, P(z) <=> Q(z)) |- forall(z, P(z) <=> Q(z))) by Hypothesis + val quant = thenHave(forall(z, P(z) <=> Q(z)) |- P(y) <=> Q(y)) by InstantiateForall(y) + + val lhs = have((forall(z, P(z) <=> Q(z)), ((y === z) <=> P(y))) |- ((y === z) <=> Q(y))) subproof { + have((P(y) <=> Q(y), ((y === z) <=> P(y))) |- ((y === z) <=> Q(y))) by Tautology + have(thesis) by Tautology.from(lastStep, quant) + } + val rhs = have((forall(z, P(z) <=> Q(z)), ((y === z) <=> Q(y))) |- ((y === z) <=> P(y))) subproof { + have((P(y) <=> Q(y), ((y === z) <=> Q(y))) |- ((y === z) <=> P(y))) by Tautology + have(thesis) by Tautology.from(lastStep, quant) + } + + have(thesis) by Tautology.from(lhs, rhs) + } + + val fy = thenHave(forall(z, P(z) <=> Q(z)) |- forall(y, ((y === z) <=> P(y)) <=> ((y === z) <=> Q(y)))) by RightForall + + have(forall(y, P(y) <=> Q(y)) |- (forall(y, P(y)) <=> forall(y, Q(y)))) by Restate.from(universalEquivalenceDistribution) + val univy = thenHave(forall(y, ((y === z) <=> P(y)) <=> ((y === z) <=> Q(y))) |- (forall(y, ((y === z) <=> P(y))) <=> forall(y, ((y === z) <=> Q(y))))) by InstPredSchema( + Map((P -> lambda(y, (y === z) <=> P(y))), (Q -> lambda(y, (y === z) <=> Q(y)))) + ) + + have(forall(z, P(z) <=> Q(z)) |- (forall(y, ((y === z) <=> P(y))) <=> forall(y, ((y === z) <=> Q(y))))) by Cut(fy, univy) + + thenHave(forall(z, P(z) <=> Q(z)) |- forall(z, forall(y, ((y === z) <=> P(y))) <=> forall(y, ((y === z) <=> Q(y))))) by RightForall + have(forall(z, P(z) <=> Q(z)) |- exists(z, forall(y, ((y === z) <=> P(y)))) <=> exists(z, forall(y, ((y === z) <=> Q(y))))) by Cut( + lastStep, + existentialEquivalenceDistribution of (P -> lambda(z, forall(y, (y === z) <=> P(y))), Q -> lambda(z, forall(y, (y === z) <=> Q(y)))) + ) + + thenHave(thesis) by Restate + } + + /** + * Theorem --- if atleast two distinct elements exist, then there is no unique + * existence + */ + val atleastTwoExist = Theorem( + (exists(x, P(x)) /\ !existsOne(x, P(x))) <=> exists(x, exists(y, P(x) /\ P(y) /\ !(x === y))) + ) { + val fwd = have((exists(x, P(x)) /\ !existsOne(x, P(x))) ==> exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) subproof { + have((P(x), ((x === y) /\ !P(y))) |- P(x) /\ !P(y)) by Restate + thenHave((P(x), ((x === y) /\ !P(y))) |- P(y) /\ !P(y)) by Substitution.ApplyRules(x === y) // contradiction + val xy = thenHave((P(x), ((x === y) /\ !P(y))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by Weakening + + have((P(x), (!(x === y) /\ P(y))) |- (!(x === y) /\ P(y) /\ P(x))) by Restate + thenHave((P(x), (!(x === y) /\ P(y))) |- exists(y, !(x === y) /\ P(y) /\ P(x))) by RightExists + val nxy = thenHave((P(x), (!(x === y) /\ P(y))) |- exists(x, exists(y, !(x === y) /\ P(y) /\ P(x)))) by RightExists + + have((P(x), (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by Tautology.from(xy, nxy) + thenHave((P(x), exists(y, (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y)))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by LeftExists + thenHave((P(x), forall(x, exists(y, (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y))))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by LeftForall + thenHave((exists(x, P(x)), forall(x, exists(y, (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y))))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by LeftExists + + thenHave(thesis) by Restate + } + + val bwd = have(exists(x, exists(y, P(x) /\ P(y) /\ !(x === y))) ==> (exists(x, P(x)) /\ !existsOne(x, P(x)))) subproof { + have((P(x), P(y), !(x === y)) |- P(x)) by Restate + val ex = thenHave((P(x), P(y), !(x === y)) |- exists(x, P(x))) by RightExists + + have((P(x), P(y), !(x === y)) |- P(y) /\ !(y === x)) by Restate + thenHave((P(x), P(y), !(x === y), (x === z)) |- P(y) /\ !(y === z)) by Substitution.ApplyRules(x === z) + thenHave((P(x), P(y), !(x === y), (x === z)) |- (P(y) /\ !(y === z)) \/ (!P(y) /\ (y === z))) by Weakening + val xz = thenHave((P(x), P(y), !(x === y), (x === z)) |- exists(y, (P(y) /\ !(y === z)) \/ (!P(y) /\ (y === z)))) by RightExists + + have((P(x), P(y), !(x === y), !(x === z)) |- (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z))) by Restate + val nxz = thenHave((P(x), P(y), !(x === y), !(x === z)) |- exists(x, (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z)))) by RightExists + + have((P(x), P(y), !(x === y)) |- exists(x, (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z)))) by Tautology.from(xz, nxz) + thenHave((P(x), P(y), !(x === y)) |- forall(z, exists(x, (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z))))) by RightForall + val uex = thenHave(P(x) /\ P(y) /\ !(x === y) |- !existsOne(z, P(z))) by Restate + + have(P(x) /\ P(y) /\ !(x === y) |- exists(x, P(x)) /\ !existsOne(z, P(z))) by Tautology.from(ex, uex) + thenHave(exists(y, P(x) /\ P(y) /\ !(x === y)) |- exists(x, P(x)) /\ !existsOne(z, P(z))) by LeftExists + thenHave(exists(x, exists(y, P(x) /\ P(y) /\ !(x === y))) |- exists(x, P(x)) /\ !existsOne(z, P(z))) by LeftExists + + thenHave(thesis) by Restate + } + + have(thesis) by Tautology.from(fwd, bwd) + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala new file mode 100644 index 000000000..6330e4caa --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala @@ -0,0 +1,201 @@ +package lisa.maths.settheory + +import lisa.SetTheoryLibrary +import lisa.SetTheoryLibrary.* +import lisa.maths.settheory.functions.functional +import lisa.prooflib.BasicStepTactic.RightForall +import lisa.prooflib.BasicStepTactic.TacticSubproof +import lisa.prooflib.SimpleDeducedSteps.* +import lisa.utils.KernelHelpers.++<< +import lisa.utils.KernelHelpers.+<< +import lisa.utils.KernelHelpers.-<< +import lisa.utils.KernelHelpers.apply +import lisa.utils.{_, given} + +// Need to objects until https://github.com/lampepfl/dotty/pull/18647 is fixed. +// See also https://github.com/lampepfl/dotty/issues/18569 + +object Comprehensions { + import lisa.fol.FOL.{*, given} + import lisa.maths.settheory.SetTheory2.{primReplacement, replacement, functionalIsFunctional, onePointRule} + import lisa.automation.Tautology + import lisa.automation.Substitution + private val x = variable + private val y = variable + private val z = variable + private val A = variable + private val B = variable + private val C = variable + private val P = predicate[2] + private val Q = predicate[1] + private val Filter = predicate[1] + private val Map = function[1] + + given lib: lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary + + // Comprehension + + /** + * A Comprehension is a local definition of a set from a base set, a filter and a map. In Set builder notation, it denotes + * {map(x) | x in A /\ filter(x)}. + * It is represented by a variable usable locally in the proof. The assumptions corresponding to the definition of that variable are automatically eliminated. + * To obtain the defining property of the comprehension, use the [[elim]] fact. + * + * @param _proof the [[Proof]] in which the comprehension is valid + * @param t The base set + * @param filter A filter on elements of the base set + * @param map A map from elements of the base set to elements of the comprehension + * @param id The identifier of the variable encoding the comprehension. + */ + class Comprehension(_proof: Proof, val t: Term, val filter: (Term ** 1) |-> Formula, val map: (Term ** 1) |-> Term, id: Identifier) extends LocalyDefinedVariable(_proof, id) { + given proof.type = proof + + protected lazy val replacer: (Term ** 2) |-> Formula = lambda((A, B), filter(A) /\ (B === map(A))) + + private val mainFact = have( + ∃(B, ∀(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y)))).substitute(P := replacer) + ) subproof { + val s = have(thesis) by Tautology.from(primReplacement of (P := replacer), functionalIsFunctional of (Filter := filter, Map := map)) + } + + /** + * forall(y, in(y, B) <=> ∃(x, in(x, A) /\ filter(x) /\ (y === map(x)) + */ + override val definition: proof.Fact = assume(using proof)(forall(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y))).substitute(P := replacer, A := t, B := this)) + + val elem_bound = definingFormula.asInstanceOf[BinderFormula].bound + + protected val instDef: proof.Fact = { + have(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) by InstantiateForall(using SetTheoryLibrary, proof)(elem_bound)(definition) + } + + /** + * `in(elem, B) <=> ∃(x, in(x, A) /\ filter(x) /\ (elem === map(x))` + * if built with term.map, `in(elem, B) <=> ∃(x, in(x, A) /\ (elem === map(x))` + * if built with term.filter, `in(elem, B) <=> (in(elem, t) /\ filter(elem))` + */ + def elim(elem: Term) = instDef of (elem_bound := elem) + + // Add elimination to proof + { + val (compS, compI) = proof.sequentAndIntOfFact(mainFact of (A := t)) + val definU = definingFormula.underlying + val exDefinU = K.BinderFormula(K.Exists, underlyingLabel, definU) + _proof.addElimination( + definingFormula, + (i, sequent) => + val resSequent = (sequent.underlying -<< definU) + List( + SC.LeftExists(resSequent +<< exDefinU, i, definU, underlyingLabel), + SC.Cut(resSequent, compI, i + 1, exDefinU) + ) + ) + } + + def toStringFull: String = s"$id{${map(elem_bound)} | ${elem_bound} ∈ $t /\\ ${filter(elem_bound)}]}" + } + + // Replacement and Set Builders + + private def innerRepl(c: Variable, replacer: (Term ** 2) |-> Formula, t: Term): BinderFormula = // forall(x, in(x, y) <=> (in(x, t) /\ φ(x))) + ∀(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y) /\ ∀(z, P(x, z) ==> (z === y)))).substitute(P := replacer, A := t, y := c) + + // Axiom(exists(y, forall(x, in(x, y) <=> (in(x, z) /\ φ(x))))) + + class Replacement(_proof: Proof, val t: Term, val replacer: (Term ** 2) |-> Formula, id: Identifier) extends LocalyDefinedVariable(_proof, id) { + given proof.type = proof + + override val definition: proof.Fact = assume(using proof)(innerRepl(this, replacer, t)) + + val elem_bound = definingFormula.asInstanceOf[BinderFormula].bound + + protected val instDef: proof.Fact = { + InstantiateForall(using SetTheoryLibrary, proof)(elem_bound)(definition)(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) + .validate(summon[sourcecode.Line], summon[sourcecode.File]) + } + + // Add elimination to proof + { + val (compS, compI) = proof.sequentAndIntOfFact(replacement of (P := replacer, z := t, y := this)) + + val definU = definingFormula.underlying + val exDefinU = K.BinderFormula(K.Exists, underlyingLabel, definU) + + _proof.addElimination( + definingFormula, + (i, sequent) => + + val resSequent = (sequent.underlying -<< definU) + List( + SC.LeftExists(resSequent +<< exDefinU, i, definU, underlyingLabel), + SC.Cut(resSequent, compI, i + 1, exDefinU) + ) + ) + } + + /** + * in(elem, y) <=> ∃(x, in(x, t) /\ replacer(x, y) /\ ∀(z, P(x, z) ==> (z === y)) + */ + def elim(elem: Term): proof.Fact = instDef of (elem_bound := elem) + + def toStringFull: String = s"$id{$elem_bound | ${definition.asInstanceOf[BinderFormula].body.asInstanceOf[AppliedConnector].args(1)}]}" + } + + extension (t: Term) { + def replace(using _proof: Proof, name: sourcecode.Name)(replacer: (Term ** 2) |-> Formula): Replacement { val proof: _proof.type } = { + if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.allSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") + val id = name.value + val c = Replacement(_proof, t, replacer, id) + c.asInstanceOf[Replacement { val proof: _proof.type }] + } + + def collect(using _proof: Proof, name: sourcecode.Name)(filter: (Term ** 1) |-> Formula, map: (Term ** 1) |-> Term): Comprehension { val proof: _proof.type } = { + if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.allSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") + val id = name.value + val c = new Comprehension(_proof, t, filter, map, id) + c.asInstanceOf[Comprehension { val proof: _proof.type }] + } + + def map(using _proof: Proof, name: sourcecode.Name)(map: (Term ** 1) |-> Term): Comprehension { val proof: _proof.type } = { + if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.allSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") + val id = name.value + inline def _map = map + inline def _t = t + val c = new Comprehension(_proof, t, lambda(x, top), map, id) { + + override val instDef: proof.Fact = { + val elim_formula = (forall(elem_bound, in(elem_bound, B) <=> ∃(x, in(x, A) /\ P(x, elem_bound))).substitute(P := lambda((A, B), B === _map(A)), A := _t, B := this)).body + + have(TacticSubproof(using proof) { + val s = have(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) by InstantiateForall(elem_bound)(definition) + thenHave(definingFormula |- elim_formula) by Restate.from + }) + + } + } + c.asInstanceOf[Comprehension { val proof: _proof.type }] + } + + def filter(using _proof: Proof, name: sourcecode.Name)(filter: (Term ** 1) |-> Formula): Comprehension { val proof: _proof.type } = { + if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.freeSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") + val id = name.value + inline def _filter = filter + inline def _t = t + val c = new Comprehension(_proof, t, filter, lambda(x, x), id) { + + override val instDef: proof.Fact = { + have(TacticSubproof(using proof) { + val ex = new Variable(freshId(definingFormula.allSchematicLabels.map(_.id), "x")) + have(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) by InstantiateForall(elem_bound)(definition) + have(in(elem_bound, this) <=> ∃(ex, (ex === elem_bound) /\ in(ex, _t) /\ _filter(ex))) by Tautology.from(lastStep) + thenHave(in(elem_bound, this) <=> (in(elem_bound, _t) /\ _filter(elem_bound))) by Substitution.ApplyRules(onePointRule of (y := elem_bound, Q := lambda(ex, in(ex, _t) /\ _filter(ex)))) + }) + } + + } + c.asInstanceOf[Comprehension { val proof: _proof.type }] + } + + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala new file mode 100644 index 000000000..cbc135a14 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala @@ -0,0 +1,159 @@ +package lisa.maths + +//import lisa.automation.settheory.SetTheoryTactics.* +//import lisa.maths.Quantifiers.* +//import lisa.automation.Substitution + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.settheory.SetTheory.* + +object InductiveSets extends lisa.Main { + + // var defs + private val a = variable + private val x = variable + private val y = variable + private val z = variable + private val r = variable + private val t = variable + + // relation and function symbols + + private val P = predicate[1] + private val schemPred = predicate[1] + + /** + * Chapter 2 + * Ordinal Numbers + */ + + /** + * Inductive and transitive sets + */ + + /** + * Theorem --- There exists an intersection of all inductive sets + */ + val inductiveIntersectionExistence = Theorem( + ∃(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y)))) + ) { + val inductExt = + have(∃(x, inductive(x)) |- ∃(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y))))) by InstPredSchema(Map(P -> lambda(x, inductive(x))))(intersectionOfPredicateClassExists) + have(∃(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y))))) by Cut(inductiveSetExists, inductExt) + } + + /** + * Theorem --- The intersection of all inductive sets is unique + */ + val inductiveIntersectionUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y)))) + ) { + val prop = ∀(y, inductive(y) ==> in(t, y)) + val fprop = ∀(t, in(t, z) <=> prop) + + val existsRhs = have(∃(z, fprop) |- ∃!(z, fprop)) by InstPredSchema(Map(schemPred -> (t, prop)))(uniqueByExtension) + val existsLhs = have(∃(z, fprop)) by Restate.from(inductiveIntersectionExistence) + + have(∃!(z, fprop)) by Cut(existsLhs, existsRhs) + } + + /** + * Natural Numbers (Inductive definition) --- The intersection of all + * inductive sets is the set of natural numbers, N. + */ + val naturalsInductive = DEF() --> The(z, ∀(t, in(t, z) <=> (∀(y, inductive(y) ==> in(t, y)))))(inductiveIntersectionUniqueness) + + /** + * Theorem --- Natural numbers form an inductive set + */ + val naturalsAreInductive = Theorem( + inductive(naturalsInductive) + ) { + val defHypo = have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- ∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) by Hypothesis + + // emptySet is in N + have(inductive(x) ==> in(∅, x)) by Weakening(inductive.definition) + val inductEmpty = thenHave(∀(x, inductive(x) ==> in(∅, x))) by RightForall + + val defEmpty = + have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(∅, z) <=> (∀(x, inductive(x) ==> in(∅, x))))) by InstantiateForall(∅)(defHypo) + + have( + ∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(∅, z) <=> (∀(x, inductive(x) ==> in(∅, x)))) /\ ∀(x, inductive(x) ==> in(∅, x)) + ) by RightAnd(defEmpty, inductEmpty) + val baseCase = thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- in(∅, z)) by Tautology + + // if y in N, then succ y in N + have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) by InstantiateForall(t)(defHypo) + thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z) |- (in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) by Weakening + thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z) |- (∀(x, inductive(x) ==> in(t, x)))) by Tautology + thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z) |- (inductive(x) ==> in(t, x))) by InstantiateForall(x) + val inInductive = thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z), inductive(x)) |- in(t, x)) by Restate + + have(inductive(x) |- ∀(t, in(t, x) ==> in(successor(t), x))) by Weakening(inductive.definition) + val inInductiveDef = thenHave(inductive(x) |- in(t, x) ==> in(successor(t), x)) by InstantiateForall(t) + + have((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z), inductive(x)) |- in(t, x) /\ (in(t, x) ==> in(successor(t), x))) by RightAnd(inInductive, inInductiveDef) + thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z), inductive(x)) |- in(successor(t), x)) by Tautology + thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- inductive(x) ==> in(successor(t), x)) by Restate + val succInst = thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- ∀(x, inductive(x) ==> in(successor(t), x))) by RightForall + + val nDefSucc = + have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(successor(t), z) <=> (∀(x, inductive(x) ==> in(successor(t), x))))) by InstantiateForall(successor(t))(defHypo) + have( + (∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- ∀(x, inductive(x) ==> in(successor(t), x)) /\ (in(successor(t), z) <=> (∀( + x, + inductive(x) ==> in(successor(t), x) + ))) + ) by RightAnd(succInst, nDefSucc) + thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- in(successor(t), z)) by Tautology + thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) |- in(t, z) ==> in(successor(t), z)) by Restate + val inductiveCase = thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- ∀(t, in(t, z) ==> in(successor(t), z))) by RightForall + + have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- in(∅, z) /\ ∀(t, in(t, z) ==> in(successor(t), z))) by RightAnd(baseCase, inductiveCase) + + val form = formulaVariable + val inductIff = thenHave( + (∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), inductive(z) <=> (in(∅, z) /\ ∀(y, in(y, z) ==> in(successor(y), z)))) |- inductive(z) + ) by RightSubstIff.withParametersSimple(List((inductive(z), in(∅, z) /\ ∀(y, in(y, z) ==> in(successor(y), z)))), lambda(form, form)) + + val inductDef = have(inductive(z) <=> (in(∅, z) /\ ∀(y, in(y, z) ==> in(successor(y), z)))) by InstFunSchema(Map(x -> z))(inductive.definition) + + have((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) |- inductive(z)) by Cut(inductDef, inductIff) + val inductExpansion = + thenHave((forall(t, in(t, naturalsInductive) <=> (forall(x, inductive(x) ==> in(t, x))))) |- inductive(naturalsInductive)) by InstFunSchema(Map(z -> naturalsInductive)) + + have((naturalsInductive === naturalsInductive) <=> forall(t, in(t, naturalsInductive) <=> (forall(x, inductive(x) ==> in(t, x))))) by InstantiateForall(naturalsInductive)( + naturalsInductive.definition + ) + val natDef = thenHave(forall(t, in(t, naturalsInductive) <=> forall(x, inductive(x) ==> in(t, x)))) by Restate + + have(inductive(naturalsInductive)) by Cut(natDef, inductExpansion) + } + + private val A = variable + + /** + * A set `'A` is transitive if and only if every member of `'A` is a subset of `'A`. + * ∀ 'x. 'x∈ 'A ⟹ 'x ⊂ 'A + */ + val transitiveSet = DEF(A) --> forall(x, in(x, A) ==> subset(x, A)) + + /* + private val R = predicate(2) + /** + * Show that the restriction of a functional to a set exists. + */ + val predicateRestrictionExists = makeTHM( + ∃!(r, forall(x, forall(y, in(pair(x, y), r) <=> in(x, A) /\ in(y, A) /\ R(x, y)))) + ) { + val z1 = firstInPair(z) + val z2 = secondInPair(z) + + have ( ∃!(r, forall(z, in(z, r) <=> in(z, cartesianProduct(A, A)) /\ R(z1, z2)))) by UniqueComprehension(cartesianProduct(A, A), lambda(Seq(z, x), R(z1, z2))) + showCurrentProof() + + } + */ + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala new file mode 100644 index 000000000..3439bad44 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala @@ -0,0 +1,2614 @@ +package lisa.maths.settheory + +import lisa.automation.kernel.CommonTactics.Definition +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* + +import scala.collection.immutable.{Map => ScalaMap} + +/** + * Set Theory Library + * + * Develops Zermelo-Fraenkel Set Theory. + * Uses the following book as the main reference: + * + * Jech, Thomas. Set theory: The third millennium edition, revised and expanded. + * Springer Berlin Heidelberg, 2003. + * [[https://link.springer.com/book/10.1007/3-540-44761-X]] + */ +object SetTheory extends lisa.Main { + + // var defs + private val w = variable + private val x = variable + private val y = variable + private val z = variable + private val h = formulaVariable + private val t = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + + // relation and function symbols + private val r = variable + private val p = variable + private val f = variable + private val g = variable + + private val P = predicate[1] + private val Q = predicate[1] + private val R = predicate[2] + private val schemPred = predicate[1] + + /** + * Chapter 1 + */ + + /** + * Axioms of Set Theory + * + * See [[SetTheoryZAxioms]] and [[SetTheoryZFAxioms]] + */ + + /** + * Theorems about basic sets + */ + + /** + * Theorem --- Russel's Paradox + * + * Consider a set `x`, that contains every set that is not a member of itself. + * The existence of `x` leads to a contradiction. This paradox forces the + * current form of the comprehension schema, i.e. `{x ∈ X | Ψ(x, X)}` + * instead of the full comprehension schema `{x | Ψ(x)}`. + */ + val russelsParadox = Theorem( + ∃(x, ∀(y, !in(y, y) <=> in(y, x))) |- () + ) { + val contra = !in(x, x) <=> in(x, x) + + have(contra |- ()) by Restate + thenHave(∀(y, !in(y, y) <=> in(y, x)) |- ()) by LeftForall + thenHave(∃(x, ∀(y, !in(y, y) <=> in(y, x))) |- ()) by LeftExists + } + + /** + * Theorem --- Uniqueness by Definition + * + * If a set is defined by its elements, existence implies uniqueness. + * + * `∃ z. ∀ t. t ∈ z ⇔ P(t) ⊢ ∃! z. ∀ t. t ∈ z ⇔ P(t)` + * + * where `P(t)` does not contain `z` as a free variable. + * + * @example {{{ + * have(∃(z, ∀(t, in(t, z) ⇔ myProperty(t))) ⊢ ∃!(z, ∀(t, in(t, z) ⇔ myProperty(t)))) by InstPredSchema(ScalaMap(schemPred -> (t, myProperty(t))))` + * }}} + * + * Instantiation will fail if `myProperty(t)` contains `z` as a free variable. + */ + val uniqueByExtension = Theorem( + ∃(z, ∀(t, in(t, z) <=> schemPred(t))) |- ∃!(z, ∀(t, in(t, z) <=> schemPred(t))) + ) { + def prop(z: Term) = in(t, z) <=> schemPred(t) + def fprop(z: Term) = ∀(t, prop(z)) + + // forward direction + have(fprop(z) |- fprop(z)) by Hypothesis + thenHave(fprop(z) /\ (z === a) |- fprop(z)) by Weakening + thenHave((fprop(z) /\ (z === a), (z === a)) |- fprop(a)) by RightSubstEq.withParametersSimple(List((z, a)), lambda(Seq(z), fprop(z))) + val forward = thenHave(fprop(z) |- (z === a) ==> fprop(a)) by Restate + + // backward direction + have(fprop(z) |- fprop(z)) by Hypothesis + val instLhs = thenHave(fprop(z) |- prop(z)) by InstantiateForall(t) + val instRhs = thenHave(fprop(a) |- prop(a)) by InstFunSchema(ScalaMap(z -> a)) + + have((fprop(z), fprop(a)) |- prop(z) /\ prop(a)) by RightAnd(instLhs, instRhs) + thenHave(fprop(z) /\ fprop(a) |- in(t, a) <=> in(t, z)) by Tautology + val extLhs = thenHave(fprop(z) /\ fprop(a) |- ∀(t, in(t, a) <=> in(t, z))) by RightForall + val extRhs = have(∀(t, in(t, a) <=> in(t, z)) <=> (a === z)) by InstFunSchema(ScalaMap(x -> a, y -> z))(extensionalityAxiom) + + have(fprop(z) /\ fprop(a) |- (∀(t, in(t, a) <=> in(t, z)) <=> (a === z)) /\ ∀(t, in(t, a) <=> in(t, z))) by RightAnd(extLhs, extRhs) + thenHave(fprop(z) /\ fprop(a) |- (a === z)) by Tautology + val backward = thenHave(fprop(z) |- fprop(a) ==> (a === z)) by Restate + + have(fprop(z) |- fprop(a) <=> (a === z)) by RightIff(forward, backward) + thenHave(fprop(z) |- ∀(a, fprop(a) <=> (a === z))) by RightForall + thenHave(fprop(z) |- ∃(z, ∀(a, fprop(a) <=> (a === z)))) by RightExists + thenHave(∃(z, fprop(z)) |- ∃(z, ∀(a, fprop(a) <=> (a === z)))) by LeftExists + thenHave(∃(z, fprop(z)) |- ∃!(z, fprop(z))) by RightExistsOne + } + + ////////////////////////////////////////////////////////////////////////////// + + /** + * Shorthand definitions + */ + + /** + * Proper Subset --- `x ⊂ y`. Shorthand for `x ⊆ y ∧ x != y`. + * + * @param x set + * @param y set + */ + def properSubset(x: Term, y: Term) = subset(x, y) /\ !(x === y) + + /** + * Singleton Set --- `{x}`. Shorthand for `{x, x}`. + * + * @param x set + */ + def singleton(x: Term) = unorderedPair(x, x) + + /** + * Ordered Pair --- `(x, y)`. Shorthand for `{{x}, {x, y}}`. + * + * @param x set + * @param y set + */ + def pair(x: Term, y: Term) = unorderedPair(unorderedPair(x, y), singleton(x)) + + /** + * Binary Set Union --- `x ∪ y = ∪ {x, y}` + * + * @param x set + * @param y set + */ + val setUnion: ConstantFunctionLabel[2] = DEF(x, y) --> union(unorderedPair(x, y)) + + /** + * Theorem --- a set is an element of `x ∪ y` iff it is an element of `x` or `y` + */ + val setUnionMembership = Theorem( + in(z, setUnion(x, y)) <=> (in(z, x) \/ in(z, y)) + ) { + have(∀(z, (z === setUnion(x, y)) <=> (z === union(unorderedPair(x, y))))) by Restate.from(setUnion.definition) + thenHave((setUnion(x, y) === setUnion(x, y)) <=> (setUnion(x, y) === union(unorderedPair(x, y)))) by InstantiateForall(setUnion(x, y)) + val unionDef = thenHave((setUnion(x, y) === union(unorderedPair(x, y)))) by Restate + + val upairax = have(in(a, unorderedPair(x, y)) <=> ((a === x) \/ (a === y))) by Restate.from(pairAxiom of (z -> a)) + + val ta = have(in(z, union(unorderedPair(x, y))) <=> ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate.from(unionAxiom of (x -> unorderedPair(x, y))) + + have(thesis) subproof { + // the proof proceeds by showing that the existence criterion reduces to the RHS of the iff in the thesis + + val fwd = have(∃(a, in(z, a) /\ in(a, unorderedPair(x, y))) ==> (in(z, x) \/ in(z, y))) subproof { + have((in(z, a), a === x) |- in(z, a)) by Hypothesis + val tax = thenHave((in(z, a), a === x) |- in(z, x)) by RightSubstEq.withParametersSimple(List((a, x)), lambda(a, in(z, a))) + + have((in(z, a), a === y) |- in(z, a)) by Hypothesis + val tay = thenHave((in(z, a), a === y) |- in(z, y)) by RightSubstEq.withParametersSimple(List((a, y)), lambda(a, in(z, a))) + + have((in(z, a), (a === x) \/ (a === y)) |- (in(z, x), in(z, y))) by LeftOr(tax, tay) + thenHave((in(z, a), in(a, unorderedPair(x, y))) |- (in(z, x), in(z, y))) by Substitution.ApplyRules(upairax) + thenHave((in(z, a) /\ in(a, unorderedPair(x, y))) |- (in(z, x), in(z, y))) by Restate + thenHave(∃(a, in(z, a) /\ in(a, unorderedPair(x, y))) |- (in(z, x), in(z, y))) by LeftExists + thenHave(thesis) by Restate + } + + val bwd = have(((in(z, x) \/ in(z, y)) ==> ∃(a, in(z, a) /\ in(a, unorderedPair(x, y))))) subproof { + have((in(z, x), (a === x)) |- in(z, x)) by Hypothesis + thenHave((in(z, x), (a === x)) |- in(z, a)) by RightSubstEq.withParametersSimple(List((a, x)), lambda(a, in(z, a))) + thenHave((in(z, x), (a === x)) |- in(z, a) /\ ((a === x) \/ (a === y))) by Tautology + andThen(Substitution.applySubst(upairax, false)) + thenHave((in(z, x), (a === x)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by RightExists + thenHave((in(z, x), (x === x)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by InstFunSchema(ScalaMap(a -> x)) + val tax = thenHave((in(z, x)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate + + have((in(z, y), (a === y)) |- in(z, y)) by Hypothesis + thenHave((in(z, y), (a === y)) |- in(z, a)) by RightSubstEq.withParametersSimple(List((a, y)), lambda(a, in(z, a))) + thenHave((in(z, y), (a === y)) |- in(z, a) /\ ((a === x) \/ (a === y))) by Tautology + andThen(Substitution.applySubst(upairax, false)) + thenHave((in(z, y), (a === y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by RightExists + thenHave((in(z, y), (y === y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by InstFunSchema(ScalaMap(a -> y)) + val tay = thenHave((in(z, y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate + + have((in(z, x) \/ in(z, y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by LeftOr(tax, tay) + thenHave(thesis) by Restate + } + + val existsSubst = have(∃(a, in(z, a) /\ in(a, unorderedPair(x, y))) <=> (in(z, x) \/ in(z, y))) by RightIff(fwd, bwd) + + have(in(z, union(unorderedPair(x, y))) <=> ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate.from(ta) + thenHave(thesis) by Substitution.ApplyRules(existsSubst, unionDef) + } + + } + + /** + * Successor Function --- Maps a set to its 'successor' in the sense required + * for an inductive set. + * + * `successor: x ↦ x ∪ {x}` + * + * @param x set + */ + val successor = DEF(x: Variable) --> union(unorderedPair(x, singleton(x))) + + /** + * Inductive set --- A set is inductive if it contains the empty set, and the + * [[successor]]s of each of its elements. + * + * `inductive(x) ⇔ (∅ ∈ x ⋀ ∀ y. y ∈ x ⇒ successor(y) ∈ x)` + * + * @param x set + */ + val inductive = DEF(x) --> in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)) + + /** + * Theorem --- There exists an inductive set. + * + * `∃ x. inductive(x)` + * + * Equivalent to the Axiom of Infinity ([[infinityAxiom]]). The proof shows + * that the two forms are equivalent by folding the definitions of + * [[successor]] and [[inductive]]. + */ + val inductiveSetExists = Theorem( + ∃(x, inductive(x)) + ) { + val form = formulaVariable + + have(∀(x, (x === successor(y)) <=> (x === union(unorderedPair(y, unorderedPair(y, y)))))) by InstFunSchema(ScalaMap(x -> y))(successor.definition) + thenHave(((successor(y) === successor(y)) <=> (successor(y) === union(unorderedPair(y, unorderedPair(y, y)))))) by InstantiateForall(successor(y)) + val succDef = thenHave((successor(y) === union(unorderedPair(y, unorderedPair(y, y))))) by Restate + val inductDef = have(inductive(x) <=> in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x))) by Restate.from(inductive.definition) + + have((in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by Restate + val succEq = thenHave( + (successor(y) === union(unorderedPair(y, unorderedPair(y, y)))) |- (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(successor(y), x)) + ) by RightSubstEq.withParametersSimple( + List((successor(y), union(unorderedPair(y, unorderedPair(y, y))))), + lambda(z, (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(z, x))) + ) + val iffinst = have((in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(successor(y), x))) by Cut(succDef, succEq) + + val iffquant = { + have((in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- (in(y, x) ==> in(successor(y), x))) by Weakening(iffinst) + thenHave(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- (in(y, x) ==> in(successor(y), x))) by LeftForall + thenHave(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- ∀(y, in(y, x) ==> in(successor(y), x))) by RightForall + val lhs = thenHave(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) ==> ∀(y, in(y, x) ==> in(successor(y), x))) by Restate + + have((in(y, x) ==> in(successor(y), x)) |- (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by Weakening(iffinst) + thenHave(∀(y, in(y, x) ==> in(successor(y), x)) |- (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by LeftForall + thenHave(∀(y, in(y, x) ==> in(successor(y), x)) |- ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by RightForall + val rhs = thenHave(∀(y, in(y, x) ==> in(successor(y), x)) ==> ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by Restate + + have(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x))) by RightIff(lhs, rhs) + } + + have( + in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- in(∅, x) /\ ∀( + y, + in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x) + ) + ) by Hypothesis + thenHave( + ( + ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x)), + in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) + ) |- in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)) + ) by RightSubstIff.withParametersSimple( + List((∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)), ∀(y, in(y, x) ==> in(successor(y), x)))), + lambda(form, in(∅, x) /\ form) + ) + val substituted = thenHave( + ( + inductive(x) <=> in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)), + ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x)), + in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) + ) |- inductive(x) + ) by RightSubstIff.withParametersSimple(List((inductive(x), in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)))), lambda(form, form)) + val cut1 = have( + ( + ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x)), + in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) + ) |- inductive(x) + ) by Cut(inductDef, substituted) + val cut2 = have((in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) |- inductive(x)) by Cut(iffquant, cut1) + + thenHave((in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) |- ∃(x, inductive(x))) by RightExists + val rhs = thenHave((∃(x, in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)))) |- ∃(x, inductive(x))) by LeftExists + + have(∃(x, inductive(x))) by Cut(infinityAxiom, rhs) + } + + ////////////////////////////////////////////////////////////////////////////// + + /** + * Properties about the empty set and power sets + */ + + /** + * Theorem --- If a set has an element, then it is not the empty set. + * + * `y ∈ x ⊢ ! x = ∅` + */ + val setWithElementNonEmpty = Theorem( + in(y, x) |- !(x === ∅) + ) { + have((x === ∅) |- !in(y, x)) by Substitution.ApplyRules(x === ∅)(emptySetAxiom of (x := y)) + } + + /** + * Theorem --- A set containing no elements is equal to the empty set. + * + * `∀ y. ! y ∈ x ⊢ x = ∅` + * + * Converse of the empty set axiom ([[emptySetAxiom]]). + */ + val setWithNoElementsIsEmpty = Theorem( + ∀(y, !in(y, x)) |- (x === ∅) + ) { + have(!in(y, ∅)) by InstFunSchema(ScalaMap(x -> y))(emptySetAxiom) + thenHave(() |- (!in(y, ∅), in(y, x))) by Weakening + val lhs = thenHave(in(y, ∅) ==> in(y, x)) by Restate + + have(!in(y, x) |- !in(y, x)) by Hypothesis + thenHave(!in(y, x) |- (!in(y, x), in(y, ∅))) by Weakening + val rhs = thenHave(!in(y, x) |- in(y, x) ==> in(y, ∅)) by Restate + + have(!in(y, x) |- in(y, x) <=> in(y, ∅)) by RightIff(lhs, rhs) + thenHave(∀(y, !in(y, x)) |- in(y, x) <=> in(y, ∅)) by LeftForall + val exLhs = thenHave(∀(y, !in(y, x)) |- ∀(y, in(y, x) <=> in(y, ∅))) by RightForall + + have(∀(z, in(z, x) <=> in(z, ∅)) <=> (x === ∅)) by InstFunSchema(ScalaMap(x -> x, y -> ∅))(extensionalityAxiom) + val exRhs = thenHave(∀(y, in(y, x) <=> in(y, ∅)) <=> (x === ∅)) by Restate + + have(∀(y, !in(y, x)) |- (∀(y, in(y, x) <=> in(y, ∅)) <=> (x === ∅)) /\ ∀(y, in(y, x) <=> in(y, ∅))) by RightAnd(exLhs, exRhs) + thenHave(∀(y, !in(y, x)) |- (x === ∅)) by Tautology + } + + /** + * Theorem --- The empty set is a subset of every set. + * + * `∅ ⊆ x` + */ + val emptySetIsASubset = Theorem( + subset(∅, x) + ) { + have(in(y, ∅) ==> in(y, x)) by Weakening(emptySetAxiom of (x := y)) + val rhs = thenHave(∀(y, in(y, ∅) ==> in(y, x))) by RightForall + have(thesis) by Tautology.from(subsetAxiom of (x := ∅, y := x), rhs) + } + + /** + * Theorem --- If a set is a subset of the empty set, it is empty. + * + * `x ⊆ ∅ <=> a = ∅` + */ + val emptySetIsItsOwnOnlySubset = Theorem( + subset(x, emptySet) <=> (x === emptySet) + ) { + val fwd = have(subset(x, emptySet) |- (x === emptySet)) subproof { + have(subset(x, emptySet) |- forall(z, in(z, x) ==> in(z, emptySet))) by Weakening(subsetAxiom of y -> emptySet) + thenHave(subset(x, emptySet) |- in(z, x) ==> in(z, emptySet)) by InstantiateForall(z) + have(subset(x, emptySet) |- !in(z, x)) by Tautology.from(lastStep, emptySetAxiom of x -> z) + thenHave(subset(x, emptySet) |- forall(z, !in(z, x))) by RightForall + + have(thesis) by Cut(lastStep, setWithNoElementsIsEmpty) + } + + val bwd = have((x === emptySet) |- subset(x, emptySet)) subproof { + have(subset(emptySet, emptySet)) by Restate.from(emptySetIsASubset of x -> emptySet) + thenHave(thesis) by Substitution.ApplyRules(x === emptySet) + } + + have(thesis) by Tautology.from(fwd, bwd) + } + + /** + * Theorem --- A power set is never empty. + * + * `! P(x) = ∅` + */ + val powerSetNonEmpty = Theorem( + !(powerSet(x) === ∅) + ) { + have(thesis) by Tautology.from(emptySetIsASubset, powerAxiom of (x := ∅, y := x), setWithElementNonEmpty of (y := ∅, x := powerSet(x))) + } + + /** + * Theorem --- Subset reflexivity + * + * Every set is a [[subset]] of itself. In other words, the [[subset]] + * predicate induces a [[reflexive]] [[relation]] on sets. + */ + val subsetReflexivity = Theorem( + subset(x, x) + ) { + val subdef = have(subset(x, x) <=> ∀(z, ⊤)) by Restate.from(subsetAxiom of (y -> x)) + thenHave(subset(x, x) <=> ⊤) by Substitution.ApplyRules(closedFormulaUniversal) + thenHave(thesis) by Restate + } + + /** + * Theorem --- If a set belongs to a [[singleton]], it must be the single element. + * + * `y ∈ {x} <=> y = x` + */ + val singletonHasNoExtraElements = Theorem( + in(y, singleton(x)) <=> (x === y) + ) { + // specialization of the pair axiom to a singleton + + have(in(y, unorderedPair(x, x)) <=> (x === y) \/ (x === y)) by InstFunSchema(ScalaMap(x -> x, y -> x, z -> y))(pairAxiom) + thenHave(in(y, singleton(x)) <=> (x === y)) by Restate + } + + /** + * Theorem --- Every set is a member of its power set. + * + * `x ∈ P(x)` + */ + val elemInItsPowerSet = Theorem( + in(x, powerSet(x)) + ) { + have(thesis) by Tautology.from(powerAxiom of (y := x), subsetReflexivity) + } + + /** + * Lemma --- The power set of the empty set is the set containing the empty set. + * + * `P(∅) = {∅}` + */ + val powerSetEmptySet = Theorem( + powerSet(∅) === singleton(∅) + ) { + val powerAx = have(in(x, powerSet(∅)) <=> subset(x, ∅)) by Weakening(powerAxiom of (y := ∅)) + + have(in(x, powerSet(∅)) <=> in(x, singleton(∅))) by Tautology.from( + powerAx, + emptySetIsItsOwnOnlySubset, + singletonHasNoExtraElements of (y := x, x := ∅) + ) + val ext = thenHave(∀(x, in(x, powerSet(∅)) <=> in(x, singleton(∅)))) by RightForall + + have(thesis) by Tautology.from(ext, extensionalityAxiom of (x := powerSet(∅), y := singleton(∅))) + } + + ////////////////////////////////////////////////////////////////////////////// + + /** + * Properties about pairs + */ + + /** + * Theorem --- First Element in Pair + * + * `x ∈ {x, y}` + * + * Unfolds the definition of [[unorderedPair]]. Easier to use in theorems than + * the complete definition. + */ + val firstElemInPair = Theorem( + in(x, unorderedPair(x, y)) + ) { + have(thesis) by Tautology.from(pairAxiom of (z := x)) + } + + /** + * Theorem --- Second Element in Pair + * + * `y ∈ {x, y}` + * + * Unfolds the definition of [[unorderedPair]]. Easier to use in theorems than + * the complete definition. + */ + val secondElemInPair = Theorem( + in(y, unorderedPair(x, y)) + ) { + val lhs = have(in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) by InstFunSchema(ScalaMap(x -> x, y -> y, z -> z))(pairAxiom) + have((z === y) |- (z === y)) by Hypothesis + val rhs = thenHave((z === y) |- (z === x) \/ (z === y)) by Restate + val factset = have((z === y) |- (in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) /\ ((z === x) \/ (z === y))) by RightAnd(lhs, rhs) + + thenHave((z === y) |- in(z, unorderedPair(x, y))) by Tautology + thenHave((y === y) |- in(y, unorderedPair(x, y))) by InstFunSchema(ScalaMap(z -> y)) + thenHave(in(y, unorderedPair(x, y))) by LeftRefl + } + + /** + * Theorem --- The [[unorderedPair]] is, in fact, unordered. + * + * `{x, y} = {y, x}` + */ + val unorderedPairSymmetry = Theorem( + unorderedPair(x, y) === unorderedPair(y, x) + ) { + have(in(z, unorderedPair(y, x)) <=> in(z, unorderedPair(x, y))) by Substitution.ApplyRules(pairAxiom)(pairAxiom of (x := y, y := x)) + thenHave(∀(z, in(z, unorderedPair(x, y)) <=> in(z, unorderedPair(y, x)))) by RightForall + thenHave(thesis) by Substitution.ApplyRules(extensionalityAxiom) + } + + val unorderedPairDeconstruction = Theorem( + (unorderedPair(a, b) === unorderedPair(c, d)) ⊢ (((a === c) ∧ (b === d)) ∨ ((a === d) ∧ (b === c))) + ) { + val s1 = have(Substitution.applySubst(unorderedPair(a, b) === unorderedPair(c, d))(pairAxiom of (x -> a, y -> b))) + val base = have(Substitution.applySubst(s1)(pairAxiom of (x -> c, y -> d))) + + have(thesis) by Tautology.from(base of (z -> a), base of (z -> b), base of (z -> c), base of (z -> d)) + } + + /** + * Theorem --- Union of a Singleton is the Original Set + * + * The unary [[union]] operation unfolds a [[singleton]] into the single + * element + * + * `∀ x. ∪ {x} === x` + */ + val unionOfSingletonIsTheOriginalSet = Theorem((union(singleton(x)) === x)) { + val X = singleton(x) + val forward = have((in(z, x) ==> in(z, union(X)))) subproof { + have(in(z, x) |- in(z, x) /\ in(x, X)) by Tautology.from(pairAxiom of (y -> x, z -> x)) + val step2 = thenHave(in(z, x) |- ∃(y, in(z, y) /\ in(y, X))) by RightExists + have(thesis) by Tautology.from(step2, unionAxiom of (x -> X)) + } + + val backward = have(in(z, union(X)) ==> in(z, x)) subproof { + have(in(z, y) |- in(z, y)) by Restate + val step2 = thenHave((y === x, in(z, y)) |- in(z, x)) by Substitution.ApplyRules(y === x) + have(in(z, y) /\ in(y, X) |- in(z, x)) by Tautology.from(pairAxiom of (y -> x, z -> y), step2) + val step4 = thenHave(∃(y, in(z, y) /\ in(y, X)) |- in(z, x)) by LeftExists + have(in(z, union(X)) ==> in(z, x)) by Tautology.from(unionAxiom of (x -> X), step4) + } + + have(in(z, union(X)) <=> in(z, x)) by RightIff(forward, backward) + thenHave(∀(z, in(z, union(X)) <=> in(z, x))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x -> union(X), y -> x))) + } + + /** + * Theorem --- Two [[unorderedPair]]s are equal iff their elements are equal pairwise. + * + * `{a, b} = {c, d} <=> ( (a = c ∧ b = d) ∨ (a = d ∧ b = c) )` + */ + val unorderedPairExtensionality = Theorem( + (unorderedPair(a, b) === unorderedPair(c, d)) <=> (((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c))) + ) { + // forward direction + // up ab = up cd |- a = c and b = d OR a = d and b = c + val fwd = have((unorderedPair(a, b) === unorderedPair(c, d)) ==> (((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c)))) by Restate.from(unorderedPairDeconstruction) + + // backward direction + // a = c and b = d => up ab = up cd (and the other case) + have(unorderedPair(a, b) === unorderedPair(a, b)) by RightRefl + thenHave((a === c, b === d) |- unorderedPair(a, b) === unorderedPair(c, d)) by RightSubstEq.withParametersSimple( + List((a, c), (b, d)), + lambda(Seq(x, y), unorderedPair(a, b) === unorderedPair(x, y)) + ) + val lhs = thenHave(Set((a === c) /\ (b === d)) |- unorderedPair(a, b) === unorderedPair(c, d)) by Restate + + have(unorderedPair(a, b) === unorderedPair(b, a)) by InstFunSchema(ScalaMap(x -> a, y -> b))(unorderedPairSymmetry) + thenHave((a === d, b === c) |- (unorderedPair(a, b) === unorderedPair(c, d))) by RightSubstEq.withParametersSimple( + List((a, d), (b, c)), + lambda(Seq(x, y), unorderedPair(a, b) === unorderedPair(y, x)) + ) + val rhs = thenHave(Set((a === d) /\ (b === c)) |- (unorderedPair(a, b) === unorderedPair(c, d))) by Restate + + have((((a === d) /\ (b === c)) \/ ((a === c) /\ (b === d))) |- (unorderedPair(a, b) === unorderedPair(c, d))) by LeftOr(lhs, rhs) + val bwd = thenHave((((a === d) /\ (b === c)) \/ ((a === c) /\ (b === d))) ==> (unorderedPair(a, b) === unorderedPair(c, d))) by Restate + + have((unorderedPair(a, b) === unorderedPair(c, d)) <=> (((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c)))) by RightIff(fwd, bwd) + } + + /** + * Theorem --- A singleton set is never empty. + * + * `! {x} = ∅` + */ + val singletonNonEmpty = Theorem( + !(singleton(x) === ∅) + ) { + val reflLhs = have(in(x, singleton(x)) <=> (x === x)) by InstFunSchema(ScalaMap(y -> x))(singletonHasNoExtraElements) + + val reflRhs = have((x === x)) by RightRefl + have((x === x) /\ (in(x, singleton(x)) <=> (x === x))) by RightAnd(reflLhs, reflRhs) + val lhs = thenHave(in(x, singleton(x))) by Tautology + + val rhs = have(in(x, singleton(x)) |- !(singleton(x) === ∅)) by InstFunSchema(ScalaMap(y -> x, x -> singleton(x)))(setWithElementNonEmpty) + + have(!(singleton(x) === ∅)) by Cut(lhs, rhs) + } + + /** + * Theorem --- Two singletons are equal iff their elements are equal + * + * `{x} = {y} <=> x = y` + */ + val singletonExtensionality = Theorem( + (singleton(x) === singleton(y)) <=> (x === y) + ) { + // forward direction + // {x} === {y} |- x === y + have(∀(z, in(z, singleton(x)) <=> in(z, singleton(y))) <=> (singleton(x) === singleton(y))) by InstFunSchema(ScalaMap(x -> singleton(x), y -> singleton(y)))(extensionalityAxiom) + thenHave((singleton(x) === singleton(y)) |- ∀(z, in(z, singleton(x)) <=> in(z, singleton(y)))) by Tautology + val singiff = thenHave((singleton(x) === singleton(y)) |- in(z, singleton(x)) <=> in(z, singleton(y))) by InstantiateForall(z) + + val singX = have(in(z, singleton(x)) <=> (z === x)) by InstFunSchema(ScalaMap(y -> z))(singletonHasNoExtraElements) + have((singleton(x) === singleton(y)) |- (in(z, singleton(x)) <=> in(z, singleton(y))) /\ (in(z, singleton(x)) <=> (z === x))) by RightAnd(singiff, singX) + val yToX = thenHave((singleton(x) === singleton(y)) |- (in(z, singleton(y)) <=> (z === x))) by Tautology + + val singY = have(in(z, singleton(y)) <=> (z === y)) by InstFunSchema(ScalaMap(x -> y))(singX) + have((singleton(x) === singleton(y)) |- (in(z, singleton(y)) <=> (z === x)) /\ (in(z, singleton(y)) <=> (z === y))) by RightAnd(yToX, singY) + thenHave((singleton(x) === singleton(y)) |- ((z === x) <=> (z === y))) by Tautology + thenHave((singleton(x) === singleton(y)) |- ((x === x) <=> (x === y))) by InstFunSchema(ScalaMap(z -> x)) + + thenHave((singleton(x) === singleton(y)) |- (x === y)) by Restate + val fwd = thenHave((singleton(x) === singleton(y)) ==> (x === y)) by Tautology + + // backward direction + // x === y |- {x} === {y} + have(singleton(x) === singleton(x)) by RightRefl + thenHave((x === y) |- singleton(x) === singleton(y)) by RightSubstEq.withParametersSimple(List((x, y)), lambda(a, singleton(x) === singleton(a))) + val bwd = thenHave((x === y) ==> (singleton(x) === singleton(y))) by Restate + + have((singleton(x) === singleton(y)) <=> (x === y)) by RightIff(fwd, bwd) + } + + /** + * Theorem --- Unordered pairs of elements of a set `x` are in its power set `P(x)`. + * + * `a ∈ x ∧ b ∈ x <=> {a, b} ∈ P(x)` + */ + val unorderedPairInPowerSet = Theorem( + (in(a, x) /\ in(b, x)) <=> in(unorderedPair(a, b), powerSet(x)) + ) { + + // forward + val fwd = have((in(a, x) /\ in(b, x)) ==> in(unorderedPair(a, b), powerSet(x))) subproof { + val axExpansion = have(in(unorderedPair(a, b), powerSet(x)) <=> ∀(z, in(z, unorderedPair(a, b)) ==> in(z, x))) by Tautology.from( + powerAxiom of (x -> unorderedPair(a, b), y -> x), + subsetAxiom of (x -> unorderedPair(a, b), y -> x) + ) + + val abToz = have(in(a, x) /\ in(b, x) |- ∀(z, in(z, unorderedPair(a, b)) ==> in(z, x))) subproof { + val pairAxab = have(in(z, unorderedPair(a, b)) |- (z === a) \/ (z === b)) by Tautology.from(pairAxiom of (x -> a, y -> b)) + + have(in(a, x) /\ in(b, x) |- in(a, x)) by Restate + val za = thenHave((in(a, x) /\ in(b, x), (z === a)) |- in(z, x)) by RightSubstEq.withParametersSimple(List((z, a)), lambda(a, in(a, x))) + have(in(a, x) /\ in(b, x) |- in(b, x)) by Restate + val zb = thenHave((in(a, x) /\ in(b, x), (z === b)) |- in(z, x)) by RightSubstEq.withParametersSimple(List((z, b)), lambda(a, in(a, x))) + + val zab = have((in(a, x) /\ in(b, x), (z === a) \/ (z === b)) |- in(z, x)) by LeftOr(za, zb) + + have((in(z, unorderedPair(a, b)), in(a, x) /\ in(b, x)) |- in(z, x)) by Cut(pairAxab, zab) + thenHave(in(a, x) /\ in(b, x) |- in(z, unorderedPair(a, b)) ==> in(z, x)) by Restate + thenHave(thesis) by RightForall + } + + have(thesis) by Tautology.from(abToz, axExpansion) + } + + val bwd = have(in(unorderedPair(a, b), powerSet(x)) ==> (in(a, x) /\ in(b, x))) subproof { + have(in(unorderedPair(a, b), powerSet(x)) |- ∀(z, in(z, unorderedPair(a, b)) ==> in(z, x))) by Tautology.from( + powerAxiom of (x -> unorderedPair(a, b), y -> x), + subsetAxiom of (x -> unorderedPair(a, b), y -> x) + ) + val upz = thenHave(in(unorderedPair(a, b), powerSet(x)) |- in(z, unorderedPair(a, b)) ==> in(z, x)) by InstantiateForall(z) + + val xa = have(in(unorderedPair(a, b), powerSet(x)) |- in(a, x)) by Tautology.from(upz of (z -> a), firstElemInPair of (x -> a, y -> b)) + val xb = have(in(unorderedPair(a, b), powerSet(x)) |- in(b, x)) by Tautology.from(upz of (z -> b), secondElemInPair of (x -> a, y -> b)) + have(in(unorderedPair(a, b), powerSet(x)) |- in(b, x) /\ in(a, x)) by RightAnd(xa, xb) + thenHave(thesis) by Restate + } + + have(thesis) by RightIff(fwd, bwd) + } + + /** + * Theorem --- Pair Extensionality + * + * Two ordered pairs are equal iff their elements are equal when taken in order. + * + * `pair(a, b) = {{a}, {a, b}}` + * + * `pair(a, b) = pair(c, d) <=> a = c ∧ b = d` + */ + val pairExtensionality = Theorem( + (pair(a, b) === pair(c, d)) <=> ((a === c) /\ (b === d)) + ) { + // forward direction + // (a === c) /\ (b === d) ==> pair a b === pair c d + val fwd = have(((a === c) /\ (b === d)) ==> (pair(a, b) === pair(c, d))) subproof { + have((pair(a, b) === pair(a, b))) by RightRefl + thenHave(Set((a === c), (b === d)) |- (pair(a, b) === pair(c, d))) by RightSubstEq.withParametersSimple(List((a, c), (b, d)), lambda(Seq(x, y), pair(a, b) === pair(x, y))) + thenHave(thesis) by Restate + } + + // backward direction + // pair a b === pair c d ==> (a === c) /\ (b === d) + val bwd = have((pair(a, b) === pair(c, d)) ==> ((a === c) /\ (b === d))) subproof { + have(((pair(a, b) === pair(c, d))) |- (pair(a, b) === pair(c, d))) by Hypothesis + val lhs1 = thenHave( + ( + (pair(a, b) === pair(c, d)), + (unorderedPair(unorderedPair(a, b), singleton(a)) === unorderedPair(unorderedPair(c, d), singleton(c))) <=> (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton( + c + ))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton(a) === unorderedPair(c, d)))) + ) |- (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton(c))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton(a) === unorderedPair(c, d)))) + ) by RightSubstIff.withParametersSimple( + List( + ( + (unorderedPair(unorderedPair(a, b), singleton(a)) === unorderedPair(unorderedPair(c, d), singleton(c))), + (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton(c))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton(a) === unorderedPair(c, d)))) + ) + ), + lambda(h, h) + ) + have( + Set((pair(a, b) === pair(c, d))) |- (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton(c))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton( + a + ) === unorderedPair(c, d)))) + ) by Cut(unorderedPairExtensionality of (a -> unorderedPair(a, b), b -> singleton(a), c -> unorderedPair(c, d), d -> singleton(c)), lhs1) + andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> b, c -> c, d -> d))) // {a, b} = {c, d} + andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> a, c -> c, d -> d))) // {a} = {c, d} + andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> b, c -> c, d -> c))) // {a, b} = {c} + andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> a, c -> c, d -> c))) // {a} = {c} + val expandedProp = thenHave( + ( + (pair(a, b) === pair(c, d)) + ) |- ((((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c))) /\ (((a === c) /\ (a === c)) \/ ((a === c) /\ (a === c)))) \/ ((((a === c) /\ (b === c)) \/ ((a === c) /\ (b === c))) /\ (((a === c) /\ (a === d)) \/ ((a === d) /\ (a === c)))) + ) by Restate + val ac = thenHave(Set((pair(a, b) === pair(c, d))) |- (a === c)) by Tautology + + // required subproof, transitivity of equality + // b = c, a = d, a = c |- b = d + val transEqdb = have((d === a, a === c, c === b) |- d === b) subproof { + val dac = have((d === a) /\ (a === c) |- (d === c)) by Restate.from(equalityTransitivity of (x -> d, y -> a, z -> c)) + have((d === c) /\ (c === b) |- (d === b)) by Restate.from(equalityTransitivity of (x -> d, y -> c, z -> b)) + val dcb = thenHave(Set((d === c), (c === b)) |- (d === b)) by Restate + val db = have(((d === a) /\ (a === c), (c === b)) |- (d === b)) by Cut(dac, dcb) + + thenHave(thesis) by Restate + } + + val db = have(((pair(a, b) === pair(c, d))) |- (a === c) /\ (b === d)) by Tautology.from(expandedProp, ac, transEqdb) + thenHave(thesis) by Restate + } + + have(thesis) by RightIff(fwd, bwd) + } + + /** + * Theorem --- No set is an element of itself. + * + * `! x ∈ x` + * + * This is imposed by the Foundation Axiom ([[foundationAxiom]]). + */ + val selfNonInclusion = Theorem( + !in(x, x) + ) { + val X = singleton(x) + + have(!(X === ∅) ==> ∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)))) by InstFunSchema(ScalaMap(x -> X))(foundationAxiom) + val lhs = thenHave(!(X === ∅) |- ∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)))) by Restate + + have(in(y, X) |- in(y, X) <=> (x === y)) by Weakening(singletonHasNoExtraElements) + val innerRhs = thenHave(in(y, X) |- (x === y)) by Tautology + + have((in(x, X), (in(z, X) ==> !in(z, x)), in(y, X)) |- in(z, X) ==> !in(z, x)) by Hypothesis + thenHave((in(x, X), ∀(z, in(z, X) ==> !in(z, x)), in(y, X)) |- in(z, X) ==> !in(z, x)) by LeftForall + thenHave((in(x, X), ∀(z, in(z, X) ==> !in(z, x)), in(x, X)) |- in(x, X) ==> !in(x, x)) by InstFunSchema(ScalaMap(z -> x, y -> x)) + val coreRhs = thenHave(in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x)) |- !in(x, x)) by Restate + + // now we need to show that the assumption is indeed true + // this requires destruction of the existential quantifier in lhs + have(in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x)) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by Hypothesis + val innerRhs2 = thenHave((in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)), x === y) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by LeftSubstEq.withParametersSimple( + List((x, y)), + lambda(Seq(y), in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) + ) + + have((in(y, X), in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by Cut(innerRhs, innerRhs2) + thenHave(in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by Restate + val coreLhs = thenHave(∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by LeftExists + + val rhs = have(∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) |- !in(x, x)) by Cut(coreLhs, coreRhs) + + val finRhs = have(!(X === ∅) |- !in(x, x)) by Cut(lhs, rhs) + val finLhs = have(!(X === ∅)) by Restate.from(singletonNonEmpty) + + have(!in(x, x)) by Cut(finLhs, finRhs) + } + + /** + * Theorem --- No Universal Set + * + * `∀ z. z ∈ x ⊢ ⊥` + * + * There does not exist a set of all sets. Alternatively, its existence, with + * the [[comprehensionSchema]] and Russel's paradox ([[russelsParadox]]), produces + * a contradiction. + */ + val noUniversalSet = Theorem( + ∀(z, in(z, x)) |- () + ) { + have(in(x, x) |- ()) by Restate.from(selfNonInclusion) + thenHave(∀(z, in(z, x)) |- ()) by LeftForall + } + + /** + * Theorem --- The power set of any set is not a proper subset of it. + * + * `∃ x. P(x) ⊂ x ⊢ ⊥` + */ + val powerSetNonInclusion = Theorem( + exists(x, properSubset(powerSet(x), x)) |- () + ) { + val lhs = have(subset(powerSet(x), x) |- subset(powerSet(x), x)) by Hypothesis + + val rhs = have(in(powerSet(x), powerSet(x)) <=> subset(powerSet(x), x)) by InstFunSchema(ScalaMap(x -> powerSet(x), y -> x))(powerAxiom) + + have(subset(powerSet(x), x) |- subset(powerSet(x), x) /\ (in(powerSet(x), powerSet(x)) <=> subset(powerSet(x), x))) by RightAnd(lhs, rhs) + val contraLhs = thenHave(subset(powerSet(x), x) |- in(powerSet(x), powerSet(x))) by Tautology + + val contraRhs = have(!in(powerSet(x), powerSet(x))) by InstFunSchema(ScalaMap(x -> powerSet(x)))(selfNonInclusion) + + have(subset(powerSet(x), x) |- !in(powerSet(x), powerSet(x)) /\ in(powerSet(x), powerSet(x))) by RightAnd(contraLhs, contraRhs) + thenHave(subset(powerSet(x), x) |- ()) by Restate + thenHave(subset(powerSet(x), x) |- (x === powerSet(x))) by Weakening + thenHave(properSubset(powerSet(x), x) |- ()) by Restate + thenHave(∃(x, properSubset(powerSet(x), x)) |- ()) by LeftExists + } + + val inclusionAntiSymmetric = Theorem( + !(in(x, y) /\ in(y, x)) + ) { + assume(in(x, y)) + assume(in(y, x)) + + // consider the set u = {x, y} + val u = unorderedPair(x, y) + + // u is not the empty set + have(in(x, u)) by Weakening(firstElemInPair) + have(!(u === emptySet)) by Tautology.from(lastStep, setWithElementNonEmpty of (y -> x, x -> u)) + + // by Foundation, there must be an inclusion minimal element in u + val minimal = have(exists(z, in(z, u) /\ forall(t, in(t, u) ==> !in(t, z)))) by Tautology.from(lastStep, foundationAxiom of x -> u) + // negation = forall(z, in(z, u) ==> exists(t, in(t, u) /\ in(t, z))) + + // it can only be x or y + val zxy = have(in(z, u) <=> ((z === x) \/ (z === y))) by Weakening(pairAxiom) + + // but x \cap u contains y, and y \cap u contains x + have(in(x, u) /\ in(x, y)) by Tautology.from(firstElemInPair) + thenHave((z === y) |- in(x, u) /\ in(x, z)) by Substitution.ApplyRules(z === y) + val zy = thenHave((z === y) |- exists(t, in(t, u) /\ in(t, z))) by RightExists + + have(in(y, u) /\ in(y, x)) by Tautology.from(secondElemInPair) + thenHave((z === x) |- in(y, u) /\ in(y, z)) by Substitution.ApplyRules(z === x) + val zx = thenHave((z === x) |- exists(t, in(t, u) /\ in(t, z))) by RightExists + + // this is a contradiction + have(in(z, u) ==> exists(t, in(t, u) /\ in(t, z))) by Tautology.from(zxy, zx, zy) + thenHave(forall(z, in(z, u) ==> exists(t, in(t, u) /\ in(t, z)))) by RightForall + + have(thesis) by Tautology.from(lastStep, minimal) + } + + ////////////////////////////////////////////////////////////////////////////// + + /** + * Operations on Sets + */ + + val setIntersectionUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ in(t, y)))) + ) { + have(∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ in(t, y))))) by UniqueComprehension(x, lambda(t, in(t, y))) + } + + /** + * Binary Set Intersection --- Intersection of two sets. + * + * `x ∩ y = {z ∈ x | z ∈ y}` + * + * The proofs are guaranteed and generated by [[UniqueComprehension]]. + * + * @param x set + * @param y set + */ + val setIntersection = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, x) /\ in(t, y))))(setIntersectionUniqueness) + + val setIntersectionCommutativity = Theorem( + setIntersection(x, y) === setIntersection(y, x) + ) { + sorry + } + + val setIntersectionMembership = Theorem( + in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y)) + ) { + sorry + } + + extension (x: Term) { + infix def ∩(y: Term) = setIntersection(x, y) + } + + val intersectionOfSubsets = Lemma( + subset(x, y) |- setIntersection(x, y) === x + ) { + have(forall(t, in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y)))) by InstantiateForall(setIntersection(x, y))(setIntersection.definition) + val txy = thenHave(in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y))) by InstantiateForall(t) + + have(subset(x, y) |- forall(t, in(t, x) ==> in(t, y))) by Weakening(subsetAxiom) + thenHave(subset(x, y) |- in(t, x) ==> in(t, y)) by InstantiateForall(t) + + have(subset(x, y) |- in(t, setIntersection(x, y)) <=> in(t, x)) by Tautology.from(lastStep, txy) + thenHave(subset(x, y) |- forall(t, in(t, setIntersection(x, y)) <=> in(t, x))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> setIntersection(x, y), y -> x)) + } + + val unaryIntersectionUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))) + ) { + val uniq = have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) by UniqueComprehension(union(x), lambda(t, ∀(b, in(b, x) ==> in(t, b)))) + + val lhs = have((in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b))) |- ∀(b, in(b, x) ==> in(t, b)) /\ exists(b, in(b, x))) subproof { + val unionAx = have(in(t, union(x)) |- exists(b, in(b, x) /\ in(t, b))) by Weakening(unionAxiom of (z -> t)) + + have(in(b, x) /\ in(t, b) |- in(b, x)) by Restate + thenHave(in(b, x) /\ in(t, b) |- exists(b, in(b, x))) by RightExists + val exRed = thenHave(exists(b, in(b, x) /\ in(t, b)) |- exists(b, in(b, x))) by LeftExists + + have(in(t, union(x)) |- exists(b, in(b, x))) by Cut(unionAx, exRed) + thenHave(thesis) by Tautology + } + + val rhs = have(∀(b, in(b, x) ==> in(t, b)) /\ exists(b, in(b, x)) |- (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))) subproof { + val unionAx = have(in(t, union(x)) <=> exists(z, in(z, x) /\ in(t, z))) by Restate.from(unionAxiom of (z -> t)) + + have((in(b, x), in(b, x) ==> in(t, b)) |- in(b, x) /\ (in(t, b))) by Tautology + thenHave((in(b, x), forall(b, in(b, x) ==> in(t, b))) |- in(b, x) /\ in(t, b)) by LeftForall + thenHave((in(b, x), forall(b, in(b, x) ==> in(t, b))) |- exists(b, in(b, x) /\ in(t, b))) by RightExists + val exRed = thenHave((exists(b, (in(b, x))), forall(b, in(b, x) ==> in(t, b))) |- exists(b, in(b, x) /\ in(t, b))) by LeftExists + + have(thesis) by Tautology.from(unionAx, exRed) + } + + have(() |- (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))) <=> (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b))))) by Tautology.from(lhs, rhs) + thenHave(() |- forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))) <=> (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) by RightForall + + have(() |- forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))) <=> forall(t, (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) by Cut( + lastStep, + universalEquivalenceDistribution of (P -> lambda(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))), Q -> lambda( + t, + (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))) + )) + ) + thenHave( + () |- forall(z, forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))) <=> forall(t, (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) + ) by RightForall + + have( + () |- existsOne(z, forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) <=> existsOne(z, forall(t, (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) + ) by Cut( + lastStep, + uniqueExistentialEquivalenceDistribution of (P -> lambda(z, forall(t, in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))), Q -> lambda( + z, + forall(t, in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))) + )) + ) + have(thesis) by Tautology.from(lastStep, uniq) + } + + /** + * Unary Set Intersection --- Intersection of all elements of a given set. + * + * `∩ x = {z ∈ ∪ x | ∀ y ∈ x. z ∈ y}` + * + * The proofs are guaranteed and generated by [[UniqueComprehension]]. + * + * @param x set + */ + val unaryIntersection = DEF(x) --> The(z, ∀(t, in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))))(unaryIntersectionUniqueness) + + val setDifferenceUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ !in(t, y)))) + ) { + have(∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ !in(t, y))))) by UniqueComprehension(x, lambda(t, !in(t, y))) + } + + /** + * Binary Set Difference --- Given two sets, produces the set that contains + * elements in the first but not in the second. + * + * `x - y = {z ∈ x | ! z ∈ y}` + * + * The proofs are guaranteed and generated by [[UniqueComprehension]]. + * + * @param x set + * @param y set + */ + val setDifference = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, x) /\ !in(t, y))))(setDifferenceUniqueness) + + /** + * Theorem --- Intersection of a non-empty Class is a Set + * + * There exists a set that is the intersection of all sets satisfying a given + * formula. With classes, this means that the unary intersection of a class + * defined by a predicate is a set. + * + * `∃ x. P(x) ⊢ ∃ z. t ∈ z ⇔ ∀ x. P(x) ⇒ t ∈ x` + */ + val intersectionOfPredicateClassExists = Theorem( + ∃(x, P(x)) |- ∃(z, ∀(t, in(t, z) <=> ∀(y, P(y) ==> in(t, y)))) + ) { + have(∃(z, ∀(t, in(t, z) <=> (in(t, x) /\ φ(t))))) by InstFunSchema(ScalaMap(z -> x))(comprehensionSchema) + + val conjunction = thenHave(∃(z, ∀(t, in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) by InstPredSchema(ScalaMap(φ -> lambda(t, ∀(y, P(y) ==> in(t, y))))) + + have(∀(y, P(y) ==> in(t, y)) |- ∀(y, P(y) ==> in(t, y))) by Hypothesis + thenHave(∀(y, P(y) ==> in(t, y)) /\ P(x) |- ∀(y, P(y) ==> in(t, y))) by Weakening + thenHave(∀(y, P(y) ==> in(t, y)) /\ P(x) |- P(x) ==> in(t, x)) by InstantiateForall(x) + thenHave(∀(y, P(y) ==> in(t, y)) /\ P(x) |- in(t, x) /\ ∀(y, P(y) ==> in(t, y))) by Tautology + val fwd = thenHave(P(x) |- ∀(y, P(y) ==> in(t, y)) ==> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by Restate + + have((in(t, x) /\ ∀(y, P(y) ==> in(t, y))) |- (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by Hypothesis + val bwd = thenHave((in(t, x) /\ ∀(y, P(y) ==> in(t, y))) ==> (∀(y, P(y) ==> in(t, y)))) by Restate + + val lhs = have(P(x) |- ∀(y, P(y) ==> in(t, y)) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by RightIff(fwd, bwd) + + val form = formulaVariable + have((in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) |- in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by Hypothesis + val rhs = thenHave( + Set((in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))), (∀(y, P(y) ==> in(t, y)) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))) |- (in(t, z) <=> (∀(y, P(y) ==> in(t, y)))) + ) by RightSubstIff.withParametersSimple(List((∀(y, P(y) ==> in(t, y)), (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))), lambda(form, in(t, z) <=> (form))) + + have((P(x), (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))) |- in(t, z) <=> (∀(y, P(y) ==> in(t, y)))) by Cut(lhs, rhs) + thenHave((P(x), ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) |- in(t, z) <=> (∀(y, P(y) ==> in(t, y)))) by LeftForall + thenHave((P(x), ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) |- ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y))))) by RightForall + thenHave((P(x), ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by RightExists + val cutRhs = thenHave((P(x), ∃(z, ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))))) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by LeftExists + + have(P(x) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by Cut(conjunction, cutRhs) + thenHave(∃(x, P(x)) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by LeftExists + + } + + /** + * The first element of an ordered [[pair]] --- `first p = ∪ ∩ p` + * + * If `p = (a, b) = {{a}, {a, b}}`, `∩ p = {a}`, and `∪ ∩ p = a`. + * + * While the function is defined on all sets, the result on non-pairs may be + * uninteresting or garbage. Generally expected to be used via + * [[firstInPairReduction]]. + */ + val firstInPair = DEF(p) --> union(unaryIntersection(p)) + + val secondInPairSingletonUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> (in(t, union(p)) /\ ((!(union(p) === unaryIntersection(p))) ==> (!in(t, unaryIntersection(p))))))) + ) { + have(thesis) by UniqueComprehension(union(p), lambda(t, ((!(union(p) === unaryIntersection(p))) ==> (!in(t, unaryIntersection(p)))))) + } + + /** + * See [[secondInPair]]. + */ + val secondInPairSingleton = + DEF(p) --> The(z, ∀(t, in(t, z) <=> (in(t, union(p)) /\ ((!(union(p) === unaryIntersection(p))) ==> (!in(t, unaryIntersection(p)))))))(secondInPairSingletonUniqueness) + + /** + * The second element of an ordered [[pair]] --- + * + * `second p = ∪ {x ∈ ∪ p | ∪ p != ∩ p ⟹ !x ∈ ∩ p} = ∪ (secondSingleton p)` + * + * There is a more naive definition: `second p = ∪ (∪ p - (first p))`. + * If `p = (a, b) = {{a}, {a, b}}`, `∪ p = {a, b}`, and `∪ p - (first p) + * = {a, b} - {a} = {b}`, the `∪` at the top level reduces this to `b`. + * However, this fails when `a = b`, and returns the [[emptySet]]. + * + * While the function is defined on all sets, the result on non-pairs may be + * uninteresting or garbage. Generally expected to be used via + * [[secondInPairReduction]]. + * + * @see https://en.wikipedia.org/wiki/Ordered_pair#Kuratowski's_definition + */ + val secondInPair = DEF(p) --> union(secondInPairSingleton(p)) + + /** + * Theorem --- The union of an ordered pair is the corresponding unordered pair. + * + * `∪ (x, y) = ∪ {{x}, {x, y}} = {x, y}` + */ + val unionOfOrderedPair = Theorem( + () |- (union(pair(x, y)) === unorderedPair(x, y)) + ) { + val upElem = have(in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) by Restate.from(pairAxiom) + + val unionElem = have(in(z, union(pair(x, y))) <=> ((z === x) \/ (z === y))) subproof { + // expand being in \cup (x, y) + val unionax = have(in(z, union(pair(x, y))) <=> exists(b, in(b, pair(x, y)) /\ in(z, b))) by Restate.from(unionAxiom of x -> pair(x, y)) + + // what does it mean for b to be in (x, y)? + // b \in (x, y) /\ z \in b <=> z = x \/ z = y + val fwd = have(exists(b, in(b, pair(x, y)) /\ in(z, b)) |- ((z === x) \/ (z === y))) subproof { + val bxy = + have(in(b, pair(x, y)) /\ in(z, b) |- ((b === unorderedPair(x, y)) \/ (b === unorderedPair(x, x)))) by Weakening(pairAxiom of (x -> unorderedPair(x, y), y -> unorderedPair(x, x), z -> b)) + val zxy = have((b === unorderedPair(x, y)) |- in(z, b) <=> ((z === x) \/ (z === y))) by Substitution.ApplyRules(b === unorderedPair(x, y))(pairAxiom) + val zxx = have((b === unorderedPair(x, x)) |- in(z, b) <=> ((z === x) \/ (z === x))) by Substitution.ApplyRules(b === unorderedPair(x, x))(pairAxiom of y -> x) + + have(in(b, pair(x, y)) /\ in(z, b) |- ((z === x) \/ (z === y))) by Tautology.from(bxy, zxy, zxx) + thenHave(thesis) by LeftExists + } + + val bwd = have(((z === x) \/ (z === y)) |- exists(b, in(b, pair(x, y)) /\ in(z, b))) subproof { + val xyp = have(in(unorderedPair(x, y), pair(x, y))) by Restate.from(firstElemInPair of (x -> unorderedPair(x, y), y -> unorderedPair(x, x))) + val zx = have((z === x) |- in(z, unorderedPair(x, y))) by Substitution.ApplyRules(z === x)(firstElemInPair) + val zy = have((z === y) |- in(z, unorderedPair(x, y))) by Substitution.ApplyRules(z === y)(secondElemInPair) + + have(((z === x) \/ (z === y)) |- in(unorderedPair(x, y), pair(x, y)) /\ in(z, unorderedPair(x, y))) by Tautology.from(xyp, zx, zy) + thenHave(thesis) by RightExists + } + + have(thesis) by Tautology.from(fwd, bwd, unionax) + } + + have(in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y))) by Tautology.from(upElem, unionElem) + thenHave(forall(z, in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y)))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> union(pair(x, y)), y -> unorderedPair(x, y))) + } + + /** + * Theorem --- The unary intersection of an ordered pair is the singleton + * containing the first element. + * + * `∩ (x, y) = ∩ {{x}, {x, y}} = {x}` + */ + val pairUnaryIntersection = Theorem( + () |- in(z, unaryIntersection(pair(x, y))) <=> (z === x) + ) { + have(forall(t, in(t, unaryIntersection(pair(x, y))) <=> (exists(b, in(b, pair(x, y))) /\ ∀(b, in(b, pair(x, y)) ==> in(t, b))))) by InstantiateForall(unaryIntersection(pair(x, y)))( + unaryIntersection.definition of (x -> pair(x, y)) + ) + val defexp = thenHave(in(z, unaryIntersection(pair(x, y))) <=> (exists(b, in(b, pair(x, y))) /\ ∀(b, in(b, pair(x, y)) ==> in(z, b)))) by InstantiateForall(z) + + val lhs = have(in(z, unaryIntersection(pair(x, y))) |- (z === x)) subproof { + have(in(z, unaryIntersection(pair(x, y))) |- forall(b, in(b, pair(x, y)) ==> in(z, b))) by Weakening(defexp) + thenHave(in(z, unaryIntersection(pair(x, y))) |- in(unorderedPair(x, x), pair(x, y)) ==> in(z, unorderedPair(x, x))) by InstantiateForall(unorderedPair(x, x)) + have(thesis) by Tautology.from(lastStep, secondElemInPair of (x -> unorderedPair(x, y), y -> unorderedPair(x, x)), singletonHasNoExtraElements of (y -> z)) + } + + val rhs = have((z === x) |- in(z, unaryIntersection(pair(x, y)))) subproof { + val xinxy = have(in(x, unaryIntersection(pair(x, y)))) subproof { + have(in(unorderedPair(x, x), pair(x, y))) by Restate.from(secondElemInPair of (x -> unorderedPair(x, y), y -> unorderedPair(x, x))) + val exClause = thenHave(exists(b, in(b, pair(x, y)))) by RightExists + + have(in(b, pair(x, y)) |- in(b, pair(x, y))) by Hypothesis + val bp = thenHave(in(b, pair(x, y)) |- (b === singleton(x)) \/ (b === unorderedPair(x, y))) by Substitution.ApplyRules(pairAxiom of (z -> b, x -> unorderedPair(x, y), y -> singleton(x))) + + have(in(x, singleton(x))) by Restate.from(singletonHasNoExtraElements of (y -> x)) + val bxx = thenHave((b === singleton(x)) |- in(x, b)) by Substitution.ApplyRules((b === singleton(x))) + + have(in(x, unorderedPair(x, y))) by Restate.from(firstElemInPair) + val bxy = thenHave((b === unorderedPair(x, y)) |- in(x, b)) by Substitution.ApplyRules((b === unorderedPair(x, y))) + + have(in(b, pair(x, y)) ==> in(x, b)) by Tautology.from(bp, bxx, bxy) + val frClause = thenHave(forall(b, in(b, pair(x, y)) ==> in(x, b))) by RightForall + + have(thesis) by Tautology.from(defexp of (z -> x), exClause, frClause) + } + thenHave(thesis) by Substitution.ApplyRules((z === x)) + } + + have(thesis) by Tautology.from(lhs, rhs) + } + + /** + * Theorem --- The unary intersection and union of an ordered pair are equal + * iff the two elements are equal. + * + * `∪ (x, y) = {x} = {x, y} = ∩ (x, y) <=> x = y` + * + * See [[pairUnaryIntersection]] and [[unionOfOrderedPair]]. + */ + val pairUnionIntersectionEqual = Theorem( + () |- (union(pair(x, y)) === unaryIntersection(pair(x, y))) <=> (x === y) + ) { + have(in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) by Restate.from(pairAxiom) + val unionPair = thenHave(in(z, union(pair(x, y))) <=> ((z === x) \/ (z === y))) by Substitution.ApplyRules(unionOfOrderedPair) + + val fwd = have((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- (x === y)) subproof { + have((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- forall(z, in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y))))) by Weakening( + extensionalityAxiom of (x -> union(pair(x, y)), y -> unaryIntersection(pair(x, y))) + ) + thenHave((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y)))) by InstantiateForall(z) + + have((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- (((z === x) \/ (z === y)) <=> (z === x))) by Tautology.from(lastStep, unionPair, pairUnaryIntersection) + thenHave((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- (((y === x) \/ (y === y)) <=> (y === x))) by InstFunSchema(ScalaMap(z -> y)) + thenHave(thesis) by Restate + } + + val bwd = have((x === y) |- (union(pair(x, y)) === unaryIntersection(pair(x, y)))) subproof { + have((x === y) |- in(z, union(pair(x, y))) <=> ((z === x) \/ (z === x))) by Substitution.ApplyRules(x === y)(unionPair) + have((x === y) |- in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y)))) by Tautology.from(lastStep, pairUnaryIntersection) + thenHave((x === y) |- forall(z, in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y))))) by RightForall + + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> union(pair(x, y)), y -> unaryIntersection(pair(x, y)))) + } + + have(thesis) by Tautology.from(fwd, bwd) + } + + /** + * Theorem --- The [[firstInPair]] operation when applied to an ordered pair + * produces the first element of the pair. + * + * `first((x, y)) = x` + */ + val firstInPairReduction = Theorem( + () |- (firstInPair(pair(x, y)) === x) + ) { + // z \in \cap (x, y) <=> z = x + val elemInter = have(in(z, unaryIntersection(pair(x, y))) <=> (z === x)) by Restate.from(pairUnaryIntersection) + + // z in \cup \cap p <=> z \in x + val elemUnion = have(in(z, union(unaryIntersection(pair(x, y)))) <=> in(z, x)) subproof { + val unionax = + have(in(z, union(unaryIntersection(pair(x, y)))) <=> exists(t, in(t, unaryIntersection(pair(x, y))) /\ in(z, t))) by Restate.from(unionAxiom of (x -> unaryIntersection(pair(x, y)))) + + val lhs = have(exists(t, in(t, unaryIntersection(pair(x, y))) /\ in(z, t)) |- in(z, x)) subproof { + have(in(z, t) |- in(z, t)) by Hypothesis + thenHave((in(z, t), (t === x)) |- in(z, x)) by Substitution.ApplyRules(t === x) + have(in(t, unaryIntersection(pair(x, y))) /\ in(z, t) |- in(z, x)) by Tautology.from(lastStep, elemInter of (z -> t)) + thenHave(thesis) by LeftExists + } + + val rhs = have(in(z, x) |- exists(t, in(t, unaryIntersection(pair(x, y))) /\ in(z, t))) subproof { + have(in(x, unaryIntersection(pair(x, y)))) by Restate.from(elemInter of (z -> x)) + thenHave(in(z, x) |- in(x, unaryIntersection(pair(x, y))) /\ in(z, x)) by Tautology + thenHave(thesis) by RightExists + } + + have(thesis) by Tautology.from(lhs, rhs, unionax) + } + + thenHave(forall(z, in(z, union(unaryIntersection(pair(x, y)))) <=> in(z, x))) by RightForall + + // \cup \cap (x, y) = x + val unioneq = have(union(unaryIntersection(pair(x, y))) === x) by Tautology.from(lastStep, extensionalityAxiom of (x -> union(unaryIntersection(pair(x, y))), y -> x)) + have((firstInPair(pair(x, y)) === union(unaryIntersection(pair(x, y))))) by InstantiateForall(firstInPair(pair(x, y)))(firstInPair.definition of (p -> pair(x, y))) + have(thesis) by Substitution.ApplyRules(lastStep)(unioneq) + } + + /** + * Theorem --- The [[secondInPairSingletone]] operation when applied to an + * ordered pair produces the singleton containing the second element of the pair. + * + * `secondSingleton((x, y)) = {y}` + * + * Used for [[secondInPair]] reduction. + */ + val secondInPairSingletonReduction = Theorem( + () |- in(z, secondInPairSingleton(pair(x, y))) <=> (z === y) + ) { + have( + forall( + t, + in(t, secondInPairSingleton(pair(x, y))) <=> (in(t, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(t, unaryIntersection(pair(x, y)))))) + ) + ) by InstantiateForall(secondInPairSingleton(pair(x, y)))(secondInPairSingleton.definition of p -> pair(x, y)) + val sipsDef = thenHave( + in(z, secondInPairSingleton(pair(x, y))) <=> (in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) + ) by InstantiateForall(z) + + val predElem = have((in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) <=> (z === y)) subproof { + + // breakdown for each of the clauses in the statement + have(forall(z, in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y)))) by Tautology.from(unionOfOrderedPair, extensionalityAxiom of (x -> union(pair(x, y)), y -> unorderedPair(x, y))) + thenHave(in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y))) by InstantiateForall(z) + val zUnion = have(in(z, union(pair(x, y))) <=> ((z === x) \/ (z === y))) by Tautology.from(lastStep, pairAxiom) + val unEqInt = have((union(pair(x, y)) === unaryIntersection(pair(x, y))) <=> (x === y)) by Restate.from(pairUnionIntersectionEqual) + val zInter = have(in(z, unaryIntersection(pair(x, y))) <=> (z === x)) by Restate.from(pairUnaryIntersection) + + have( + (in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) <=> (in(z, union(pair(x, y))) /\ ((!(union( + pair(x, y) + ) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) + ) by Restate + val propDest = thenHave( + (in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in( + z, + unaryIntersection(pair(x, y)) + )))) <=> (((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x)))) + ) by Substitution.ApplyRules(zUnion, zInter, unEqInt) + + have((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x)))) <=> (z === y)) subproof { + val hypo = have((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x)))) |- (((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))))) by Hypothesis + thenHave((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), (x === y)) |- (((z === y) \/ (z === y)) /\ ((!(y === y)) ==> (!(z === x))))) by Substitution.ApplyRules(x === y) + val xeqy = thenHave((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), (x === y)) |- (z === y)) by Tautology + + have((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), !(x === y)) |- (((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))))) by Weakening(hypo) + val xneqy = thenHave((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), !(x === y)) |- (z === y)) by Tautology + + have(thesis) by Tautology.from(xeqy, xneqy, equalityTransitivity of (z -> y, y -> z)) + } + + have(thesis) by Tautology.from(lastStep, propDest) + } + + have(thesis) by Tautology.from(sipsDef, predElem) + } + + /** + * Theorem --- The [[secondInPair]] operation when applied to an ordered pair + * produces the second element of the pair. + * + * `second((x, y)) = y` + */ + val secondInPairReduction = Theorem( + () |- secondInPair(pair(x, y)) === y + ) { + have(secondInPair(pair(x, y)) === union(secondInPairSingleton(pair(x, y)))) by InstantiateForall(secondInPair(pair(x, y)))(secondInPair.definition of p -> pair(x, y)) + have(forall(z, in(z, secondInPair(pair(x, y))) <=> in(z, union(secondInPairSingleton(pair(x, y)))))) by Tautology.from( + lastStep, + extensionalityAxiom of (x -> secondInPair(pair(x, y)), y -> union(secondInPairSingleton(pair(x, y)))) + ) + thenHave(in(z, secondInPair(pair(x, y))) <=> in(z, union(secondInPairSingleton(pair(x, y))))) by InstantiateForall(z) + val secondElem = + have(in(z, secondInPair(pair(x, y))) <=> (exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b)))) by Tautology.from(lastStep, unionAxiom of (x -> secondInPairSingleton(pair(x, y)))) + + val elemIsY = have((exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b))) <=> in(z, y)) subproof { + val lhs = have((exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b))) |- in(z, y)) subproof { + have(in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b) |- in(z, b)) by Restate + thenHave((in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b), (b === y)) |- in(z, y)) by Substitution.ApplyRules(b === y) + have((in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b)) |- in(z, y)) by Tautology.from(lastStep, secondInPairSingletonReduction of z -> b) + + thenHave(thesis) by LeftExists + } + + val rhs = have(in(z, y) |- exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b))) subproof { + have(in(z, y) |- in(z, y)) by Hypothesis + have(in(z, y) |- in(y, secondInPairSingleton(pair(x, y))) /\ in(z, y)) by Tautology.from(lastStep, secondInPairSingletonReduction of z -> y) + thenHave(thesis) by RightExists + } + + have(thesis) by Tautology.from(lhs, rhs) + } + + have(in(z, secondInPair(pair(x, y))) <=> in(z, y)) by Tautology.from(secondElem, elemIsY) + thenHave(forall(z, in(z, secondInPair(pair(x, y))) <=> in(z, y))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of x -> secondInPair(pair(x, y))) + } + + /** + * Theorem --- Pair Reconstruction + * + * If `x` is a pair (i.e. `= (c, d)` for some `c` and `d`), then pair element + * projection on it is invertible, so `x = (fst x, snd x)`. + */ + val pairReconstruction = Lemma( + exists(c, exists(d, pair(c, d) === x)) |- x === pair(firstInPair(x), secondInPair(x)) + ) { + sorry + } + + /** + * Cartesian Products and Relations + */ + + val cartesianProductUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) + ) { + have(∃!(z, ∀(t, in(t, z) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))))))) by UniqueComprehension( + powerSet(powerSet(setUnion(x, y))), + lambda(t, ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) + ) + } + + /** + * Cartesian Product --- Given two sets `x` and `y`, their cartesian product + * is the set containing pairs with the first element in `x` and the second + * in `y`. The cartesian product can be seen as a comprehension on the set + * `PP(x ∪ y)`. + * + * `x * y = {z ∈ PP(x ∪ y) | ∃ a ∈ x, b ∈ y. z = (a, b)}` + * + * The proofs are guaranteed and generated by [[UniqueComprehension]]. + * + * @param x set + * @param y set + */ + val cartesianProduct = + DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))))))(cartesianProductUniqueness) + + /** + * Theorem --- cartesian Product ([[cartesianProd]]) of any set with the + * [[emptySet]] is empty. + */ + val productWithEmptySetEmpty = Theorem( + () |- (cartesianProduct(x, emptySet) === emptySet) /\ (cartesianProduct(emptySet, x) === emptySet) + ) { + val xFirst = have(() |- (cartesianProduct(x, emptySet) === emptySet)) subproof { + have( + forall(t, in(t, cartesianProduct(x, emptySet)) <=> (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))))) + ) by InstantiateForall(cartesianProduct(x, emptySet))(cartesianProduct.definition of (y -> emptySet)) + val impl = thenHave( + in(t, cartesianProduct(x, emptySet)) <=> (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet)))) + ) by InstantiateForall(t) + + val elemEmpty = have(in(t, emptySet) <=> (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))))) subproof { + val lhs = have(in(t, emptySet) |- (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))))) by Weakening( + emptySet.definition of (x -> t) + ) + + have((t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet) |- in(t, emptySet)) by Weakening(emptySet.definition of (x -> b)) + thenHave(exists(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet)) |- in(t, emptySet)) by LeftExists + thenHave(exists(a, exists(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))) |- in(t, emptySet)) by LeftExists + val rhs = thenHave(in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ exists(a, exists(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))) |- in(t, emptySet)) by Weakening + + have(thesis) by Tautology.from(lhs, rhs) + } + + have(in(t, cartesianProduct(x, emptySet)) <=> in(t, emptySet)) by Tautology.from(impl, elemEmpty) + val ext = thenHave(forall(t, in(t, cartesianProduct(x, emptySet)) <=> in(t, emptySet))) by RightForall + + have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> cartesianProduct(x, emptySet), y -> emptySet)) + } + + val xSecond = have(() |- (cartesianProduct(emptySet, x) === emptySet)) subproof { + have( + forall(t, in(t, cartesianProduct(emptySet, y)) <=> (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))))) + ) by InstantiateForall(cartesianProduct(emptySet, y))(cartesianProduct.definition of (x -> emptySet)) + val impl = thenHave( + in(t, cartesianProduct(emptySet, y)) <=> (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y)))) + ) by InstantiateForall(t) + + val elemEmpty = have(in(t, emptySet) <=> (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))))) subproof { + val lhs = have(in(t, emptySet) |- (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))))) by Weakening( + emptySet.definition of (x -> t) + ) + + have((t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y) |- in(t, emptySet)) by Weakening(emptySet.definition of (x -> a)) + thenHave(exists(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y)) |- in(t, emptySet)) by LeftExists + thenHave(exists(a, exists(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))) |- in(t, emptySet)) by LeftExists + val rhs = thenHave(in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ exists(a, exists(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))) |- in(t, emptySet)) by Weakening + + have(thesis) by Tautology.from(lhs, rhs) + } + + have(in(t, cartesianProduct(emptySet, y)) <=> in(t, emptySet)) by Tautology.from(impl, elemEmpty) + val ext = thenHave(forall(t, in(t, cartesianProduct(emptySet, y)) <=> in(t, emptySet))) by RightForall + + have(thesis) by Tautology.from(ext of (y -> x), extensionalityAxiom of (x -> cartesianProduct(emptySet, x), y -> emptySet)) + } + + have(thesis) by RightAnd(xFirst, xSecond) + } + + /** + * Theorem --- a pair is in the product `x * y` iff its elements are in `x` and + * `y` respectively. + * + * `(a, b) ∈ x * y <=> a ∈ x ∧ b ∈ y` + */ + val pairInCartesianProduct = Theorem( + in(pair(a, b), cartesianProduct(x, y)) <=> (in(a, x) /\ in(b, y)) + ) { + have( + (cartesianProduct(x, y) === cartesianProduct(x, y)) <=> ∀( + t, + in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y)))) + ) + ) by InstantiateForall(cartesianProduct(x, y))(cartesianProduct.definition) + thenHave(∀(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y)))))) by Restate + val cartProdDef = thenHave( + in(pair(a, b), cartesianProduct(x, y)) <=> (in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)))) + ) by InstantiateForall(pair(a, b)) + + // forward + // (a, b) \in x * y ⟹ a ∈ x ∧ b ∈ y + val fwd = have(in(pair(a, b), cartesianProduct(x, y)) ==> (in(a, x) /\ in(b, y))) subproof { + have((a === c, b === d, in(c, x) /\ in(d, y)) |- in(c, x) /\ in(d, y)) by Hypothesis + thenHave((a === c, b === d, in(c, x) /\ in(d, y)) |- in(a, x) /\ in(b, y)) by RightSubstEq.withParametersSimple(List((a, c), (b, d)), lambda(Seq(a, b), in(a, x) /\ in(b, y))) + thenHave(Set((a === c) /\ (b === d), in(c, x) /\ in(d, y)) |- in(a, x) /\ in(b, y)) by Restate + andThen(Substitution.applySubst(pairExtensionality)) + thenHave((pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y) |- in(a, x) /\ in(b, y)) by Restate + thenHave(∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)) |- in(a, x) /\ in(b, y)) by LeftExists + thenHave(∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(a, x) /\ in(b, y)) by LeftExists + val cdExists = thenHave((in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(a, x) /\ in(b, y))) by Weakening + have(thesis) by Tautology.from(cdExists, cartProdDef) + } + // backward + // a \in x /\ b \in y ==> (a, b) \in x * y + val bwd = have(in(a, x) /\ in(b, y) ==> in(pair(a, b), cartesianProduct(x, y))) subproof { + val membership = have(in(a, x) /\ in(b, y) |- in(pair(a, b), powerSet(powerSet(setUnion(x, y))))) subproof { + val powerSubsetDef = have(in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) <=> ∀(z, in(z, pair(a, b)) ==> in(z, powerSet(setUnion(x, y))))) by Tautology.from( + powerAxiom of (x -> pair(a, b), y -> powerSet(setUnion(x, y))), + subsetAxiom of (x -> pair(a, b), y -> powerSet(setUnion(x, y))) + ) + + val unionToPower = have((in(a, setUnion(x, y)) /\ in(b, setUnion(x, y)), in(z, pair(a, b))) |- in(z, powerSet(setUnion(x, y)))) subproof { + val zabHypo = have(in(z, pair(a, b)) |- in(z, pair(a, b))) by Hypothesis + val cutLhs = have(in(z, pair(a, b)) |- (z === unorderedPair(a, b)) \/ (z === singleton(a))) by Tautology.from(zabHypo, pairAxiom of (x -> unorderedPair(a, b), y -> singleton(a))) + + // need to show that {a, b} and {a} = {a, a} are in P(x \cup y) + val prem = (in(a, setUnion(x, y)) /\ in(b, setUnion(x, y))) + + have(prem |- in(unorderedPair(a, b), powerSet(setUnion(x, y)))) by Weakening(unorderedPairInPowerSet of (x -> setUnion(x, y))) + val zab = + thenHave((prem, (z === unorderedPair(a, b))) |- in(z, powerSet(setUnion(x, y)))) by RightSubstEq.withParametersSimple( + List((z, unorderedPair(a, b))), + lambda(a, in(a, powerSet(setUnion(x, y)))) + ) + have(prem |- in(unorderedPair(a, a), powerSet(setUnion(x, y)))) by Weakening(unorderedPairInPowerSet of (x -> setUnion(x, y), b -> a)) + val zaa = + thenHave((prem, (z === unorderedPair(a, a))) |- in(z, powerSet(setUnion(x, y)))) by RightSubstEq.withParametersSimple( + List((z, unorderedPair(a, a))), + lambda(a, in(a, powerSet(setUnion(x, y)))) + ) + + val cutRhs = have((prem, (z === unorderedPair(a, b)) \/ (z === singleton(a))) |- in(z, powerSet(setUnion(x, y)))) by LeftOr(zab, zaa) + + have(thesis) by Cut(cutLhs, cutRhs) + } + + val abToUnion = have(in(a, x) /\ in(b, y) |- in(a, setUnion(x, y)) /\ in(b, setUnion(x, y))) subproof { + have(in(a, x) |- in(a, setUnion(x, y)) <=> (in(a, x) \/ in(a, y))) by Weakening(setUnionMembership of (z -> a)) + val aUn = thenHave(in(a, x) |- in(a, setUnion(x, y))) by Tautology + have(in(b, y) |- in(b, setUnion(x, y)) <=> (in(b, x) \/ in(b, y))) by Weakening(setUnionMembership of (z -> b)) + val bUn = thenHave(in(b, y) |- in(b, setUnion(x, y))) by Tautology + + have((in(a, x), in(b, y)) |- in(a, setUnion(x, y)) /\ in(b, setUnion(x, y))) by RightAnd(aUn, bUn) + thenHave(thesis) by Restate + } + + have((in(a, x) /\ in(b, y), in(z, pair(a, b))) |- in(z, powerSet(setUnion(x, y)))) by Cut(abToUnion, unionToPower) + thenHave((in(a, x) /\ in(b, y)) |- in(z, pair(a, b)) ==> in(z, powerSet(setUnion(x, y)))) by Restate + val abToPower = thenHave((in(a, x) /\ in(b, y)) |- ∀(z, in(z, pair(a, b)) ==> in(z, powerSet(setUnion(x, y))))) by RightForall + + have(thesis) by Tautology.from(abToPower, powerSubsetDef) + } + + val filtering = have(in(a, x) /\ in(b, y) |- ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)))) subproof { + have(in(a, x) /\ in(b, y) |- (pair(a, b) === pair(a, b)) /\ in(a, x) /\ in(b, y)) by Restate + thenHave(in(a, x) /\ in(b, y) |- ∃(d, (pair(a, d) === pair(a, b)) /\ in(a, x) /\ in(d, y))) by RightExists + thenHave(in(a, x) /\ in(b, y) |- ∃(c, ∃(d, (pair(c, d) === pair(a, b)) /\ in(c, x) /\ in(d, y)))) by RightExists + } + + val compCriterion = + have(in(a, x) /\ in(b, y) |- in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)))) by RightAnd(membership, filtering) + + have(thesis) by Tautology.from(compCriterion, cartProdDef) + } + + have(thesis) by RightIff(fwd, bwd) + } + + /** + * Theorem --- If `t` is a pair containing elements from `x` and `y`, then + * it is in `PP(x ∪ y)`. + * + * `∃ c, d. t = (c, d) ∧ c ∈ x ∧ d ∈ y ⊢ t ∈ PP(x ∪ y)` + * + * Asserts that the first part of the [[cartesianProduct]] definition is redundant. + */ + val elemOfPowerPowerUnion = Theorem( + ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(t, powerSet(powerSet(setUnion(x, y)))) + ) { + val upCD = have((in(c, x), in(d, y)) |- in(unorderedPair(c, d), powerSet(setUnion(x, y)))) subproof { + + have((in(c, x), in(d, y)) |- subset(unorderedPair(c, d), setUnion(x, y))) subproof { + val zcd = have(in(z, unorderedPair(c, d)) <=> ((z === c) \/ (z === d))) by Restate.from(pairAxiom of (x -> c, y -> d)) + val zunion = have(in(z, setUnion(x, y)) <=> (in(z, x) \/ in(z, y))) by Restate.from(setUnionMembership) + + val zc = have((z === c) |- in(z, setUnion(x, y)) <=> (in(c, x) \/ in(c, y))) by Substitution.ApplyRules(z === c)(zunion) + val zd = have((z === d) |- in(z, setUnion(x, y)) <=> (in(d, x) \/ in(d, y))) by Substitution.ApplyRules(z === d)(zunion) + + have((in(c, x), in(d, y)) |- in(z, unorderedPair(c, d)) ==> in(z, setUnion(x, y))) by Tautology.from(zcd, zc, zd) + thenHave((in(c, x), in(d, y)) |- forall(z, in(z, unorderedPair(c, d)) ==> in(z, setUnion(x, y)))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> unorderedPair(c, d), y -> setUnion(x, y))) + } + + have(thesis) by Tautology.from(lastStep, powerAxiom of (y -> setUnion(x, y), x -> unorderedPair(c, d))) + } + + val upCC = have((in(c, x)) |- in(unorderedPair(c, c), powerSet(setUnion(x, y)))) subproof { + + have((in(c, x)) |- subset(unorderedPair(c, c), setUnion(x, y))) subproof { + val zcd = have(in(z, unorderedPair(c, c)) <=> (z === c)) by Restate.from(pairAxiom of (x -> c, y -> c)) + val zunion = have(in(z, setUnion(x, y)) <=> (in(z, x) \/ in(z, y))) by Restate.from(setUnionMembership) + + val zc = have((z === c) |- in(z, setUnion(x, y)) <=> (in(c, x) \/ in(c, y))) by Substitution.ApplyRules(z === c)(zunion) + + have(in(c, x) |- in(z, unorderedPair(c, c)) ==> in(z, setUnion(x, y))) by Tautology.from(zcd, zc) + thenHave(in(c, x) |- forall(z, in(z, unorderedPair(c, c)) ==> in(z, setUnion(x, y)))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> unorderedPair(c, c), y -> setUnion(x, y))) + } + + have(thesis) by Tautology.from(lastStep, powerAxiom of (y -> setUnion(x, y), x -> unorderedPair(c, c))) + + } + + have((in(c, x), in(d, y)) |- in(pair(c, d), powerSet(powerSet(setUnion(x, y))))) subproof { + + have((in(c, x), in(d, y)) |- subset(pair(c, d), powerSet(setUnion(x, y)))) subproof { + val zp = have(in(z, pair(c, d)) <=> ((z === unorderedPair(c, d)) \/ (z === unorderedPair(c, c)))) by Restate.from(pairAxiom of (x -> unorderedPair(c, d), y -> unorderedPair(c, c))) + + val zcc = have((z === unorderedPair(c, c), in(c, x)) |- in(z, powerSet(setUnion(x, y)))) by Substitution.ApplyRules(z === unorderedPair(c, c))(upCC) + val zcd = have((z === unorderedPair(c, d), in(c, x), in(d, y)) |- in(z, powerSet(setUnion(x, y)))) by Substitution.ApplyRules(z === unorderedPair(c, d))(upCD) + + have((in(c, x), in(d, y)) |- in(z, pair(c, d)) ==> in(z, powerSet(setUnion(x, y)))) by Tautology.from(zp, zcc, zcd) + thenHave((in(c, x), in(d, y)) |- forall(z, in(z, pair(c, d)) ==> in(z, powerSet(setUnion(x, y))))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> pair(c, d), y -> powerSet(setUnion(x, y)))) + } + + have(thesis) by Tautology.from(lastStep, powerAxiom of (y -> powerSet(setUnion(x, y)), x -> pair(c, d))) + + } + + thenHave((t === pair(c, d), in(c, x), in(d, y)) |- in(t, powerSet(powerSet(setUnion(x, y))))) by Substitution.ApplyRules(t === pair(c, d)) + thenHave(((t === pair(c, d)) /\ in(c, x) /\ in(d, y)) |- in(t, powerSet(powerSet(setUnion(x, y))))) by Restate + thenHave(exists(d, ((t === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(t, powerSet(powerSet(setUnion(x, y))))) by LeftExists + thenHave(thesis) by LeftExists + } + + /** + * Theorem --- the binary set union operation is commutative. + * + * `a ∪ b = b ∪ a` + */ + val unionCommutativity = Theorem( + setUnion(a, b) === setUnion(b, a) + ) { + have(in(z, setUnion(a, b)) <=> in(z, setUnion(b, a))) by Tautology.from(setUnionMembership of (x -> a, y -> b), setUnionMembership of (x -> b, y -> a)) + thenHave(forall(z, in(z, setUnion(a, b)) <=> in(z, setUnion(b, a)))) by RightForall + + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> setUnion(a, b), y -> setUnion(b, a))) + } + + /** + * Theorem --- the first element of a union is a subset of it. + * + * `a ⊆ a ∪ b` + */ + val unionSubsetFirst = Theorem( + subset(a, setUnion(a, b)) + ) { + have(in(z, a) ==> in(z, setUnion(a, b))) by Weakening(setUnionMembership of (x -> a, y -> b)) + thenHave(forall(z, in(z, a) ==> in(z, setUnion(a, b)))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> a, y -> setUnion(a, b))) + } + + /** + * Theorem --- the second element of a union is a subset of it. + * + * `a ⊆ a ∪ b` + */ + val unionSubsetSecond = Theorem( + subset(b, setUnion(a, b)) + ) { + have(thesis) by Substitution.ApplyRules(unionCommutativity)(unionSubsetFirst of (a -> b, b -> a)) + } + + /** + * Theorem --- the union of two subsets of a set is still a subset of it. + * + * `a ⊆ c ∧ b ⊆ c ⊢ a ∪ b ⊆ c` + */ + val unionOfTwoSubsets = Theorem( + subset(a, c) /\ subset(b, c) |- subset(setUnion(a, b), c) + ) { + val unionDef = have(in(z, setUnion(a, b)) <=> (in(z, a) \/ in(z, b))) by Restate.from(setUnionMembership of (x -> a, y -> b)) + + have(subset(a, c) |- forall(z, in(z, a) ==> in(z, c))) by Weakening(subsetAxiom of (x -> a, y -> c)) + val ac = thenHave(subset(a, c) |- in(z, a) ==> in(z, c)) by InstantiateForall(z) + val bc = ac of a -> b + + have(subset(a, c) /\ subset(b, c) |- in(z, setUnion(a, b)) ==> in(z, c)) by Tautology.from(unionDef, ac, bc) + thenHave(subset(a, c) /\ subset(b, c) |- forall(z, in(z, setUnion(a, b)) ==> in(z, c))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> setUnion(a, b), y -> c)) + } + + /** + * Theorem --- the union of subsets of two sets is a subset of their union. + * + * `a ⊆ c ∧ b ⊆ d ⊢ a ∪ b ⊆ c ∪ d` + */ + val unionOfSubsetsOfDifferentSets = Theorem( + subset(a, c) /\ subset(b, d) |- subset(setUnion(a, b), setUnion(c, d)) + ) { + val unionDefab = have(in(z, setUnion(a, b)) <=> (in(z, a) \/ in(z, b))) by Restate.from(setUnionMembership of (x -> a, y -> b)) + val unionDefcd = unionDefab of (a -> c, b -> d) + + have(subset(a, c) |- forall(z, in(z, a) ==> in(z, c))) by Weakening(subsetAxiom of (x -> a, y -> c)) + val ac = thenHave(subset(a, c) |- in(z, a) ==> in(z, c)) by InstantiateForall(z) + val bc = ac of (a -> b, c -> d) + + have(subset(a, c) /\ subset(b, d) |- in(z, setUnion(a, b)) ==> (in(z, c) \/ in(z, d))) by Tautology.from(unionDefab, ac, bc) + thenHave(subset(a, c) /\ subset(b, d) |- in(z, setUnion(a, b)) ==> in(z, setUnion(c, d))) by Substitution.ApplyRules(unionDefcd) + thenHave(subset(a, c) /\ subset(b, d) |- forall(z, in(z, setUnion(a, b)) ==> in(z, setUnion(c, d)))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> setUnion(a, b), y -> setUnion(c, d))) + } + + /** + * Theorem --- the subset predicate is transitive. + * + * `a ⊆ b ∧ b ⊆ c ⊢ a ⊆ c` + */ + val subsetTransitivity = Theorem( + subset(a, b) /\ subset(b, c) |- subset(a, c) + ) { + have(subset(a, b) |- forall(z, in(z, a) ==> in(z, b))) by Weakening(subsetAxiom of (x -> a, y -> b)) + val sab = thenHave(subset(a, b) |- in(z, a) ==> in(z, b)) by InstantiateForall(z) + val sbc = sab of (a -> b, b -> c) + + have(subset(a, b) /\ subset(b, c) |- in(z, a) ==> in(z, c)) by Tautology.from(sab, sbc) + thenHave(subset(a, b) /\ subset(b, c) |- forall(z, in(z, a) ==> in(z, c))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> a, y -> c)) + } + + /** + * Theorem --- a set is an element of a Cartesian product iff it is a pair containing + * elements from the constituents of the product. + * + * `t ∈ x * y <=> ∃ a, b. t = (a, b) ∧ a ∈ x ∧ b ∈ y` + * + * Asserts a stronger definition of the [[cartesianProduct]]. See + * [[elemOfPowerPowerUnion]] for the redundancy proof. + */ + val elemOfCartesianProduct = Theorem( + in(t, cartesianProduct(x, y)) <=> ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))) + ) { + have(forall(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by InstantiateForall( + cartesianProduct(x, y) + )(cartesianProduct.definition) + val defUnfold = thenHave(in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))))) by InstantiateForall(t) + + have(∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y))) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y))))) by Tautology.from( + elemOfPowerPowerUnion + ) + + have(thesis) by Tautology.from(lastStep, defUnfold) + } + + /** + * Theorem --- the union of two Cartesian products is a subset of the product of unions. + * + * `a * b ∪ c * d ⊆ (a ∪ c) * (b ∪ d)` + */ + val unionOfCartesianProducts = Theorem( + subset(setUnion(cartesianProduct(a, b), cartesianProduct(c, d)), cartesianProduct(setUnion(a, c), setUnion(b, d))) + ) { + val axb = cartesianProduct(a, b) + val cxd = cartesianProduct(c, d) + + val unionDef = have(in(z, setUnion(axb, cxd)) |- in(z, axb) \/ in(z, cxd)) by Weakening(setUnionMembership of (x -> axb, y -> cxd)) + + /* + z in a x b + <=> + exist x, y. z = (x, y); x in a; y in b + ==> x in a U c, y in b U d + ==> z in (a U c) x (b U d) + */ + val zab = have(in(z, axb) |- in(z, cartesianProduct(setUnion(a, c), setUnion(b, d)))) subproof { + have(forall(z, in(z, a) ==> in(z, setUnion(a, c)))) by Tautology.from(unionSubsetFirst of (b -> c), subsetAxiom of (x -> a, y -> setUnion(a, c))) + val xa = thenHave((in(x, a) ==> in(x, setUnion(a, c)))) by InstantiateForall(x) + + have(forall(z, in(z, b) ==> in(z, setUnion(b, d)))) by Tautology.from(unionSubsetFirst of (a -> b, b -> d), subsetAxiom of (x -> b, y -> setUnion(b, d))) + val yb = thenHave((in(y, b) ==> in(y, setUnion(b, d)))) by InstantiateForall(y) + + have(in(x, a) /\ in(y, b) |- in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))) by Tautology.from(xa, yb) + thenHave((z === pair(x, y)) /\ in(x, a) /\ in(y, b) |- (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))) by Tautology + thenHave((z === pair(x, y)) /\ in(x, a) /\ in(y, b) |- exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d)))) by RightExists + thenHave((z === pair(x, y)) /\ in(x, a) /\ in(y, b) |- exists(x, exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))))) by RightExists + thenHave(exists(y, (z === pair(x, y)) /\ in(x, a) /\ in(y, b)) |- exists(x, exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))))) by LeftExists + thenHave(exists(x, exists(y, (z === pair(x, y)) /\ in(x, a) /\ in(y, b))) |- exists(x, exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))))) by LeftExists + + have(thesis) by Tautology.from(lastStep, elemOfCartesianProduct of (x -> a, y -> b, t -> z), elemOfCartesianProduct of (x -> setUnion(a, c), y -> setUnion(b, d), t -> z)) + } + + val zcd = + have(in(z, cxd) |- in(z, cartesianProduct(setUnion(a, c), setUnion(b, d)))) by Substitution.ApplyRules(unionCommutativity)( + lastStep of (a -> c, b -> d, c -> a, d -> b) + ) + + have(in(z, setUnion(axb, cxd)) ==> in(z, cartesianProduct(setUnion(a, c), setUnion(b, d)))) by Tautology.from(unionDef, zab, zcd) + thenHave(forall(z, in(z, setUnion(axb, cxd)) ==> in(z, cartesianProduct(setUnion(a, c), setUnion(b, d))))) by RightForall + + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> setUnion(axb, cxd), y -> cartesianProduct(setUnion(a, c), setUnion(b, d)))) + } + + /** + * Theorem --- if a pair is in a set `r`, then elements of the pair are in `∪ ∪ r`. + * + * `(a, b) ∈ r ⊢ a, b ∈ ∪ ∪ r` + * + * Used to prove stronger definitions for [[relationDomain]] and [[relationRange]] + */ + val pairInSetImpliesPairInUnion = Theorem( + in(pair(a, b), r) |- in(a, union(union(r))) /\ in(b, union(union(r))) + ) { + // a, b in {a, b} and union union r + // {a, b} in union r + // pair a b in r + val unionUP = have(in(pair(a, b), r) |- in(unorderedPair(a, b), union(r))) subproof { + val hypo = have(in(pair(a, b), r) |- in(pair(a, b), r)) by Hypothesis + have(in(pair(a, b), r) |- in(unorderedPair(a, b), pair(a, b)) /\ in(pair(a, b), r)) by RightAnd(hypo, firstElemInPair of (x -> unorderedPair(a, b), y -> singleton(a))) + thenHave(in(pair(a, b), r) |- ∃(y, in(unorderedPair(a, b), y) /\ in(y, r))) by RightExists + andThen(Substitution.applySubst(unionAxiom of (z -> unorderedPair(a, b), x -> r))) + } + val unionA = have(in(unorderedPair(a, b), union(r)) |- in(a, union(union(r)))) subproof { + val hypo = have(in(unorderedPair(a, b), union(r)) |- in(unorderedPair(a, b), union(r))) by Hypothesis + have(in(unorderedPair(a, b), union(r)) |- in(a, unorderedPair(a, b)) /\ in(unorderedPair(a, b), union(r))) by RightAnd(hypo, firstElemInPair of (x -> a, y -> b)) + thenHave(in(unorderedPair(a, b), union(r)) |- ∃(y, in(a, y) /\ in(y, union(r)))) by RightExists + andThen(Substitution.applySubst(unionAxiom of (z -> a, x -> union(r)))) + } + val unionB = have(in(unorderedPair(a, b), union(r)) |- in(b, union(union(r)))) subproof { + val hypo = have(in(unorderedPair(a, b), union(r)) |- in(unorderedPair(a, b), union(r))) by Hypothesis + have(in(unorderedPair(a, b), union(r)) |- in(b, unorderedPair(a, b)) /\ in(unorderedPair(a, b), union(r))) by RightAnd(hypo, secondElemInPair of (x -> a, y -> b)) + thenHave(in(unorderedPair(a, b), union(r)) |- ∃(y, in(b, y) /\ in(y, union(r)))) by RightExists + andThen(Substitution.applySubst(unionAxiom of (z -> b, x -> union(r)))) + } + + have(thesis) by Tautology.from(unionUP, unionA, unionB) + } + + /** + * Binary Relation --- A binary relation `r` from `a` to `b` is a subset of + * the [[cartesianProduct]] of `a` and `b`, `a * b`. We say `x r y`, `r(x, + * y)`, or `r relates x to y` for `(x, y) ∈ r`. + */ + val relationBetween = DEF(r, a, b) --> subset(r, cartesianProduct(a, b)) + + /** + * `r` is a relation *from* `a` if there exists a set `b` such that `r` is a + * relation from `a` to `b`. + */ + val relationFrom = DEF(r, a) --> ∃(b, relationBetween(r, a, b)) + + /** + * `r` is a relation if there exist sets `a` and `b` such that `r` is a + * relation from `a` to `b`. + */ + val relation = DEF(r) --> ∃(a, ∃(b, relationBetween(r, a, b))) + + val relationDomainUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> ∃(a, in(pair(t, a), r)))) + ) { + val uniq = have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))))) by UniqueComprehension( + union(union(r)), + lambda(t, ∃(a, in(pair(t, a), r))) + ) + + // eliminating t \in UU r + // since it is implied by the second condition + val transform = have(∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) subproof { + val hypo = have(in(pair(t, a), r) |- in(pair(t, a), r)) by Hypothesis + have(in(pair(t, a), r) |- in(t, union(union(r))) /\ in(a, union(union(r)))) by Cut(hypo, pairInSetImpliesPairInUnion of (a -> t, b -> a)) + thenHave(in(pair(t, a), r) |- in(t, union(union(r)))) by Weakening + thenHave(∃(a, in(pair(t, a), r)) |- in(t, union(union(r)))) by LeftExists + val lhs = thenHave(∃(a, in(pair(t, a), r)) ==> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) by Tautology + val rhs = have((∃(a, in(pair(t, a), r)) /\ in(t, union(union(r)))) ==> ∃(a, in(pair(t, a), r))) by Restate + + val subst = have(∃(a, in(pair(t, a), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) by RightIff(lhs, rhs) + + have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) by Hypothesis + val cutRhs = thenHave( + (in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))), ∃(a, in(pair(t, a), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (∃( + a, + in(pair(t, a), r) + )) + ) by RightSubstIff.withParametersSimple(List((∃(a, in(pair(t, a), r)), in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))), lambda(h, in(t, z) <=> h)) + have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (∃(a, in(pair(t, a), r)))) by Cut(subst, cutRhs) + thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (∃(a, in(pair(t, a), r)))) by LeftForall + thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r))))) by RightForall + thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by RightExists + thenHave(thesis) by LeftExists + } + + // converting the exists to existsOne + val cutL = have( + ∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) + ) by Restate.from(existsOneImpliesExists of (P -> lambda(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))))) + val cutR = have(∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by Restate.from( + uniqueByExtension of (schemPred -> lambda(t, (∃(a, in(pair(t, a), r))))) + ) + + val trL = + have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by Cut(cutL, transform) + val trR = + have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by Cut(trL, cutR) + + have(thesis) by Cut.withParameters(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))))(uniq, trR) + } + + /** + * (Binary) Relation Domain --- The set containing the first elements of every + * pair in a relation `r`. Alternatively, the set of elements which are + * related to another element by `r`. + * + * `dom(r) = {z ∈ ∪ ∪ r | ∃ t. (z, t) ∈ r}` + * + * The proofs are guaranteed and generated by [[UniqueComprehension]]. + * + * The first part is proved redundant by [[pairInSetImpliesPairInUnion]]. + * We have, + * + * `dom(r) = {z | ∃ t. (z, t) ∈ r}` + * + * @param r relation (set) + */ + val relationDomain = DEF(r) --> The(z, ∀(t, in(t, z) <=> ∃(a, in(pair(t, a), r))))(relationDomainUniqueness) + + val relationRangeUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> ∃(a, in(pair(a, t), r)))) + ) { + val uniq = have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))))) by UniqueComprehension( + union(union(r)), + lambda(t, ∃(a, in(pair(a, t), r))) + ) + + // eliminating t \in UU r + // since it is implied by the second condition + val transform = have(∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) subproof { + val hypo = have(in(pair(a, t), r) |- in(pair(a, t), r)) by Hypothesis + have(in(pair(a, t), r) |- in(t, union(union(r))) /\ in(a, union(union(r)))) by Cut(hypo, pairInSetImpliesPairInUnion of (a -> a, b -> t)) + thenHave(in(pair(a, t), r) |- in(t, union(union(r)))) by Weakening + thenHave(∃(a, in(pair(a, t), r)) |- in(t, union(union(r)))) by LeftExists + val lhs = thenHave(∃(a, in(pair(a, t), r)) ==> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) by Tautology + val rhs = have((∃(a, in(pair(a, t), r)) /\ in(t, union(union(r)))) ==> ∃(a, in(pair(a, t), r))) by Restate + + val subst = have(∃(a, in(pair(a, t), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) by RightIff(lhs, rhs) + + have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) by Hypothesis + val cutRhs = thenHave( + (in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))), ∃(a, in(pair(a, t), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (∃( + a, + in(pair(a, t), r) + )) + ) by RightSubstIff.withParametersSimple(List((∃(a, in(pair(a, t), r)), in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))), lambda(h, in(t, z) <=> h)) + have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (∃(a, in(pair(a, t), r)))) by Cut(subst, cutRhs) + thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (∃(a, in(pair(a, t), r)))) by LeftForall + thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r))))) by RightForall + thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by RightExists + thenHave(thesis) by LeftExists + } + + // converting the exists to existsOne + val cutL = have( + ∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) + ) by Restate.from(existsOneImpliesExists of (P -> lambda(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))))) + val cutR = have(∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by Restate.from( + uniqueByExtension of (schemPred -> lambda(t, (∃(a, in(pair(a, t), r))))) + ) + + val trL = + have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by Cut(cutL, transform) + val trR = + have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by Cut(trL, cutR) + + have(thesis) by Cut.withParameters(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))))(uniq, trR) + } + + /** + * Theorem --- the relation domain of the empty set is the empty set. + */ + val domainOfEmptySetIsEmpty = Theorem( + relationDomain(∅) === ∅ + ) { + have(∀(t, in(t, relationDomain(∅)) <=> ∃(a, in(pair(t, a), ∅)))) by InstantiateForall(relationDomain(∅))( + relationDomain.definition of (r := ∅) + ) + val domainDef = thenHave(in(t, relationDomain(∅)) <=> ∃(a, in(pair(t, a), ∅))) by InstantiateForall(t) // todo: check if can be simplified with shortDefinition + val contraPosDomainDef = have(!in(t, relationDomain(∅)) <=> ∀(a, !in(pair(t, a), ∅))) by Tautology.from(domainDef) + have(!in(pair(t, a), ∅)) by Tautology.from(emptySetAxiom of (x := pair(t, a))) + val nothingInEmpty = thenHave(∀(a, !in(pair(t, a), ∅))) by RightForall + + have(!in(t, relationDomain(∅))) by Tautology.from(nothingInEmpty, contraPosDomainDef) + thenHave(∀(t, !in(t, relationDomain(∅)))) by RightForall + + have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (x := relationDomain(∅))) + } + + /** + * (Binary) Relation Range --- The set containing the second elements of every + * pair in a relation `r`. Alternatively, the set of elements which another + * element relates to by `r`. + * + * `range(r) = {z ∈ ∪ ∪ r | ∃ t. (t, z) ∈ r} + * + * The proofs are guaranteed and generated by [[UniqueComprehension]]. + * + * The first part is proved redundant by [[pairInSetImpliesPairInUnion]]. + * We have, + * + * `range(r) = {z | ∃ t. (t, z) ∈ r}` + * + * @param r relation (set) + */ + val relationRange = DEF(r) --> The(z, ∀(t, in(t, z) <=> ∃(a, in(pair(a, t), r))))(relationRangeUniqueness) + + /** + * Theorem --- If `r` is a relation, then `r` is a relation between its domain and its range. + */ + val relationImpliesRelationBetweenDomainAndRange = Theorem( + relation(r) |- relationBetween(r, relationDomain(r), relationRange(r)) + ) { + // Lay out the definitions to apply them later + have(∀(t, in(t, relationDomain(r)) <=> ∃(b, in(pair(t, b), r)))) by Definition(relationDomain, relationDomainUniqueness)(r) + val relationDomainDef = thenHave(in(t, relationDomain(r)) <=> ∃(b, in(pair(t, b), r))) by InstantiateForall(t) + + have(∀(t, in(t, relationRange(r)) <=> ∃(a, in(pair(a, t), r)))) by Definition(relationRange, relationRangeUniqueness)(r) + val relationRangeDef = thenHave(in(t, relationRange(r)) <=> ∃(a, in(pair(a, t), r))) by InstantiateForall(t) + + // Start the proof + have(relation(r) |- ∃(x, ∃(y, relationBetween(r, x, y)))) by Tautology.from(relation.definition) + + have(relationBetween(r, x, y) |- subset(r, cartesianProduct(x, y))) by Tautology.from(relationBetween.definition of (a -> x, b -> y)) + have(relationBetween(r, x, y) |- ∀(t, in(t, r) ==> in(t, cartesianProduct(x, y)))) by Tautology.from( + lastStep, + subset.definition of (x -> r, y -> cartesianProduct(x, y)) + ) + thenHave(relationBetween(r, x, y) |- in(t, r) ==> in(t, cartesianProduct(x, y))) by InstantiateForall(t) + thenHave((relationBetween(r, x, y), in(t, r)) |- in(t, cartesianProduct(x, y))) by Restate + + // Apply the definition of the cartesian product + val relationDef = have((relationBetween(r, x, y), in(t, r)) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) by Tautology.from( + lastStep, + elemOfCartesianProduct + ) + + // Show that x ⊇ relationDomain(r) and y ⊇ relationRange(r) + val memberships = have((in(t, r), (t === pair(a, b))) |- in(a, relationDomain(r)) /\ in(b, relationRange(r))) subproof { + have(in(t, r) |- in(t, r)) by Hypothesis + val membership = thenHave((in(t, r), (t === pair(a, b))) |- in(pair(a, b), r)) by Substitution.ApplyRules(t === pair(a, b)) + + assume(in(t, r)) + assume(t === pair(a, b)) + have(∃(b, in(pair(a, b), r))) by RightExists(membership) + val left = have(in(a, relationDomain(r))) by Tautology.from(lastStep, relationDomainDef of (t -> a)) + + have(∃(a, in(pair(a, b), r))) by RightExists(membership) + val right = have(in(b, relationRange(r))) by Tautology.from(lastStep, relationRangeDef of (t -> b)) + + have(thesis) by RightAnd(left, right) + } + + // We can now reconstruct the definition of relationBetween(r, relationDomain(r), relationRange(r)) + have((t === pair(a, b)) |- (t === pair(a, b))) by Hypothesis + val toCut = have((in(t, r), (t === pair(a, b))) |- (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))) by RightAnd(lastStep, memberships) + + have((t === pair(a, b)) /\ in(a, x) /\ in(b, y) |- (t === pair(a, b))) by Tautology + have((in(t, r), (t === pair(a, b)) /\ in(a, x) /\ in(b, y)) |- (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))) by Cut(lastStep, toCut) + + // Re-add the existential quantifiers + thenHave((in(t, r), (t === pair(a, b)) /\ in(a, x) /\ in(b, y)) |- ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r)))) by RightExists + thenHave((in(t, r), (t === pair(a, b)) /\ in(a, x) /\ in(b, y)) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by RightExists + thenHave((in(t, r), ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by LeftExists + thenHave((in(t, r), ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by LeftExists + + // Cut and rewrap the definition + have((in(t, r), relationBetween(r, x, y)) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by Cut( + relationDef, + lastStep + ) + have((in(t, r), relationBetween(r, x, y)) |- in(t, cartesianProduct(relationDomain(r), relationRange(r)))) by Tautology.from( + lastStep, + elemOfCartesianProduct of (x -> relationDomain(r), y -> relationRange(r)) + ) + thenHave(relationBetween(r, x, y) |- in(t, r) ==> in(t, cartesianProduct(relationDomain(r), relationRange(r)))) by Restate + thenHave(relationBetween(r, x, y) |- ∀(t, in(t, r) ==> in(t, cartesianProduct(relationDomain(r), relationRange(r))))) by RightForall + have(relationBetween(r, x, y) |- subset(r, cartesianProduct(relationDomain(r), relationRange(r)))) by Tautology.from( + lastStep, + subset.definition of (x -> r, y -> cartesianProduct(relationDomain(r), relationRange(r))) + ) + have(relationBetween(r, x, y) |- relationBetween(r, relationDomain(r), relationRange(r))) by Tautology.from( + lastStep, + relationBetween.definition of (a -> relationDomain(r), b -> relationRange(r)) + ) + + // Add the existential quantifier to finish the proofs + thenHave(∃(y, relationBetween(r, x, y)) |- relationBetween(r, relationDomain(r), relationRange(r))) by LeftExists + thenHave(∃(x, ∃(y, relationBetween(r, x, y))) |- relationBetween(r, relationDomain(r), relationRange(r))) by LeftExists + + have(thesis) by Tautology.from(lastStep, relation.definition) + } + + /** + * Theorem --- The Cartesian Product of two sets is a relation. + */ + val cartesianProductIsRelation = Theorem( + relation(cartesianProduct(x, y)) + ) { + // trivially follows from the fact that a relation is a subset of the cartesian product + have(relationBetween(cartesianProduct(x, y), x, y)) by Tautology.from( + subsetReflexivity of (x := cartesianProduct(x, y)), + relationBetween.definition of (r := cartesianProduct(x, y), a := x, b := y) + ) + thenHave(∃(b, relationBetween(cartesianProduct(x, y), x, b))) by RightExists + val relationSpec = thenHave(∃(a, ∃(b, relationBetween(cartesianProduct(x, y), a, b)))) by RightExists + + have(thesis) by Tautology.from(relationSpec, relation.definition of (r := cartesianProduct(x, y))) + } + + /** + * (Binary) Relation Field --- The union of the domain and range of a + * relation, or the set of all elements related by `r`. + * + * @param r relation (set) + */ + val relationField = DEF(r) --> (setUnion(relationDomain(r), relationRange(r))) + + /** + * Theorem --- the union of two relations is a relation, with domains and codomains + * unions of the constituents. + * + * Effectively, + * + * `f ⊆ a * b; g ⊆ c * d ⊢ (f ∪ g) ⊆ (a ∪ c) * (b ∪ d)` + */ + val unionOfTwoRelationsWithField = Theorem( + relationBetween(f, a, b) /\ relationBetween(g, c, d) |- relationBetween(setUnion(f, g), setUnion(a, c), setUnion(b, d)) + ) { + val fab = have(relationBetween(f, a, b) <=> subset(f, cartesianProduct(a, b))) by Restate.from(relationBetween.definition of r -> f) + val gcd = fab of (f -> g, a -> c, b -> d) + val fug = fab of (f -> setUnion(f, g), a -> setUnion(a, c), b -> setUnion(b, d)) + + have(subset(f, cartesianProduct(a, b)) /\ subset(g, cartesianProduct(c, d)) |- subset(setUnion(f, g), cartesianProduct(setUnion(a, c), setUnion(b, d)))) by Tautology.from( + unionOfCartesianProducts, + unionOfSubsetsOfDifferentSets of (a -> f, b -> g, c -> cartesianProduct(a, b), d -> cartesianProduct(c, d)), + subsetTransitivity of (a -> setUnion(f, g), b -> setUnion(cartesianProduct(a, b), cartesianProduct(c, d)), c -> cartesianProduct(setUnion(a, c), setUnion(b, d))) + ) + + have(thesis) by Tautology.from(lastStep, fab, gcd, fug) + } + + /** + * Theorem --- the union of two relations is a relation. (weaker form) + * + * Weakening of [[unionOfTwoRelationsWithField]] to unknown fields. + */ + val unionOfTwoRelations = Theorem( + relation(f) /\ relation(g) |- relation(setUnion(f, g)) + ) { + val relf = have(relation(f) <=> exists(x, exists(y, relationBetween(f, x, y)))) by Restate.from(relation.definition of r -> f) + val relg = relf of f -> g + val relfug = relf of f -> setUnion(f, g) + + have((relationBetween(f, a, b), relationBetween(g, c, d)) |- relationBetween(setUnion(f, g), setUnion(a, c), setUnion(b, d))) by Restate.from(unionOfTwoRelationsWithField) + thenHave((relationBetween(f, a, b), relationBetween(g, c, d)) |- exists(y, relationBetween(setUnion(f, g), setUnion(a, c), y))) by RightExists + thenHave((relationBetween(f, a, b), relationBetween(g, c, d)) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by RightExists + thenHave((relationBetween(f, a, b), exists(d, relationBetween(g, c, d))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists + thenHave((relationBetween(f, a, b), exists(c, exists(d, relationBetween(g, c, d)))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists + thenHave((exists(b, relationBetween(f, a, b)), exists(c, exists(d, relationBetween(g, c, d)))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists + thenHave((exists(a, exists(b, relationBetween(f, a, b))), exists(c, exists(d, relationBetween(g, c, d)))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists + + thenHave((relation(f), relation(g)) |- relation(setUnion(f, g))) by Substitution.ApplyRules(relf, relg, relfug) + } + + /** + * Theorem --- Pair in Relation + * + * If a pair `(x, y)` exists in a relation `r` from `a` to `b`, + * then `x` and `y` are elements of `a` and `b` respectively. + */ + val pairInRelation = Lemma( + relationBetween(r, a, b) /\ in(pair(x, y), r) |- in(x, a) /\ in(y, b) + ) { + assume(relationBetween(r, a, b)) + assume(in(pair(x, y), r)) + have(forall(t, in(t, r) ==> in(t, cartesianProduct(a, b)))) by Tautology.from(relationBetween.definition, subsetAxiom of (x -> r, y -> cartesianProduct(a, b))) + thenHave(in(pair(x, y), r) ==> in(pair(x, y), cartesianProduct(a, b))) by InstantiateForall(pair(x, y)) + have(thesis) by Tautology.from(lastStep, pairInCartesianProduct of (x -> a, y -> b, a -> x, b -> y)) + } + + // TODO: any subset of a functional is functional + // TODO: a functional over something restricted to x is still functional + + /** + * Properties of relations + */ + + /** + * Reflexive Relation --- `∀ x. x R x` + */ + val reflexive = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, in(y, x) ==> in(pair(y, y), r)) + + /** + * Symmetric Relation --- `∀ x y. x R y ⇔ y R x` + */ + val symmetric = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, in(pair(y, z), r) <=> in(pair(z, y), r))) + + /** + * Transitive Relation --- `∀ x y z. x R y ∧ y R z ⇒ x R z` + */ + val transitive = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(w, ∀(y, ∀(z, (in(pair(w, y), r) /\ in(pair(y, z), r)) ==> in(pair(w, z), r)))) + + /** + * Equivalence Relation --- A relation is an equivalence relation if it is + * [[reflexive]], [[symmetric]], and [[transitive]]. + */ + val equivalence = DEF(r, x) --> reflexive(r, x) /\ symmetric(r, x) /\ transitive(r, x) + + /** + * Anti-reflexive Relation --- `∀ x. ! x R x` + */ + val antiReflexive = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, in(y, x) ==> !in(pair(y, y), r)) + + /** + * Irreflexive Relation --- Alias for [[antiReflexive]]. + */ + val irreflexive = antiReflexive + + /** + * Anti-symmetric Relation --- `∀ x y. x R y ∧ y R x ⇒ y = x` + */ + val antiSymmetric = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(pair(y, z), r) /\ in(pair(z, y), r)) ==> (y === z))) + + /** + * Asymmetric Relation --- `∀ x y. x R y ⇔ ! y R x` + */ + val asymmetric = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, in(pair(y, z), r) ==> !in(pair(z, y), r))) + + /** + * Connected Relation --- `∀ x y. (x R y) ∨ (y R x) ∨ (y = x)` + */ + val connected = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(y, x) /\ in(z, x)) ==> (in(pair(y, z), r) \/ in(pair(z, y), r) \/ (y === z)))) + + /** + * Total Relation --- Alias for [[connected]]. + */ + val total = connected + + /** + * Strongly Connected Relation --- + * `∀ x y z. y R x ∧ z R x ⇒ y R z ∨ z R y` + */ + val stronglyConnected = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(y, x) /\ in(z, x)) ==> (in(pair(y, z), r) \/ in(pair(z, y), r)))) + + /** + * Theorem --- the empty set is a relation, the empty relation, between any two sets. + */ + val emptySetRelation = Theorem( + () |- relationBetween(emptySet, a, b) + ) { + have(thesis) by Tautology.from(emptySetIsASubset of (x -> cartesianProduct(a, b)), relationBetween.definition of (r -> emptySet)) + } + + /** + * Theorem --- the empty relation is a relation on the empty set. + */ + val emptySetRelationOnItself = Theorem( + () |- relationBetween(emptySet, emptySet, emptySet) + ) { + have(thesis) by Restate.from(emptySetRelation of (a -> emptySet, b -> emptySet)) + } + + /** + * Theorem --- empty relation on the empty set is reflexive. + */ + val emptyRelationReflexiveOnItself = Theorem( + () |- reflexive(emptySet, emptySet) + ) { + have(() |- in(y, emptySet) ==> in(pair(y, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> y)) + val refCond = thenHave(() |- forall(y, in(y, emptySet) ==> in(pair(y, y), emptySet))) by RightForall + + have(thesis) by Tautology.from(reflexive.definition of (r -> emptySet, x -> emptySet), emptySetRelationOnItself, refCond) + } + + /** + * Theorem --- the empty relation is symmetric. + */ + val emptyRelationSymmetric = Theorem( + () |- symmetric(emptySet, a) + ) { + have(() |- in(pair(y, z), emptySet) <=> in(pair(z, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(y, z)), emptySetAxiom of (x -> pair(z, y))) + thenHave(() |- forall(z, in(pair(y, z), emptySet) <=> in(pair(z, y), emptySet))) by RightForall + val symCond = thenHave(() |- forall(y, forall(z, in(pair(y, z), emptySet) <=> in(pair(z, y), emptySet)))) by RightForall + + have(thesis) by Tautology.from(symmetric.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), symCond) + } + + /** + * Theorem --- the empty relation is irreflexive. + */ + val emptyRelationIrreflexive = Theorem( + () |- irreflexive(emptySet, a) + ) { + have(() |- in(y, a) ==> !in(pair(y, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(y, y))) + val irrefCond = thenHave(() |- forall(y, in(y, a) ==> !in(pair(y, y), emptySet))) by RightForall + + have(thesis) by Tautology.from(irreflexive.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), irrefCond) + } + + /** + * Theorem --- the empty relation is transitive. + */ + val emptyRelationTransitive = Theorem( + () |- transitive(emptySet, a) + ) { + have(() |- (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(w, y))) + thenHave(() |- forall(z, (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet))) by RightForall + thenHave(() |- forall(y, forall(z, (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet)))) by RightForall + val trsCond = thenHave(() |- forall(w, forall(y, forall(z, (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet))))) by RightForall + + have(thesis) by Tautology.from(transitive.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), trsCond) + } + + /** + * Theorem --- the empty relation is an equivalence relation on the empty set. + */ + val emptyRelationEquivalence = Theorem( + () |- equivalence(emptySet, emptySet) + ) { + have(thesis) by Tautology.from( + equivalence.definition of (r -> emptySet, x -> emptySet), + emptyRelationReflexiveOnItself, + emptyRelationSymmetric of (a -> emptySet), + emptyRelationTransitive of (a -> emptySet) + ) + } + + /** + * Theorem --- the empty relation is anti-symmetric. + */ + val emptyRelationAntiSymmetric = Theorem( + () |- antiSymmetric(emptySet, a) + ) { + have(() |- (in(pair(y, z), emptySet) /\ in(pair(z, y), emptySet)) ==> (y === z)) by Tautology.from(emptySetAxiom of (x -> pair(y, z))) + thenHave(() |- forall(z, (in(pair(y, z), emptySet) /\ in(pair(z, y), emptySet)) ==> (y === z))) by RightForall + val ansymCond = thenHave(() |- forall(y, forall(z, (in(pair(y, z), emptySet) /\ in(pair(z, y), emptySet)) ==> (y === z)))) by RightForall + + have(thesis) by Tautology.from(antiSymmetric.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), ansymCond) + } + + /** + * Theorem --- the empty relation is asymmetric. + */ + val emptyRelationAsymmetric = Theorem( + () |- asymmetric(emptySet, a) + ) { + have(() |- in(pair(y, z), emptySet) ==> !in(pair(z, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(y, z))) + thenHave(() |- forall(z, in(pair(y, z), emptySet) ==> !in(pair(z, y), emptySet))) by RightForall + val asymCond = thenHave(() |- forall(y, forall(z, in(pair(y, z), emptySet) ==> !in(pair(z, y), emptySet)))) by RightForall + + have(thesis) by Tautology.from(asymmetric.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), asymCond) + } + + /** + * Theorem --- the empty relation is total on the empty set. + */ + val emptyRelationTotalOnItself = Theorem( + () |- total(emptySet, emptySet) + ) { + have((in(y, emptySet) /\ in(z, emptySet)) ==> (in(pair(y, z), emptySet) \/ in(pair(z, y), emptySet) \/ (y === z))) by Tautology.from(emptySetAxiom of x -> y) + thenHave(forall(z, (in(y, emptySet) /\ in(z, emptySet)) ==> (in(pair(y, z), emptySet) \/ in(pair(z, y), emptySet) \/ (y === z)))) by RightForall + thenHave(forall(y, forall(z, (in(y, emptySet) /\ in(z, emptySet)) ==> (in(pair(y, z), emptySet) \/ in(pair(z, y), emptySet) \/ (y === z))))) by RightForall + + have(thesis) by Tautology.from(lastStep, total.definition of (r -> emptySet, x -> emptySet), emptySetRelationOnItself) + } + + // smaller needed lemmas + // f from x to y => range f <= y + // f from x to y => dom f = x + // x <= y, y <= x |- x = y + + /** + * Theorem --- Symmetry of Equality and Subset + * + * [[equality]] implies a [[subset]] ordering, and [[subset]] ordering in both + * directions implies [[equality]]. + */ + val subsetEqualitySymmetry = Theorem( + (x === y) <=> (subset(x, y) /\ subset(y, x)) + ) { + have(subset(x, y) /\ subset(y, x) <=> subset(x, y) /\ subset(y, x)) by Restate + thenHave(subset(x, y) /\ subset(y, x) <=> forall(t, in(t, x) ==> in(t, y)) /\ subset(y, x)) by Substitution.ApplyRules(subsetAxiom) + thenHave(subset(x, y) /\ subset(y, x) <=> forall(t, in(t, x) ==> in(t, y)) /\ forall(t, in(t, y) ==> in(t, x))) by Substitution.ApplyRules(subsetAxiom) + andThen(Substitution.applySubst(universalConjunctionCommutation of (P -> lambda(t, in(t, x) ==> in(t, y)), Q -> lambda(t, in(t, y) ==> in(t, x))))) + andThen(Substitution.applySubst(extensionalityAxiom)) + thenHave(thesis) by Restate + } + + /** + * Theorem --- a set containing only pairs is a relation, and vice versa. + * + * `(\forall t \in z. \exists a, b. t = (a, b)) <=> relation(z)` + * + * The domain and codomain of this relation can be obtained constructively by applying + * the [[replacementSchema]] with the [[firstInPair]] and [[secondInPair]] projection + * functions. + * + * Here, it is sufficient to deal with them abstractly through the definitions of + * [[relationDomain]] and [[relationRange]]. + */ + val setOfPairsIsRelation = Theorem( + forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) <=> relation(z) + ) { + // if the set contains only pairs, it is a relation + val fwd = have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) ==> relation(z)) subproof { + val dom = relationDomain(z) + val ran = relationRange(z) + + val inst = have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- (in(t, z) ==> exists(a, exists(b, (t === pair(a, b)))))) by InstantiateForall + + // unfold defs + have(forall(t, in(t, dom) <=> exists(a, in(pair(t, a), z)))) by InstantiateForall(dom)(relationDomain.definition of r -> z) + val inDom = thenHave((in(a, dom) <=> exists(b, in(pair(a, b), z)))) by InstantiateForall(a) + have(forall(t, in(t, ran) <=> exists(a, in(pair(a, t), z)))) by InstantiateForall(ran)(relationRange.definition of r -> z) + val inRan = thenHave((in(b, ran) <=> exists(a, in(pair(a, b), z)))) by InstantiateForall(b) + + have((in(t, z)) |- in(t, z)) by Restate + val abz = thenHave((in(t, z), (t === pair(a, b))) |- in(pair(a, b), z)) by Substitution.ApplyRules(t === pair(a, b)) + + val exa = have((in(t, z), (t === pair(a, b))) |- exists(a, in(pair(a, b), z))) by RightExists(abz) + val exb = have((in(t, z), (t === pair(a, b))) |- exists(b, in(pair(a, b), z))) by RightExists(abz) + + have((in(t, z), (t === pair(a, b))) |- (t === pair(a, b)) /\ in(a, dom) /\ in(b, ran)) by Tautology.from(exa, exb, inDom, inRan) + thenHave((in(t, z), (t === pair(a, b))) |- exists(b, (t === pair(a, b)) /\ in(a, dom) /\ in(b, ran))) by RightExists + thenHave((in(t, z), (t === pair(a, b))) |- exists(a, exists(b, (t === pair(a, b)) /\ in(a, dom) /\ in(b, ran)))) by RightExists + + have((in(t, z), (t === pair(a, b))) |- in(t, cartesianProduct(dom, ran))) by Tautology.from(lastStep, elemOfCartesianProduct of (x -> dom, y -> ran)) + thenHave((in(t, z), exists(b, t === pair(a, b))) |- in(t, cartesianProduct(dom, ran))) by LeftExists + thenHave((in(t, z), exists(a, exists(b, t === pair(a, b)))) |- in(t, cartesianProduct(dom, ran))) by LeftExists + + have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- in(t, z) ==> in(t, cartesianProduct(dom, ran))) by Tautology.from(lastStep, inst) + thenHave(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- forall(t, in(t, z) ==> in(t, cartesianProduct(dom, ran)))) by RightForall + + have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- relationBetween(z, dom, ran)) by Tautology.from( + lastStep, + subsetAxiom of (x -> z, y -> cartesianProduct(dom, ran)), + relationBetween.definition of (r -> z, a -> dom, b -> ran) + ) + thenHave(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- exists(b, relationBetween(z, dom, b))) by RightExists + thenHave(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- exists(a, exists(b, relationBetween(z, a, b)))) by RightExists + + have(thesis) by Tautology.from(lastStep, relation.definition of r -> z) + } + + // if the set is a relation, it contains only pairs + val bwd = have(relation(z) ==> forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b)))))) subproof { + have(subset(z, cartesianProduct(c, d)) |- forall(t, in(t, z) ==> in(t, cartesianProduct(c, d)))) by Weakening(subsetAxiom of (x -> z, y -> cartesianProduct(c, d))) + val tz = thenHave(subset(z, cartesianProduct(c, d)) |- (in(t, z) ==> in(t, cartesianProduct(c, d)))) by InstantiateForall(t) + + have(in(t, cartesianProduct(c, d)) |- exists(a, exists(b, (t === pair(a, b))))) subproof { + have(((t === pair(a, b)) /\ in(a, c) /\ in(a, b)) ==> (t === pair(a, b))) by Restate + thenHave(forall(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b)) ==> (t === pair(a, b)))) by RightForall + have(exists(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b))) ==> exists(b, (t === pair(a, b)))) by Cut( + lastStep, + existentialImplicationDistribution of (P -> lambda(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b))), Q -> lambda(b, (t === pair(a, b)))) + ) + thenHave(forall(a, exists(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b))) ==> exists(b, (t === pair(a, b))))) by RightForall + val elemCart = have(exists(a, exists(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b)))) ==> exists(a, exists(b, (t === pair(a, b))))) by Cut( + lastStep, + existentialImplicationDistribution of (P -> lambda(a, exists(b, (t === pair(a, b)) /\ in(a, c) /\ in(a, b))), Q -> lambda(a, exists(b, t === pair(a, b)))) + ) + + // TODO: Tautology bug + have(thesis) by Tautology.from(lastStep, elemOfCartesianProduct of (x -> c, y -> d, z -> t)) + } + + have(relationBetween(z, c, d) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by Tautology.from(lastStep, tz, relationBetween.definition of (r -> z, a -> c, b -> d)) + thenHave(exists(d, relationBetween(z, c, d)) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by LeftExists + thenHave(exists(c, exists(d, relationBetween(z, c, d))) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by LeftExists + + have(relation(z) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by Tautology.from(lastStep, relation.definition of r -> z) + thenHave(relation(z) |- forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b)))))) by RightForall + thenHave(thesis) by Restate + } + + have(thesis) by Tautology.from(fwd, bwd) + } + + /** + * Theorem --- the union of a set of relations is a relation itself. + * + * `\forall t \in z. relation(t) |- relation(union(z))` + * + * This implication also holds in the other direction, but that is + * not as useful. + */ + val unionOfRelationSet = Theorem( + forall(t, in(t, z) ==> relation(t)) |- relation(union(z)) + ) { + // union of a set of relations contains only pairs + have(forall(t, in(t, z) ==> relation(t)) |- forall(t, in(t, union(z)) ==> exists(a, exists(b, (t === pair(a, b)))))) subproof { + assume(forall(t, in(t, z) ==> relation(t))) + have(in(x, z) ==> relation(x)) by InstantiateForall + have(in(x, z) |- forall(t, in(t, x) ==> exists(a, exists(b, (t === pair(a, b)))))) by Tautology.from(lastStep, setOfPairsIsRelation of z -> x) + thenHave((in(x, z) /\ in(t, x)) |- exists(a, exists(b, (t === pair(a, b))))) by InstantiateForall(t) + thenHave(exists(x, in(x, z) /\ in(t, x)) |- exists(a, exists(b, (t === pair(a, b))))) by LeftExists + + have(in(t, union(z)) ==> exists(a, exists(b, (t === pair(a, b))))) by Tautology.from(lastStep, unionAxiom of (x -> z, z -> t)) + thenHave(thesis) by RightForall + } + + // a set of pairs is a relation + have(thesis) by Tautology.from(lastStep, setOfPairsIsRelation of z -> union(z)) + } + + /** + * Theorem --- Domain of Relational Union + * + * If the unary union of a set is relational, then its domain is defined + * precisely by the union of the domains of its elements. + * + * relation(\cup z) |- \forall t. t \in dom(U z) <=> \exists y \in z. t \in + * dom(y) + * + * This holds, particularly, as the elements of z must be relations + * themselves, which follows from the assumption. + */ + val domainOfRelationalUnion = Theorem( + relation(union(z)) |- forall(t, in(t, relationDomain(union(z))) <=> exists(y, in(y, z) /\ in(t, relationDomain(y)))) + ) { + val uz = union(z) + + have(forall(t, in(t, relationDomain(uz)) <=> exists(a, in(pair(t, a), uz)))) by InstantiateForall(relationDomain(uz))(relationDomain.definition of r -> uz) + val inDom = thenHave(in(t, relationDomain(uz)) <=> exists(a, in(pair(t, a), uz))) by InstantiateForall(t) + + assume(relation(uz)) // proof assumption + + have(exists(a, in(pair(t, a), uz)) <=> exists(y, in(y, z) /\ in(t, relationDomain(y)))) subproof { + // we prove the directions separately + val fwd = have(exists(a, in(pair(t, a), uz)) |- exists(y, in(y, z) /\ in(t, relationDomain(y)))) subproof { + have(in(pair(t, a), uz) |- exists(y, in(y, z) /\ in(t, relationDomain(y)))) subproof { + assume(in(pair(t, a), uz)) + // since \cup z is a union + // \exists y such that (t, a) \in y + // and so t \in dom y + val exy = have(exists(y, in(pair(t, a), y) /\ in(y, z))) by Tautology.from(unionAxiom of (z -> pair(t, a), x -> z)) + + have(exists(y, in(pair(t, a), y) /\ in(y, z)) |- exists(y, in(t, relationDomain(y)) /\ in(y, z))) subproof { + have(forall(z, (z === relationDomain(y)) <=> forall(t, in(t, z) <=> exists(a, in(pair(t, a), y))))) by Weakening(relationDomain.definition of r -> y) + thenHave(forall(t, in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y)))) by InstantiateForall(relationDomain(y)) + val inDomY = thenHave(in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y))) by InstantiateForall(t) + have(in(pair(t, a), y) |- in(pair(t, a), y)) by Hypothesis + thenHave(in(pair(t, a), y) |- exists(a, in(pair(t, a), y))) by RightExists + have(in(pair(t, a), y) /\ in(y, z) |- in(t, relationDomain(y)) /\ in(y, z)) by Tautology.from(lastStep, inDomY) + thenHave(in(pair(t, a), y) /\ in(y, z) |- exists(y, in(t, relationDomain(y)) /\ in(y, z))) by RightExists + thenHave(thesis) by LeftExists + } + + have(thesis) by Cut(exy, lastStep) + } + + thenHave(thesis) by LeftExists + } + val bwd = have(exists(y, in(y, z) /\ in(t, relationDomain(y))) |- exists(a, in(pair(t, a), uz))) subproof { + have(in(y, z) /\ in(t, relationDomain(y)) |- exists(a, in(pair(t, a), uz))) subproof { + assume(in(y, z) /\ in(t, relationDomain(y))) + have(forall(z, (z === relationDomain(y)) <=> forall(t, in(t, z) <=> exists(a, in(pair(t, a), y))))) by Weakening(relationDomain.definition of r -> y) + thenHave(forall(t, in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y)))) by InstantiateForall(relationDomain(y)) + thenHave(in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y))) by InstantiateForall(t) + val exA = thenHave(exists(a, in(pair(t, a), y))) by Tautology + + have(exists(a, in(pair(t, a), y)) |- exists(a, in(pair(t, a), uz))) subproof { + have(in(pair(t, a), y) |- in(pair(t, a), y) /\ in(y, z)) by Restate + thenHave(in(pair(t, a), y) |- exists(y, in(pair(t, a), y) /\ in(y, z))) by RightExists + have(in(pair(t, a), y) |- in(pair(t, a), uz)) by Tautology.from(lastStep, unionAxiom of (z -> pair(t, a), x -> z)) + thenHave(in(pair(t, a), y) |- exists(a, in(pair(t, a), uz))) by RightExists + thenHave(thesis) by LeftExists + } + + have(exists(a, in(pair(t, a), uz))) by Cut(exA, lastStep) + } + thenHave(thesis) by LeftExists + } + + have(thesis) by Tautology.from(fwd, bwd) + } + + have(in(t, relationDomain(union(z))) <=> exists(y, in(y, z) /\ in(t, relationDomain(y)))) by Tautology.from(inDom, lastStep) + thenHave(thesis) by RightForall + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala new file mode 100644 index 000000000..2957a9ec4 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala @@ -0,0 +1,108 @@ +package lisa.maths.settheory + +/** + * Revamp of the set theory library from scratch, since most of the current one is severely outdated. + * We can make a better presentation and organisation of theorems, better automation, uniform comments/latex tags, etc. + */ +object SetTheory2 extends lisa.Main { + import lisa.maths.settheory.SetTheory.* + // import Comprehensions.* + + private val s = variable + private val x = variable + private val x_1 = variable + private val y = variable + private val z = variable + private val f = function[1] + private val t = variable + private val A = variable + private val B = variable + private val C = variable + private val P = predicate[2] + private val Q = predicate[1] + private val Filter = predicate[1] + private val Map = function[1] + + val primReplacement = Theorem( + ∀(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))) |- + ∃(B, forall(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y)))) + ) { + have(thesis) by Restate.from(replacementSchema of (A := A, x := x, P := P)) + } + + val manyForall = Lemma( + ∀(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))).substitute(P := lambda((A, B), P(A, B) /\ ∀(C, P(A, C) ==> (B === C)))) <=> top + ) { + have(thesis) by Tableau + } + + val functionalIsFunctional = Theorem( + ∀(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))).substitute(P := lambda((A, B), Filter(A) /\ (B === Map(A)))) <=> top + ) { + + have(y === Map(x) |- (y === Map(x))) by Restate + thenHave((y === Map(x), z === Map(x)) |- y === z) by Substitution.ApplyRules(Map(x) === z) + thenHave(in(x, A) |- ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z))) by Weakening + thenHave(in(x, A) |- ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z)))) by RightForall + thenHave(in(x, A) |- ∀(y, ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z))))) by RightForall + thenHave(in(x, A) ==> ∀(y, ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z))))) by Restate + thenHave(∀(x, in(x, A) ==> ∀(y, ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z)))))) by RightForall + thenHave(thesis) by Restate + + } + + /** + * Theorem --- the refined replacement axiom. Easier to use as a rule than primReplacement. + */ + val replacement = Theorem( + ∃(B, ∀(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y) /\ ∀(z, P(x, z) ==> (z === y))))) + ) { + have(thesis) by Tautology.from(manyForall, primReplacement of (P := lambda((A, B), P(A, B) /\ ∀(C, P(A, C) ==> (B === C))))) + } + + val onePointRule = Theorem( + ∃(x, (x === y) /\ Q(x)) <=> Q(y) + ) { + val s1 = have(∃(x, (x === y) /\ Q(x)) ==> Q(y)) subproof { + assume(∃(x, (x === y) /\ Q(x))) + val ex = witness(lastStep) + val s1 = have(Q(ex)) by Tautology.from(ex.definition) + val s2 = have(ex === y) by Tautology.from(ex.definition) + have(Q(y)) by Substitution.ApplyRules(s2)(s1) + } + val s2 = have(Q(y) ==> ∃(x, (x === y) /\ Q(x))) subproof { + assume(Q(y)) + thenHave((y === y) /\ Q(y)) by Restate + thenHave(∃(x, (x === y) /\ Q(x))) by RightExists + thenHave(thesis) by Restate.from + } + have(thesis) by Tautology.from(s1, s2) + } + + /** + * Theorem - `∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) <=> (x === f(∅))` + */ + val singletonMap = Lemma( + ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) <=> (x === f(∅)) + ) { + val s1 = have(∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) ==> (x === f(∅))) subproof { + have(x === f(∅) |- x === f(∅)) by Restate + thenHave((x_1 === ∅, x === f(x_1)) |- x === f(∅)) by Substitution.ApplyRules(x_1 === ∅) + thenHave((x_1 === ∅) /\ (x === f(x_1)) |- x === f(∅)) by Restate + thenHave((in(x_1, singleton(∅))) /\ ((x === f(x_1))) |- x === f(∅)) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) + thenHave(∃(x_1, in(x_1, singleton(∅)) /\ ((x === f(x_1)))) |- x === f(∅)) by LeftExists + + } + + val s2 = have((x === f(∅)) ==> ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) subproof { + have(x === f(∅) |- (∅ === ∅) /\ (x === f(∅))) by Restate + thenHave(x === f(∅) |- in(∅, singleton(∅)) /\ (x === f(∅))) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) + thenHave(x === f(∅) |- ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) by RightExists + thenHave(thesis) by Restate.from + + } + + have(thesis) by Tautology.from(s1, s2) + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala new file mode 100644 index 000000000..23468423d --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala @@ -0,0 +1,428 @@ +package lisa.maths.settheory.functions + +import lisa.automation.kernel.CommonTactics.ExistenceAndUniqueness + +/** + * Classes/properties of functions. + * + * Describes properties of functions such as being injective ([[FunctionPoperties.injective]]), being invertible + * ([[FunctionPoperties.invertibleFunction]]), or being constant ([[FunctionProperties.constantFunction]]). + */ +object FunctionProperties extends lisa.Main { + import lisa.maths.settheory.SetTheory.* + import lisa.maths.Quantifiers.* + import lisa.maths.settheory.functions.Functionals.* + + // var defs + private val x = variable + private val y = variable + private val z = variable + private val t = variable + private val a = variable + private val b = variable + private val p = variable + private val A = variable + private val B = variable + + // relation and function symbols + private val r = variable + private val f = variable + private val g = variable + + private val P = predicate[1] + private val Q = predicate[1] + + /** + * Surjective (function) --- a function `f: x → y` is surjective iff it + * maps to every `b ∈ y` from atleast one `a ∈ x`. + * + * `surjective(f, x, y) = f ∈ x → y ∧ ∀ b ∈ y. (∃ a ∈ x. f(a) = b)` + */ + val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) + + /** + * Alias for [[surjective]] + */ + val onto = surjective + + /** + * Injective (function) --- a function `f: x → y` is injective iff it maps + * to every `b ∈ y` from atmost one `a ∈ x`. + * + * `injective(f, x, y) = f ∈ x → y ∧ ∀ b ∈ y. (∃ a ∈ x. f(a) = b) ⟹ (∃! a ∈ x. f(a) = b)` + */ + val injective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> (∃(a, in(a, x) /\ in(pair(a, b), f)) ==> ∃!(a, in(a, x) /\ in(pair(a, b), f)))) + + /** + * Alias for [[injective]] + */ + val oneone = injective + + /** + * Bijective function --- a function `f: x → y` is bijective iff it is + * [[injective]] and [[surjective]]. + */ + val bijective = DEF(f, x, y) --> injective(f, x, y) /\ surjective(f, x, y) + + /** + * Invertible Function --- a function from `x` to `y` is invertible iff it is + * [[bijective]]. See also, [[inverseFunction]] + */ + val invertibleFunction = DEF(f, x, y) --> bijective(f, x, y) + + /** + * Inverse Function --- the inverse of a function `f: x → y`, denoted + * `f^-1`, is a function from `y` to `x` such that `∀ a ∈ x, b ∈ y. + * f(f^-1(b)) = b ∧ f^-1(f(b)) = b`. + */ + val inverseFunctionOf = DEF(g, f, x, y) --> functionFrom(g, y, x) /\ functionFrom(f, x, y) /\ ∀(a, (in(a, y) ==> (a === app(f, app(g, a)))) /\ (in(a, x) ==> (a === app(g, app(f, a))))) + + // val inverseFunctionExistsIfInvertible = Theorem( + // invertibleFunction(f, x, y) <=> ∃(g, inverseFunctionOf(g, f, x, y)) + // ) { + // ??? + // } + + // val inverseFunctionIsUniqueIfItExists = Theorem( + // ∃(g, inverseFunctionOf(g, f, x, y)) |- ∃!(g, inverseFunctionOf(g, f, x, y)) + // ) { + // ??? + // } + + // val inverseFunctionUniqueness = Theorem( + // ∃!(g, invertibleFunction(f) ==> inverseFunctionOf(g, f, x, y)) + // ) { + // ??? + // } + + // val inverseFunction = DEF (f, x, y) --> The(g, invertibleFunction(f) ==> inverseFunctionOf(g, f, x, y))(inverseFunctionUniqueness) + + /** + * Theorem --- if a function is [[surjective]], its range is equal to its codomain. + */ + val surjectiveImpliesRangeIsCodomain = Theorem( + surjective(f, x, y) |- (y === functionRange(f)) + ) { + have(surjective(f, x, y) |- ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f)))) by Tautology.from(surjective.definition) + val surjDef = thenHave(surjective(f, x, y) |- in(b, y) ==> ∃(a, in(pair(a, b), f))) by InstantiateForall(b) + have(∀(t, in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f))))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) + val rangeDef = thenHave(in(b, functionRange(f)) <=> (∃(a, in(pair(a, b), f)))) by InstantiateForall(b) + + have(surjective(f, x, y) |- in(b, y) ==> in(b, functionRange(f))) by Tautology.from(surjDef, rangeDef) + thenHave(surjective(f, x, y) |- ∀(b, in(b, y) ==> in(b, functionRange(f)))) by RightForall + val surjsub = andThen(Substitution.applySubst(subsetAxiom of (x -> y, y -> functionRange(f)))) + + have((surjective(f, x, y), functionFrom(f, x, y)) |- subset(y, functionRange(f)) /\ subset(functionRange(f), y)) by RightAnd(surjsub, functionImpliesRangeSubsetOfCodomain) + val funceq = andThen(Substitution.applySubst(subsetEqualitySymmetry of (x -> y, y -> functionRange(f)))) + + val surjfunc = have(surjective(f, x, y) |- functionFrom(f, x, y)) by Tautology.from(surjective.definition) + + have(thesis) by Cut(surjfunc, funceq) + } + + /** + * Theorem --- Cantor's Theorem + * + * There is no [[surjective]] mapping ([[functionFrom]]) a set to its [[powerSet]]. + * + * In terms of cardinality, it asserts that a set is strictly smaller than + * its power set. + */ + val cantorTheorem = Theorem( + surjective(f, x, powerSet(x)) |- () + ) { + // define y = {z \in x | ! z \in f(z)} + val ydef = ∀(t, in(t, y) <=> (in(t, x) /\ !in(t, app(f, t)))) + + // y \subseteq x + // y \in P(x) + have(ydef |- ydef) by Hypothesis + thenHave(ydef |- in(t, y) <=> (in(t, x) /\ !in(t, app(f, t)))) by InstantiateForall(t) + thenHave(ydef |- in(t, y) ==> in(t, x)) by Weakening + thenHave(ydef |- ∀(t, in(t, y) ==> in(t, x))) by RightForall + andThen(Substitution.applySubst(subsetAxiom of (x -> y, y -> x))) + andThen(Substitution.applySubst(powerAxiom of (x -> y, y -> x))) + val yInPower = thenHave(ydef |- in(y, powerSet(x))) by Restate + + // y \in range(f) + have(surjective(f, x, powerSet(x)) |- (powerSet(x) === functionRange(f))) by Restate.from(surjectiveImpliesRangeIsCodomain of (y -> powerSet(x))) + andThen(Substitution.applySubst(extensionalityAxiom of (x -> powerSet(x), y -> functionRange(f)))) + val surjRange = thenHave(surjective(f, x, powerSet(x)) |- in(y, powerSet(x)) <=> in(y, functionRange(f))) by InstantiateForall(y) + val yInRange = have((ydef, surjective(f, x, powerSet(x))) |- in(y, functionRange(f))) by Tautology.from(yInPower, surjRange) + + // \exists z. z \in x /\ f(z) = y + val surjToFunFrom = have(surjective(f, x, powerSet(x)) |- functionFrom(f, x, powerSet(x))) by Tautology.from(surjective.definition of (y -> powerSet(x))) + val existsZdom = have((ydef, surjective(f, x, powerSet(x))) |- ∃(z, in(z, functionDomain(f)) /\ (app(f, z) === y))) by Tautology.from( + yInRange, + surjective.definition of (y -> powerSet(x)), + inRangeImpliesPullbackExists of (z -> y), + functionFromImpliesFunctional of (y -> powerSet(x)) + ) + val xeqdom = thenHave((ydef, surjective(f, x, powerSet(x)), (functionDomain(f) === x)) |- ∃(z, in(z, x) /\ (app(f, z) === y))) by RightSubstEq.withParametersSimple( + List((x, functionDomain(f))), + lambda(x, ∃(z, in(z, x) /\ (app(f, z) === y))) + ) + val existsZ = have((ydef, surjective(f, x, powerSet(x))) |- ∃(z, in(z, x) /\ (app(f, z) === y))) by Tautology.from( + surjective.definition of (y -> powerSet(x)), + functionFromImpliesDomainEq of (y -> powerSet(x)), + xeqdom + ) + + // z \in Y <=> z \in x /\ ! z \in f(z) + // y = f(z) so z \in f(z) <=> ! z \in f(z) + have(ydef |- ydef) by Hypothesis + thenHave(ydef |- in(z, y) <=> (in(z, x) /\ !in(z, app(f, z)))) by InstantiateForall(z) + thenHave((ydef, in(z, x), (app(f, z) === y)) |- in(z, y) <=> (in(z, x) /\ !in(z, app(f, z)))) by Weakening + thenHave((ydef, in(z, x), (app(f, z) === y)) |- in(z, app(f, z)) <=> (in(z, x) /\ !in(z, app(f, z)))) by RightSubstEq.withParametersSimple( + List((y, app(f, z))), + lambda(y, in(z, y) <=> (in(z, x) /\ !in(z, app(f, z)))) + ) + thenHave((ydef, in(z, x) /\ (app(f, z) === y)) |- ()) by Tautology + val existsToContra = thenHave((ydef, ∃(z, in(z, x) /\ (app(f, z) === y))) |- ()) by LeftExists + + have((ydef, surjective(f, x, powerSet(x))) |- ()) by Cut(existsZ, existsToContra) + val yToContra = thenHave((∃(y, ydef), surjective(f, x, powerSet(x))) |- ()) by LeftExists + val yexists = have(∃(y, ydef)) by Restate.from(comprehensionSchema of (z -> x, φ -> lambda(t, !in(t, app(f, t))))) + + have(thesis) by Cut(yexists, yToContra) + } + + /** + * Constant function --- for every element in its domain, the value is the same. + */ + val constantFunction = DEF(x, t) --> cartesianProduct(x, singleton(t)) + + /** + * Theorem --- the value of a constant function is the same for all elements in its domain. + * + * `a ∈ x |- app(constantFunction(x, t), a) = t` + */ + val constantFunctionApplication = Theorem( + in(a, x) |- app(constantFunction(x, t), a) === t + ) { + assume(in(a, x)) + have(functionFrom(constantFunction(x, t), x, singleton(t))) by Weakening(constantFunctionFunctionFrom) + + have(in(app(constantFunction(x, t), a), singleton(t))) by Tautology.from( + functionFromApplication of (f := constantFunction(x, t), y := singleton(t)), + lastStep + ) + + have(thesis) by Tautology.from( + singletonHasNoExtraElements of (y := app(constantFunction(x, t), a), x := t), + lastStep + ) + } + + /** + * Theorem --- the domain of a constant function is the set it is defined on. + * + * `dom(constantFunction(x, t)) = x` + */ + val constantFunctionDomain = Theorem( + functionDomain(constantFunction(x, t)) === x + ) { + // since we define constant function using the cartesian product, this requires a bit more effort + val constFunDef = have((constantFunction(x, t) === cartesianProduct(x, singleton(t)))) by Weakening(constantFunction.definition of constantFunction(x, t)) + + have(∀(p, in(p, functionDomain(constantFunction(x, t))) <=> ∃(a, in(pair(p, a), constantFunction(x, t))))) by InstantiateForall(functionDomain(constantFunction(x, t)))( + functionDomain.definition of (r := constantFunction(x, t)) + ) + val domainDef = thenHave(in(p, functionDomain(constantFunction(x, t))) <=> ∃(a, in(pair(p, a), constantFunction(x, t)))) by InstantiateForall(p) + + val rhs = have(∃(a, in(pair(p, a), constantFunction(x, t))) ==> in(p, x)) subproof { + val assumption = assume(∃(a, in(pair(p, a), constantFunction(x, t)))) + val aw = witness(assumption) + have(in(pair(p, aw), constantFunction(x, t))) by Restate + thenHave(in(pair(p, aw), cartesianProduct(x, singleton(t)))) by Substitution.ApplyRules(constFunDef) + + have(thesis) by Tautology.from(lastStep, pairInCartesianProduct of (a := p, b := aw, y := singleton(t))) + } + + val lhs = have(in(p, x) ==> ∃(a, in(pair(p, a), constantFunction(x, t)))) subproof { + assume(in(p, x)) + val tIn = have(in(t, singleton(t))) by Tautology.from(singletonHasNoExtraElements of (y := t, x := t)) + + have(in(pair(p, t), cartesianProduct(x, singleton(t)))) by Tautology.from( + pairInCartesianProduct of (a := p, b := t, y := singleton(t)), + tIn + ) + thenHave(∃(a, in(pair(p, a), cartesianProduct(x, singleton(t))))) by RightExists + thenHave(∃(a, in(pair(p, a), constantFunction(x, t)))) by Substitution.ApplyRules(constFunDef) + } + + have(in(p, x) <=> in(p, functionDomain(constantFunction(x, t)))) by Tautology.from(domainDef, rhs, lhs) + val ext = thenHave(∀(p, in(p, x) <=> in(p, functionDomain(constantFunction(x, t))))) by RightForall + + have(thesis) by Tautology.from(ext, extensionalityAxiom of (y := functionDomain(constantFunction(x, t)))) + } + + /** + * Theorem --- a constant function is functional. + */ + val constantFunctionIsFunctional = Theorem( + functional(constantFunction(x, t)) + ) { + val constFunDef = have((constantFunction(x, t) === cartesianProduct(x, singleton(t)))) by Weakening(constantFunction.definition of constantFunction(x, t)) + + val isRelation = have(relation(constantFunction(x, t))) subproof { + have(relation(cartesianProduct(x, singleton(t)))) by Weakening(cartesianProductIsRelation of (y := singleton(t))) + thenHave(thesis) by Substitution.ApplyRules(constFunDef) + } + + val uniqueY = have(∀(a, ∃(y, in(pair(a, y), constantFunction(x, t))) ==> ∃!(y, in(pair(a, y), constantFunction(x, t))))) subproof { + have(∃(y, in(pair(a, y), constantFunction(x, t))) ==> ∃!(y, in(pair(a, y), constantFunction(x, t)))) subproof { + val existence = assume(∃(y, in(pair(a, y), constantFunction(x, t)))) + + val uniqueness = have((in(pair(a, y), constantFunction(x, t)), in(pair(a, p), constantFunction(x, t))) |- (y === p)) subproof { + val assumption1 = assume(in(pair(a, y), constantFunction(x, t))) + val assumption2 = assume(in(pair(a, p), constantFunction(x, t))) + + have(in(pair(a, y), cartesianProduct(x, singleton(t)))) by Substitution.ApplyRules(constFunDef)(assumption1) + val eq1 = have(y === t) by Tautology.from( + pairInCartesianProduct of (b := y, y := singleton(t)), + lastStep, + singletonHasNoExtraElements of (x := t) + ) + + have(in(pair(a, p), cartesianProduct(x, singleton(t)))) by Substitution.ApplyRules(constFunDef)(assumption2) + val eq2 = have(p === t) by Tautology.from( + pairInCartesianProduct of (b := p, y := singleton(t)), + lastStep, + singletonHasNoExtraElements of (x := t, y := p) + ) + + have(y === p) by Tautology.from(eq1, eq2, equalityTransitivity of (x := y, y := t, z := p)) + } + + have(∃!(y, in(pair(a, y), constantFunction(x, t)))) by ExistenceAndUniqueness(in(pair(a, y), constantFunction(x, t)))(existence, uniqueness) + } + thenHave(thesis) by RightForall + } + + have(thesis) by Tautology.from(isRelation, uniqueY, functional.definition of (f := constantFunction(x, t))) + } + + /** + * Theorem --- a constant function is a function from `x` to the singleton of `t`. + * + * `constantFunction(x, t) ∈ x → {t}` + */ + val constantFunctionFunctionFrom = Theorem( + functionFrom(constantFunction(x, t), x, singleton(t)) + ) { + val constFunDef = have((constantFunction(x, t) === cartesianProduct(x, singleton(t)))) by Weakening(constantFunction.definition of constantFunction(x, t)) + + have(∀(a, in(a, setOfFunctions(x, singleton(t))) <=> (in(a, powerSet(cartesianProduct(x, singleton(t)))) /\ functionalOver(a, x)))) by InstantiateForall(setOfFunctions(x, singleton(t)))( + setOfFunctions.definition of (y := singleton(t)) + ) + val setOfFunctionsDef = thenHave( + in(constantFunction(x, t), setOfFunctions(x, singleton(t))) <=> (in(constantFunction(x, t), powerSet(cartesianProduct(x, singleton(t)))) /\ functionalOver(constantFunction(x, t), x)) + ) by InstantiateForall(constantFunction(x, t)) + + have(in(cartesianProduct(x, singleton(t)), powerSet(cartesianProduct(x, singleton(t))))) by Weakening(elemInItsPowerSet of (x := cartesianProduct(x, singleton(t)))) + val inPowerSet = thenHave(in(constantFunction(x, t), powerSet(cartesianProduct(x, singleton(t))))) by Substitution.ApplyRules(constFunDef) + + val funcOver = have(functionalOver(constantFunction(x, t), x)) by Tautology.from( + constantFunctionIsFunctional, + constantFunctionDomain, + functionalOver.definition of (f := constantFunction(x, t)) + ) + + have(thesis) by Tautology.from( + inPowerSet, + funcOver, + setOfFunctionsDef, + functionFrom.definition of (f := constantFunction(x, t), y := singleton(t)) + ) + } + + /** + * Theorem --- Sigma with a constant function is the cartesian product + * + * `Σ(A, constantFunction(A, t)) = A × t` + */ + val sigmaIsCartesianProductWhenBIsConstant = Theorem( + Sigma(A, constantFunction(A, t)) === cartesianProduct(A, t) + ) { + have(∀(p, in(p, Sigma(A, constantFunction(A, t))) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))))))) by InstantiateForall( + Sigma(A, constantFunction(A, t)) + )( + Sigma.definition of (B -> constantFunction(A, t)) + ) + val sigmaDef = thenHave(in(p, Sigma(A, constantFunction(A, t))) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))))) by InstantiateForall(p) + + have((in(a, A) /\ in(b, app(constantFunction(A, t), a))) <=> (in(a, A) /\ in(b, t))) subproof { + val constApp = have(in(a, A) <=> (in(a, A) /\ (app(constantFunction(A, t), a) === t))) by Tautology.from( + constantFunctionApplication of (x := A) + ) + + val lhs = have(in(a, A) /\ in(b, app(constantFunction(A, t), a)) |- (in(a, A) /\ in(b, t))) subproof { + val inA = assume(in(a, A)) + val subst = have(app(constantFunction(A, t), a) === t) by Tautology.from(constApp, inA) + + assume(in(b, app(constantFunction(A, t), a))) + thenHave(in(a, A) /\ in(b, app(constantFunction(A, t), a))) by Tautology + thenHave(thesis) by Substitution.ApplyRules(subst) + } + + val rhs = have(in(a, A) /\ in(b, t) |- (in(a, A) /\ in(b, app(constantFunction(A, t), a)))) subproof { + val inA = assume(in(a, A)) + val subst = have(app(constantFunction(A, t), a) === t) by Tautology.from(constApp, inA) + + assume(in(b, t)) + thenHave(in(a, A) /\ in(b, t)) by Tautology + thenHave(thesis) by Substitution.ApplyRules(subst) + } + + have(thesis) by Tautology.from(lhs, rhs) + } + have(((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))) <=> ((p === pair(a, b)) /\ in(a, A) /\ in(b, t))) by Tautology.from(lastStep) + thenHave(∀(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))) <=> ((p === pair(a, b)) /\ in(a, A) /\ in(b, t)))) by RightForall + have(∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))) <=> ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, t)))) by Cut( + lastStep, + existentialEquivalenceDistribution of (P := lambda(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))), Q := lambda(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, t))) + ) + thenHave(∀(a, ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))) <=> ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, t))))) by RightForall + val constApp = have(∃(a, ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))))) <=> ∃(a, ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, t))))) by Cut( + lastStep, + existentialEquivalenceDistribution of (P := lambda(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))), Q := lambda( + a, + ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, t)) + )) + ) + val simplSigmaDef = have(in(p, Sigma(A, constantFunction(A, t))) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, t))))) by Substitution.ApplyRules(constApp)(sigmaDef) + + have(in(p, Sigma(A, constantFunction(A, t))) <=> in(p, cartesianProduct(A, t))) by Tautology.from(simplSigmaDef, elemOfCartesianProduct of (x := A, y := t, t := p)) + val ext = thenHave(∀(p, in(p, Sigma(A, constantFunction(A, t))) <=> in(p, cartesianProduct(A, t)))) by RightForall + + have(thesis) by Tautology.from(ext, extensionalityAxiom of (x := Sigma(A, constantFunction(A, t)), y := cartesianProduct(A, t))) + } + + /** + * Theorem --- Pi with a constant function is the set of functions. + * + * `Π(A, constantFunction(A, t)) = A |=> t` + */ + val piIsSetOfFunctionsWhenBIsConstant = Theorem( + Pi(A, constantFunction(A, t)) === (A |=> t) + ) { + have(∀(g, in(g, setOfFunctions(A, t)) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A)))) by InstantiateForall(setOfFunctions(A, t))( + setOfFunctions.definition of (x -> A, y -> t) + ) + val setOfFunctionsDef = thenHave(in(g, setOfFunctions(A, t)) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A))) by InstantiateForall(g) + + have(∀(g, in(g, Pi(A, constantFunction(A, t))) <=> (in(g, powerSet(Sigma(A, constantFunction(A, t)))) /\ functionalOver(g, A)))) by InstantiateForall(Pi(A, constantFunction(A, t)))( + Pi.definition of (x -> A, f -> constantFunction(A, t)) + ) + thenHave(∀(g, in(g, Pi(A, constantFunction(A, t))) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A)))) by Substitution.ApplyRules(sigmaIsCartesianProductWhenBIsConstant) + thenHave(in(g, Pi(A, constantFunction(A, t))) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A))) by InstantiateForall(g) + thenHave(in(g, Pi(A, constantFunction(A, t))) <=> in(g, setOfFunctions(A, t))) by Substitution.ApplyRules(setOfFunctionsDef) + val ext = thenHave(∀(g, in(g, Pi(A, constantFunction(A, t))) <=> in(g, setOfFunctions(A, t)))) by RightForall + + have(thesis) by Tautology.from( + ext, + extensionalityAxiom of (x := Pi(A, constantFunction(A, t)), y := setOfFunctions(A, t)) + ) + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala new file mode 100644 index 000000000..71efaef16 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala @@ -0,0 +1,1365 @@ +package lisa.maths.settheory.functions +import lisa.automation.kernel.CommonTactics.Definition +import lisa.automation.settheory.SetTheoryTactics._ +import lisa.maths.Quantifiers._ +import lisa.maths.settheory.SetTheory._ + +/** + * Functional sets + * + * Defines functionals, their application, and spaces of functions. + */ +object Functionals extends lisa.Main { + // var defs + private val w = variable + private val x = variable + private val y = variable + private val z = variable + private val p = variable + private val h = formulaVariable + private val t = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + private val A = variable + private val B = variable + + // relation and function symbols + private val r = variable + private val f = variable + private val g = variable + + private val P = predicate[1] + private val Q = predicate[1] + + /** + * Functional --- A binary [[relation]] is functional if it maps every element in its domain + * to a unique element (in its range). + * + * `functional(f) ⇔ relation(f) ∧ ∀ x. (∃ y. (x, y) ∈ f) ⟹ (∃! y. (x, y) ∈ f)` + * + * We may alternatively denote `(z, y) ∈ f` as `y = f(z)`. + * + * @param f relation (set) + */ + val functional = DEF(f) --> relation(f) /\ ∀(x, ∃(y, in(pair(x, y), f)) ==> ∃!(y, in(pair(x, y), f))) + + /** + * Alias for [[relationDomain]]. + */ + val functionDomain = relationDomain + + /** + * Alias for [[relationRange]]. + */ + val functionRange = relationRange + + /** + * Functional Over a Set --- A binary [[relation]] is functional over a set `x` if it is + * [[functional]] and has`x` as its domain ([[functionDomain]]). + * + * @param f relation (set) + * @param x set + */ + val functionalOver = DEF(f, x) --> functional(f) /\ (functionDomain(f) === x) + + /** + * Theorem --- The empty set is a functional relation. + * + * `functional(∅)` + */ + val emptySetFunctional = Theorem( + functional(∅) + ) { + val isRelation = have(relation(∅)) subproof { + // ∅ ⊆ A x B, so it must be a (empty) relation + have(relationBetween(∅, a, b)) by Tautology.from(emptySetIsASubset of (x := cartesianProduct(a, b)), relationBetween.definition of (r := ∅)) + thenHave(∃(b, relationBetween(∅, a, b))) by RightExists + val relationSpec = thenHave(∃(a, ∃(b, relationBetween(∅, a, b)))) by RightExists + + have(thesis) by Tautology.from(relationSpec, relation.definition of (r := ∅)) + } + + val uniqueY = have(∀(x, ∃(y, in(pair(x, y), ∅)) ==> ∃!(y, in(pair(x, y), ∅)))) subproof { + // contradiction directly from the emptySetAxiom: there is nothing in the empty set + have(in(pair(x, y), ∅) |- ∃!(y, in(pair(x, y), ∅))) by Tautology.from(emptySetAxiom of (x := pair(x, y))) + thenHave(∃(y, in(pair(x, y), ∅)) |- ∃!(y, in(pair(x, y), ∅))) by LeftExists + thenHave(∃(y, in(pair(x, y), ∅)) ==> ∃!(y, in(pair(x, y), ∅))) by Restate + thenHave(thesis) by RightForall + } + + have(thesis) by Tautology.from(isRelation, uniqueY, functional.definition of (f := ∅)) + } + + /** + * Lemma --- If `f` is a function, then `t ∈ f` implies `t = (x, y)` such that `x ∈ functionDomain(f)`. + */ + val functionalMembership = Lemma( + functional(f) |- ∀(t, in(t, f) ==> ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) + ) { + assume(functional(f)) + + have((functional(f), in(t, f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) subproof { + val isRelation = have(relation(f)) by Tautology.from(functional.definition) + + // Use the definitions + have(relationBetween(f, functionDomain(f), functionRange(f)) |- ∀(x, in(x, f) ==> in(x, cartesianProduct(functionDomain(f), functionRange(f))))) by Tautology.from( + relationBetween.definition of (r -> f, a -> functionDomain(f), b -> functionRange(f)), + subset.definition of (x -> f, y -> cartesianProduct(functionDomain(f), functionRange(f))) + ) + thenHave(relationBetween(f, functionDomain(f), functionRange(f)) |- in(t, f) ==> in(t, cartesianProduct(functionDomain(f), functionRange(f)))) by InstantiateForall(t) + thenHave((relationBetween(f, functionDomain(f), functionRange(f)), in(t, f)) |- in(t, cartesianProduct(functionDomain(f), functionRange(f)))) by Restate + + val almostThere = + have((relationBetween(f, functionDomain(f), functionRange(f)), in(t, f)) |- ∃(x, ∃(y, (t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f))))) by Tautology.from( + lastStep, + elemOfCartesianProduct of (x -> functionDomain(f), y -> functionRange(f)) + ) + + // Remove the extraneous term in the conjunction + have((t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)) |- in(x, functionDomain(f)) /\ (t === pair(x, y))) by Tautology + thenHave((t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)) |- ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y)))) by RightExists + thenHave((t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by RightExists + thenHave(∃(y, (t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f))) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by LeftExists + thenHave(∃(x, ∃(y, (t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)))) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by LeftExists + + have((relationBetween(f, functionDomain(f), functionRange(f)), in(t, f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Cut(almostThere, lastStep) + have((relation(f), in(t, f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Cut(relationImpliesRelationBetweenDomainAndRange of (r -> f), lastStep) + have(in(t, f) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Cut(isRelation, lastStep) + } + thenHave(in(t, f) ==> ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Restate + thenHave(thesis) by RightForall + } + + /** + * Theorem --- a function cannot have two pairs representing different values + * for a given element. + * + * `functional(f) /\ (x, y) \in f /\ (x, z) \in f /\ !(y = z) |- \bot` + */ + val violatingPairInFunction = Theorem( + functional(f) /\ in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z) |- () + ) { + assume(functional(f), in(pair(x, y), f), in(pair(x, z), f), !(y === z)) + + have(forall(x, exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f)))) by Tautology.from(functional.definition) + val exExOne = thenHave(exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f))) by InstantiateForall(x) + + have(in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z)) by Restate + thenHave(exists(z, in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z))) by RightExists + thenHave(exists(y, exists(z, in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z)))) by RightExists + + have(thesis) by Tautology.from(lastStep, exExOne, atleastTwoExist of (P -> lambda(y, in(pair(x, y), f)))) + } + + /** + * Theorem --- a set containing a single pair is a function. + * + * Given `s = {(x, y)}`, + * + * `functional(s) /\ dom(s) = {x} /\ ran(s) = {y}` + */ + val pairSingletonIsFunctional = Theorem( + { + val s = singleton(pair(x, y)) + functional(s) /\ (functionDomain(s) === singleton(x)) /\ (functionRange(s) === singleton(y)) + } + ) { + val s = singleton(pair(x, y)) + + val elemOfS = have(in(z, s) <=> (z === pair(x, y))) by Restate.from(singletonHasNoExtraElements of (y -> z, x -> pair(x, y))) + + // (x, y) in {x} * {y} + val xyInCart = have(in(pair(x, y), cartesianProduct(singleton(x), singleton(y)))) subproof { + have((pair(x, y) === pair(x, y)) /\ in(x, singleton(x)) /\ in(y, singleton(y))) by Tautology.from(singletonHasNoExtraElements of (y -> x), singletonHasNoExtraElements of (x -> y)) + thenHave(exists(b, (pair(x, y) === pair(x, b)) /\ in(x, singleton(x)) /\ in(b, singleton(y)))) by RightExists + thenHave(exists(a, exists(b, (pair(x, y) === pair(a, b)) /\ in(a, singleton(x)) /\ in(b, singleton(y))))) by RightExists + have(thesis) by Tautology.from(lastStep, elemOfCartesianProduct of (t -> pair(x, y), x -> singleton(x), y -> singleton(y))) + } + + val relS = have(relation(s)) subproof { + have((z === pair(x, y)) |- in(z, cartesianProduct(singleton(x), singleton(y)))) by Substitution.ApplyRules(z === pair(x, y))(xyInCart) + have(in(z, s) ==> in(z, cartesianProduct(singleton(x), singleton(y)))) by Tautology.from(lastStep, elemOfS) + thenHave(forall(z, in(z, s) ==> in(z, cartesianProduct(singleton(x), singleton(y))))) by RightForall + have(relationBetween(s, singleton(x), singleton(y))) by Tautology.from( + lastStep, + subsetAxiom of (x -> s, y -> cartesianProduct(singleton(x), singleton(y))), + relationBetween.definition of (r -> s, a -> singleton(x), b -> singleton(y)) + ) + thenHave(exists(b, relationBetween(s, singleton(x), b))) by RightExists + thenHave(exists(a, exists(b, relationBetween(s, a, b)))) by RightExists + have(thesis) by Tautology.from(lastStep, relation.definition of r -> s) + } + + val uniq = have(forall(a, exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s)))) subproof { + have((pair(a, z) === pair(x, y)) <=> in(pair(a, z), s)) by Restate.from(elemOfS of z -> pair(a, z)) + val eq = thenHave(((a === x) /\ (z === y)) <=> in(pair(a, z), s)) by Substitution.ApplyRules(pairExtensionality) + thenHave((a === x) |- (z === y) <=> in(pair(a, z), s)) by Tautology + thenHave((a === x) |- forall(z, (z === y) <=> in(pair(a, z), s))) by RightForall + thenHave((a === x) |- exists(b, forall(z, (z === b) <=> in(pair(a, z), s)))) by RightExists + thenHave((a === x) |- existsOne(b, in(pair(a, b), s))) by RightExistsOne + val pos = thenHave((a === x) |- exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s))) by Weakening + + val ax = have(in(pair(a, z), s) |- (a === x)) by Weakening(eq) + thenHave(!(a === x) |- !in(pair(a, z), s)) by Restate + thenHave(!(a === x) |- forall(z, !in(pair(a, z), s))) by RightForall + thenHave(!(a === x) |- !exists(z, in(pair(a, z), s))) by Restate + val neg = thenHave(!(a === x) |- exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s))) by Weakening + + have(exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s))) by Tautology.from(pos, neg) + thenHave(thesis) by RightForall + } + + val dom = have(functionDomain(s) === singleton(x)) subproof { + have(forall(t, in(t, functionDomain(s)) <=> exists(a, in(pair(t, a), s)))) by InstantiateForall(functionDomain(s))(functionDomain.definition of r -> s) + val inDom = thenHave(in(t, functionDomain(s)) <=> exists(a, in(pair(t, a), s))) by InstantiateForall(t) + + have(in(pair(t, a), s) <=> ((t === x) /\ (a === y))) by Substitution.ApplyRules(pairExtensionality)(elemOfS of z -> pair(t, a)) + thenHave(forall(a, in(pair(t, a), s) <=> ((t === x) /\ (a === y)))) by RightForall + val exToEq = have(exists(a, in(pair(t, a), s)) <=> exists(a, ((t === x) /\ (a === y)))) by Cut( + lastStep, + existentialEquivalenceDistribution of (P -> lambda(a, in(pair(t, a), s)), Q -> lambda(a, ((t === x) /\ (a === y)))) + ) + + val exRedundant = have(exists(a, ((t === x) /\ (a === y))) <=> (t === x)) subproof { + have((t === x) /\ (a === y) |- (t === x)) by Restate + val fwd = thenHave(exists(a, (t === x) /\ (a === y)) |- (t === x)) by LeftExists + + have((t === x) |- (t === x) /\ (y === y)) by Restate + val bwd = thenHave((t === x) |- exists(a, (t === x) /\ (a === y))) by RightExists + + have(thesis) by Tautology.from(fwd, bwd) + } + + have((t === x) <=> in(t, singleton(x))) by Restate.from(singletonHasNoExtraElements of y -> t) + have(in(t, functionDomain(s)) <=> in(t, singleton(x))) by Tautology.from(lastStep, exRedundant, exToEq, inDom) + thenHave(forall(t, in(t, functionDomain(s)) <=> in(t, singleton(x)))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> functionDomain(s), y -> singleton(x))) + } + + val ran = have(functionRange(s) === singleton(y)) subproof { + have(forall(t, in(t, functionRange(s)) <=> exists(a, in(pair(a, t), s)))) by InstantiateForall(functionRange(s))(functionRange.definition of r -> s) + val inDom = thenHave(in(t, functionRange(s)) <=> exists(a, in(pair(a, t), s))) by InstantiateForall(t) + + have(in(pair(a, t), s) <=> ((a === x) /\ (t === y))) by Substitution.ApplyRules(pairExtensionality)(elemOfS of z -> pair(a, t)) + thenHave(forall(a, in(pair(a, t), s) <=> ((a === x) /\ (t === y)))) by RightForall + val exToEq = have(exists(a, in(pair(a, t), s)) <=> exists(a, ((a === x) /\ (t === y)))) by Cut( + lastStep, + existentialEquivalenceDistribution of (P -> lambda(a, in(pair(a, t), s)), Q -> lambda(a, ((a === x) /\ (t === y)))) + ) + + val exRedundant = have(exists(a, ((a === x) /\ (t === y))) <=> (t === y)) subproof { + have((a === x) /\ (t === y) |- (t === y)) by Restate + val fwd = thenHave(exists(a, (a === x) /\ (t === y)) |- (t === y)) by LeftExists + + have((t === y) |- (x === x) /\ (t === y)) by Restate + val bwd = thenHave((t === y) |- exists(a, (a === x) /\ (t === y))) by RightExists + + have(thesis) by Tautology.from(fwd, bwd) + } + + have((t === y) <=> in(t, singleton(y))) by Restate.from(singletonHasNoExtraElements of (y -> t, x -> y)) + have(in(t, functionRange(s)) <=> in(t, singleton(y))) by Tautology.from(lastStep, exRedundant, exToEq, inDom) + thenHave(forall(t, in(t, functionRange(s)) <=> in(t, singleton(y)))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> functionRange(s), y -> singleton(y))) + } + + have(functional(s)) by Tautology.from(relS, uniq, functional.definition of f -> s) + + have(thesis) by Tautology.from(ran, dom, lastStep) + + } + + val setOfFunctionsUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) + ) { + have(thesis) by UniqueComprehension(powerSet(cartesianProduct(x, y)), lambda(t, functionalOver(t, x))) + } + + /** + * Set of functions --- All functions from `x` to `y`, denoted `x → y` or + * `→(x, y)`. + * + * Since functions from `x` to `y` contain pairs of the form `(a, b) | a ∈ + * x, b ∈ y`, it is a filtering on the power set of their product, i.e. `x + * → y ⊆ PP(x * y)`. + */ + val setOfFunctions = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x))))(setOfFunctionsUniqueness) + val |=> = setOfFunctions + + extension (x: Term) { + // Infix notation for a set of functions: x |=> y + def |=>(y: Term): Term = setOfFunctions(x, y) + } + + // class SetOfFunctions(val x: Term, val y: Term) extends AppliedFunctional(setOfFunctions, Seq(x, y)) with LisaObject[SetOfFunctions] { + // override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): SetOfFunctions = SetOfFunctions(x.substituteUnsafe(map), y.substituteUnsafe(map)) + + // override def toString(): String = x.toStringSeparated() + " |=> " + y.toStringSeparated() + // override def toStringSeparated(): String = toString() + // } + // object SetOfFunctions { + // def unapply(sof: SetOfFunctions): Option[(Term, Term)] = sof match + // case AppliedFunctional(label, Seq(x, y)) if label == setOfFunctions => Some((x, y)) + // case _ => None + // } + // extension (x: Term) { + // // Infix notation for a set of functions: x |=> y + // def |=>(y: Term): Term = SetOfFunctions(x, y) + // } + + /** + * Function From (x to y) --- denoted `f ∈ x → y` or `f: x → y`. + */ + val functionFrom = DEF(f, x, y) --> in(f, x |=> y) + + /** + * Theorem --- A function between two sets is [[functional]] + */ + val functionFromImpliesFunctional = Theorem( + functionFrom(f, x, y) |- functional(f) + ) { + have(∀(t, in(t, x |=> y) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) by InstantiateForall(x |=> y)(setOfFunctions.definition) + val funSetDef = thenHave(in(f, x |=> y) <=> (in(f, powerSet(cartesianProduct(x, y))) /\ functionalOver(f, x))) by InstantiateForall(f) + + val funOver = have(functionFrom(f, x, y) |- functional(f)) by Tautology.from(funSetDef, functionFrom.definition, functionalOver.definition) + } + + /** + * Theorem --- if `f` is a [[functionFrom]] `x` to `y`, i.e. `f ∈ x → y`, + * then `x` is precisely its domain as a relation. + */ + val functionFromImpliesDomainEq = Theorem( + functionFrom(f, x, y) |- (functionDomain(f) === x) + ) { + have(∀(t, in(t, x |=> y) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) by InstantiateForall(x |=> y)(setOfFunctions.definition) + val funSetDef = thenHave(in(f, x |=> y) <=> (in(f, powerSet(cartesianProduct(x, y))) /\ functionalOver(f, x))) by InstantiateForall(f) + + have(thesis) by Tautology.from(functionFrom.definition, funSetDef, functionalOver.definition) + } + + /** + * Theorem --- the range of a function is a subset of its codomain. + */ + val functionImpliesRangeSubsetOfCodomain = Theorem( + functionFrom(f, x, y) |- subset(functionRange(f), y) + ) { + have(∀(t, in(t, x |=> y) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) by InstantiateForall(x |=> y)(setOfFunctions.definition) + val funSetDef = thenHave(in(f, x |=> y) <=> (in(f, powerSet(cartesianProduct(x, y))) /\ functionalOver(f, x))) by InstantiateForall(f) + + have(functionFrom(f, x, y) |- ∀(z, in(z, f) ==> in(z, cartesianProduct(x, y)))) by Tautology.from( + functionFrom.definition, + funSetDef, + powerAxiom of (x -> f, y -> cartesianProduct(x, y)), + subsetAxiom of (x -> f, y -> cartesianProduct(x, y)) + ) + thenHave(functionFrom(f, x, y) |- in(pair(a, t), f) ==> in(pair(a, t), cartesianProduct(x, y))) by InstantiateForall(pair(a, t)) + thenHave((functionFrom(f, x, y), in(pair(a, t), f)) |- in(pair(a, t), cartesianProduct(x, y))) by Restate + andThen(Substitution.applySubst(pairInCartesianProduct of (b -> t))) + thenHave((functionFrom(f, x, y), in(pair(a, t), f)) |- in(t, y)) by Weakening + val funFromty = thenHave((functionFrom(f, x, y), ∃(a, in(pair(a, t), f))) |- in(t, y)) by LeftExists + + have(∀(t, in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f))))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) + thenHave(in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f)))) by InstantiateForall(t) + val ranat = thenHave(in(t, functionRange(f)) |- ∃(a, in(pair(a, t), f))) by Weakening + + have((functionFrom(f, x, y), in(t, functionRange(f))) |- in(t, y)) by Cut(ranat, funFromty) + thenHave((functionFrom(f, x, y)) |- in(t, functionRange(f)) ==> in(t, y)) by Restate + thenHave((functionFrom(f, x, y)) |- ∀(t, in(t, functionRange(f)) ==> in(t, y))) by RightForall + andThen(Substitution.applySubst(subsetAxiom of (x -> functionRange(f)))) + } + + val functionApplicationUniqueness = Theorem( + ∃!(z, ((functional(f) /\ in(x, functionDomain(f))) ==> in(pair(x, z), f)) /\ ((!functional(f) \/ !in(x, functionDomain(f))) ==> (z === ∅))) + ) { + val prem = functional(f) /\ in(x, functionDomain(f)) + + // we prove thesis by two cases, first by assuming prem, and then by assuming !prem + + // positive direction + have(functional(f) |- ∀(x, ∃(y, in(pair(x, y), f)) ==> ∃!(y, in(pair(x, y), f)))) by Tautology.from(functional.definition) + val funcDef = thenHave(functional(f) |- ∃(y, in(pair(x, y), f)) ==> ∃!(y, in(pair(x, y), f))) by InstantiateForall(x) + + have((functionDomain(f) === functionDomain(f)) <=> ∀(t, in(t, functionDomain(f)) <=> (∃(y, in(pair(t, y), f))))) by InstantiateForall(functionDomain(f))( + functionDomain.definition of (r -> f) + ) + thenHave(∀(t, in(t, functionDomain(f)) <=> (∃(y, in(pair(t, y), f))))) by Restate + thenHave(in(x, functionDomain(f)) <=> (∃(y, in(pair(x, y), f)))) by InstantiateForall(x) + val domDef = thenHave(in(x, functionDomain(f)) |- ∃(y, in(pair(x, y), f))) by Weakening + + val uniqPrem = have(functional(f) /\ in(x, functionDomain(f)) |- ∃!(z, in(pair(x, z), f))) by Tautology.from(funcDef, domDef) + + val positive = have(prem |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { + val lhs = have(prem /\ ((z === y) <=> in(pair(x, y), f)) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ ⊤))) subproof { + val iff = have(prem |- (in(pair(x, y), f)) <=> (prem ==> in(pair(x, y), f))) by Restate + have(prem /\ ((z === y) <=> in(pair(x, y), f)) |- ((z === y) <=> in(pair(x, y), f))) by Restate + val subst = thenHave((prem /\ ((z === y) <=> in(pair(x, y), f)), (in(pair(x, y), f)) <=> (prem ==> in(pair(x, y), f))) |- ((z === y) <=> (prem ==> in(pair(x, y), f)))) by RightSubstIff + .withParametersSimple( + List(((in(pair(x, y), f)), (prem ==> in(pair(x, y), f)))), + lambda(h, ((z === y) <=> h)) + ) + + have((prem /\ ((z === y) <=> in(pair(x, y), f)), prem) |- ((z === y) <=> (prem ==> in(pair(x, y), f)))) by Cut(iff, subst) + thenHave(thesis) by Restate + } + + val topIntro = have((prem, ((z === y) <=> in(pair(x, y), f))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) subproof { + val topIff = have(prem |- (!prem ==> (y === ∅)) <=> ⊤) by Restate + val topSubst = have( + (prem /\ ((z === y) <=> in(pair(x, y), f)), ((!prem ==> (y === ∅)) <=> ⊤)) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))) + ) by RightSubstIff.withParametersSimple(List(((!prem ==> (y === ∅)), ⊤)), lambda(h, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ h))))(lhs) + + have((prem /\ ((z === y) <=> in(pair(x, y), f)), prem) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Cut(topIff, topSubst) + thenHave((prem, ((z === y) <=> in(pair(x, y), f))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Restate + } + + val quantification = have((prem, ∃!(z, in(pair(x, z), f))) |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { + have((prem, ∀(y, ((z === y) <=> in(pair(x, y), f)))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by LeftForall(topIntro) + thenHave((prem, ∀(y, ((z === y) <=> in(pair(x, y), f)))) |- ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))))) by RightForall + thenHave((prem, ∀(y, ((z === y) <=> in(pair(x, y), f)))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))))) by RightExists + thenHave( + (prem, ∃(z, ∀(y, ((z === y) <=> in(pair(x, y), f))))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))))) + ) by LeftExists + thenHave(thesis) by Restate + } + + have(thesis) by Cut(uniqPrem, quantification) + } + + // negative + have((∅ === y) <=> (∅ === y)) by Restate + thenHave(∀(y, (∅ === y) <=> (∅ === y))) by RightForall + thenHave(∃(z, ∀(y, (z === y) <=> (∅ === y)))) by RightExists + val emptyPrem = thenHave(∃!(z, (z === ∅))) by Restate + + val negative = have(!prem |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { + val lhs = have(!prem /\ ((z === y) <=> (y === ∅)) |- ((z === y) <=> ((!prem ==> (y === ∅)) /\ ⊤))) subproof { + val iff = have(!prem |- ((y === ∅)) <=> (!prem ==> (y === ∅))) by Restate + have(!prem /\ ((z === y) <=> (y === ∅)) |- ((z === y) <=> (y === ∅))) by Restate + val subst = thenHave( + (!prem /\ ((z === y) <=> (y === ∅)), ((y === ∅)) <=> (!prem ==> (y === ∅))) |- ((z === y) <=> (!prem ==> (y === ∅))) + ) by RightSubstIff.withParametersSimple(List((((y === ∅)), (!prem ==> (y === ∅)))), lambda(h, ((z === y) <=> h))) + + have((!prem /\ ((z === y) <=> (y === ∅)), !prem) |- ((z === y) <=> (!prem ==> (y === ∅)))) by Cut(iff, subst) + thenHave(thesis) by Restate + } + + val topIntro = have((!prem, ((z === y) <=> (y === ∅))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) subproof { + val topIff = have(!prem |- (prem ==> in(pair(x, y), f)) <=> ⊤) by Restate + val topSubst = have( + (!prem /\ ((z === y) <=> (y === ∅)), ((prem ==> in(pair(x, y), f)) <=> ⊤)) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))) + ) by RightSubstIff.withParametersSimple(List(((prem ==> in(pair(x, y), f)), ⊤)), lambda(h, ((z === y) <=> ((!prem ==> (y === ∅)) /\ h))))(lhs) + + have((!prem /\ ((z === y) <=> (y === ∅)), !prem) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Cut(topIff, topSubst) + thenHave((!prem, ((z === y) <=> (y === ∅))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Restate + } + + val quantification = + have((!prem, ∃!(z, (z === ∅))) |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { + have((!prem, ∀(y, ((z === y) <=> (y === ∅)))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by LeftForall(topIntro) + thenHave((!prem, ∀(y, ((z === y) <=> (y === ∅)))) |- ∀(y, (z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by RightForall + thenHave((!prem, ∀(y, ((z === y) <=> (y === ∅)))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))))) by RightExists + thenHave( + (!prem, ∃(z, ∀(y, ((z === y) <=> (y === ∅))))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))))) + ) by LeftExists + thenHave(thesis) by Restate + } + + have(thesis) by Cut(emptyPrem, quantification) + } + + val negRhs = thenHave(() |- (prem, ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅)))))) by Restate + + have(thesis) by Cut.withParameters(prem)(negRhs, positive) + } + + /** + * Function application --- denoted `f(x)`. The unique element `z` such that + * `(x, z) ∈ f` if it exists and `f` is functional, [[emptySet]] otherwise. + */ + val app = + DEF(f, x) --> The(z, ((functional(f) /\ in(x, functionDomain(f))) ==> in(pair(x, z), f)) /\ ((!functional(f) \/ !in(x, functionDomain(f))) ==> (z === ∅)))(functionApplicationUniqueness) + + /** + * Theorem -- Function application typing + * + * `f ∈ x → y, a ∈ x |- f(a) ∈ y` + */ + val functionFromApplication = Theorem( + in(f, x |=> y) /\ in(a, x) |- in(app(f, a), y) + ) { + val thm = have(functionFrom(f, x, y) /\ in(a, x) |- in(app(f, a), y)) subproof { + val funcFrom = assume(functionFrom(f, x, y)) + val inDomain = assume(in(a, x)) + + val isFunctional = have(functional(f)) by Tautology.from(funcFrom, functionFromImpliesFunctional) + val relDomainEq = have(functionDomain(f) === x) by Tautology.from(funcFrom, functionFromImpliesDomainEq) + val inRelDomain = have(in(a, functionDomain(f))) by Substitution.ApplyRules(relDomainEq)(inDomain) + + val appDef = have( + ((functional(f) /\ in(a, functionDomain(f))) ==> in(pair(a, app(f, a)), f)) + /\ ((!functional(f) \/ !in(a, functionDomain(f))) ==> (app(f, a) === ∅)) + ) by InstantiateForall(app(f, a))( + app.definition of (x := a) + ) + + have(in(pair(a, app(f, a)), f)) by Tautology.from( + isFunctional, + inRelDomain, + appDef + ) + val pairInF = thenHave(∃(z, in(pair(z, app(f, a)), f))) by RightExists + + have(∀(t, in(t, functionRange(f)) <=> ∃(z, in(pair(z, t), f)))) by InstantiateForall(functionRange(f))( + functionRange.definition of (r := f) + ) + val rangeDef = thenHave(in(app(f, a), functionRange(f)) <=> ∃(z, in(pair(z, app(f, a)), f))) by InstantiateForall(app(f, a)) + + val appInRange = have(in(app(f, a), functionRange(f))) by Tautology.from(rangeDef, pairInF) + + have(subset(functionRange(f), y)) by Weakening(functionImpliesRangeSubsetOfCodomain) + thenHave(∀(z, in(z, functionRange(f)) ==> in(z, y))) by Substitution.ApplyRules(subsetAxiom of (x := functionRange(f))) + thenHave(in(app(f, a), functionRange(f)) ==> in(app(f, a), y)) by InstantiateForall(app(f, a)) + + have(thesis) by Tautology.from(appInRange, lastStep) + } + + have(thesis) by Tautology.from(thm, functionFrom.definition) + + } + + val pairInFunctionIsApp = Theorem( + functional(f) /\ in(a, functionDomain(f)) |- in(pair(a, b), f) <=> (app(f, a) === b) + ) { + val appDef = have( + (app(f, a) === b) <=> (((functional(f) /\ in(a, functionDomain(f))) ==> in(pair(a, b), f)) /\ ((!functional(f) \/ !in(a, functionDomain(f))) ==> (b === ∅))) + ) by InstantiateForall(b)(app.definition of x -> a) + + assume(functional(f) /\ in(a, functionDomain(f))) + + val fwd = have(in(pair(a, b), f) |- app(f, a) === b) by Tautology.from(appDef) + val bwd = have(app(f, a) === b |- in(pair(a, b), f)) by Tautology.from(appDef) + have(thesis) by Tautology.from(fwd, bwd) + } + + val functionalOverApplication = Theorem( + functionalOver(f, x) /\ in(a, x) |- in(pair(a, b), f) <=> (app(f, a) === b) + ) { + assume(functionalOver(f, x)) + assume(in(a, x)) + + val domEQ = have(functionDomain(f) === x) by Tautology.from(functionalOver.definition) + have(in(a, x)) by Restate + thenHave(in(a, functionDomain(f))) by Substitution.ApplyRules(domEQ) + + have(thesis) by Tautology.from(lastStep, functionalOver.definition, pairInFunctionIsApp) + } + + /** + * Lemma --- If something is in the application of function `f` to `x`, then + * `f` is functional, `x` is in the domain of `f`, and the pair `(x, f(x))` is + * in `f`. Essentially an inversion lemma for [[app]]. + */ + val inAppIsFunction = Lemma( + in(y, app(f, x)) |- functional(f) /\ in(x, functionDomain(f)) /\ in(pair(x, app(f, x)), f) + ) { + assume(in(y, app(f, x))) + + val appIsNotEmpty = have(!(app(f, x) === ∅)) by Tautology.from( + setWithElementNonEmpty of (x := app(f, x)) + ) + + val appDef = have( + ((functional(f) /\ in(x, functionDomain(f))) ==> in(pair(x, app(f, x)), f)) + /\ ((!functional(f) \/ !in(x, functionDomain(f))) ==> (app(f, x) === ∅)) + ) by InstantiateForall(app(f, x))( + app.definition + ) + + val isFunctional = have(functional(f) /\ in(x, functionDomain(f))) by Tautology.from(appDef, appIsNotEmpty) + + val pairIn = have(in(pair(x, app(f, x)), f)) by Tautology.from( + appDef, + isFunctional + ) + + have(thesis) by Tautology.from(isFunctional, pairIn) + } + + val elemOfFunctional = Theorem( + functional(f) |- in(t, f) <=> exists(c, exists(d, in(c, functionDomain(f)) /\ in(d, functionRange(f)) /\ (t === pair(c, d)) /\ (app(f, c) === d))) + ) { + // since f is a relation + // t \in f <=> \exists c \in dom f, d \in ran f. t = (c, d) + // we need to show that the app part of the conclusion is redundant by definition of app + // have(functional(f) |- in(t, f) <=> exists(c, exists(d, in(c, functionDomain(f)) /\ in(d, functionRange(f)) /\ (t === pair(c, d))))) by Tautology.from(functional.definition, ???) + sorry + } + + val elemOfFunctionalOver = Theorem( + functionalOver(f, a) |- in(t, f) <=> exists(c, exists(d, in(c, a) /\ in(d, functionRange(f)) /\ (t === pair(c, d)) /\ (app(f, c) === d))) + ) { + sorry + } + + val elemOfFunctionFrom = Theorem( + functionFrom(f, a, b) |- in(t, f) <=> exists(c, exists(d, in(c, a) /\ in(d, b) /\ (t === pair(c, d)) /\ (app(f, c) === d))) + ) { + sorry + } + + val functionsEqualIfEqualOnDomain = Theorem( + functionalOver(f, a) /\ functionalOver(g, a) |- forall(z, in(z, a) ==> (app(f, z) === app(g, z))) <=> (f === g) + ) { + assume(functionalOver(f, a)) + assume(functionalOver(g, a)) + + sorry + } + + val functionsSubsetIfEqualOnSubsetDomain = Theorem( + functionalOver(f, a) /\ functionalOver(g, b) /\ subset(a, b) /\ forall(z, in(z, a) ==> (app(f, z) === app(g, z))) |- subset(f, g) + ) { + assume(functionalOver(f, a)) + assume(functionalOver(g, b)) + + sorry + } + + val restrictedFunctionUniqueness = Theorem( + ∃!(g, ∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z))))))) + ) { + have(∃!(g, ∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z)))))))) by UniqueComprehension( + f, + lambda(t, ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z))))) + ) + } + + /** + * Function Restriction --- The restriction of a function f to a domain x, + * also written as f_x. + * + * `restrictedFunction(f, x) = {(y, f(y)) | y ∈ x}` + * + * @param f function (set) + * @param x set to restrict to + */ + val restrictedFunction = DEF(f, x) --> The(g, ∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z)))))))(restrictedFunctionUniqueness) + + /** + * Pair membership in a restricted function -- A pair `(t, a)` is in `f_x` iff `(t, a) ∈ f` and `t ∈ x`. + * + * This is a direct but painful corollary of the definition. + */ + val restrictedFunctionPairMembership = Lemma( + in(pair(t, a), restrictedFunction(f, x)) <=> (in(pair(t, a), f) /\ in(t, x)) + ) { + val g = restrictedFunction(f, x) + + have(∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z))))))) by Definition( + restrictedFunction, + restrictedFunctionUniqueness + )(f, x) + val pairMembership = thenHave( + in(pair(t, a), g) <=> (in(pair(t, a), f) /\ ∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))))) + ) by InstantiateForall(pair(t, a)) + + have((pair(t, a) === pair(y, z)) <=> ((t === y) /\ (a === z))) by Restate.from(pairExtensionality of (a -> t, b -> a, c -> y, d -> z)) + thenHave((in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y) /\ (a === z))) by Tautology + thenHave(∀(z, (in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y) /\ (a === z)))) by RightForall + + val existentialEquiv1 = have(∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))) <=> ∃(z, in(y, x) /\ (t === y) /\ (a === z))) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P -> lambda(z, in(y, x) /\ (pair(t, a) === pair(y, z))), + Q -> lambda(z, in(y, x) /\ (t === y) /\ (a === z)) + ) + ) + + have(∃(z, in(y, x) /\ (t === y) /\ (a === z)) <=> (in(y, x) /\ (t === y))) by Restate.from( + equalityInExistentialQuantifier of ( + P -> lambda(z, in(y, x) /\ (t === y)), + y -> a + ) + ) + + have(∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y))) by Tautology.from(existentialEquiv1, lastStep) + thenHave(∀(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y)))) by RightForall + + val existentialEquiv2 = have(∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z)))) <=> ∃(y, in(y, x) /\ (t === y))) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P -> lambda(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z)))), + Q -> lambda(y, in(y, x) /\ (t === y)) + ) + ) + + have(∃(y, in(y, x) /\ (t === y)) <=> in(t, x)) by Restate.from( + equalityInExistentialQuantifier of ( + P -> lambda(y, in(y, x)), + y -> t + ) + ) + + have(∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z)))) <=> in(t, x)) by Tautology.from(existentialEquiv2, lastStep) + thenHave((in(pair(t, a), f) /\ ∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))))) <=> (in(pair(t, a), f) /\ in(t, x))) by Tautology + + have(thesis) by Tautology.from(lastStep, pairMembership) + } + + /** + * Restricted function domain -- For a function `f`, the domain of `f_x` is `x ∩ functionDomain(f)`. + */ + val restrictedFunctionDomain = Theorem( + functionDomain(restrictedFunction(f, x)) === (x ∩ functionDomain(f)) + ) { + val D = variable + val dom = x ∩ functionDomain(f) + val g = restrictedFunction(f, x) + + // Characterize x ∩ functionDomain(f) + val domCharacterization = have(∀(t, in(t, dom) <=> (∃(a, in(pair(t, a), f)) /\ in(t, x)))) subproof { + // Use the definition of the intersection + have(∀(t, in(t, dom) <=> (in(t, x) /\ in(t, functionDomain(f))))) by Definition( + setIntersection, + setIntersectionUniqueness + )(x, functionDomain(f)) + val intersectionDef = thenHave(in(t, dom) <=> (in(t, x) /\ in(t, functionDomain(f)))) by InstantiateForall(t) + + // Use the definition of the relation domain + have(∀(t, in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f)))) by Definition( + functionDomain, + relationDomainUniqueness + )(f) + thenHave(in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f))) by InstantiateForall(t) + + // Conclude + have(in(t, dom) <=> (∃(a, in(pair(t, a), f)) /\ in(t, x))) by Tautology.from(intersectionDef, lastStep) + thenHave(thesis) by RightForall + } + + // Characterize the domain of g + have(∀(D, (functionDomain(g) === D) <=> ∀(t, in(t, D) <=> ∃(a, in(pair(t, a), g))))) by Tautology.from( + functionDomain.definition of (r -> g), + relationDomainUniqueness + ) + val characterization = thenHave((functionDomain(g) === dom) <=> ∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), g)))) by InstantiateForall(dom) + + // Use the membership of a pair in the restricted function to derive a simpler characterization + have(∀(a, in(pair(t, a), g) <=> (in(pair(t, a), f) /\ in(t, x)))) by RightForall(restrictedFunctionPairMembership) + have(∃(a, in(pair(t, a), g)) <=> ∃(a, in(pair(t, a), f) /\ in(t, x))) by Tautology.from( + lastStep, + existentialEquivalenceDistribution of ( + P -> lambda(a, in(pair(t, a), g)), + Q -> lambda(a, in(pair(t, a), f) /\ in(t, x)) + ) + ) + + // Extract in(t, x) from the existential quantifier + val p = formulaVariable // local shadowing to correctly use the theorem + have(∃(a, in(pair(t, a), g)) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x)) by Tautology.from( + lastStep, + existentialConjunctionWithClosedFormula of ( + P -> lambda(a, in(pair(t, a), f)), + p -> in(t, x) + ) + ) + + thenHave((in(t, dom) <=> ∃(a, in(pair(t, a), g))) <=> (in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x))) by Tautology + thenHave(∀(t, (in(t, dom) <=> ∃(a, in(pair(t, a), g))) <=> (in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x)))) by RightForall + + have(∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), g))) <=> ∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x))) by Cut( + lastStep, + universalEquivalenceDistribution of ( + P -> lambda(t, in(t, dom) <=> ∃(a, in(pair(t, a), g))), + Q -> lambda(t, in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x)) + ) + ) + + val simplerCharacterization = have((functionDomain(g) === dom) <=> ∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x))) by Tautology.from(characterization, lastStep) + + have(thesis) by Tautology.from(domCharacterization, simplerCharacterization) + } + + val restrictedFunctionIsFunctionalOver = Lemma( + functional(f) |- functionalOver(restrictedFunction(f, x), setIntersection(x, functionDomain(f))) + ) { + // restriction is a function + + // its domain is indeed x \cap dom f + + // it is functionalOver its domain + + sorry + } + + val restrictedFunctionApplication = Lemma( + in(y, x) |- app(f, y) === app(restrictedFunction(f, x), y) + ) { + sorry + } + + /** + * Restricted function cancellation --- Restricting a function to its relation domain does nothing. + */ + val restrictedFunctionCancellation = Theorem( + functional(f) |- restrictedFunction(f, functionDomain(f)) === f + ) { + val g = restrictedFunction(f, functionDomain(f)) + + assume(functional(f)) + + have(∀(t, in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f)))) by Definition(functionDomain, relationDomainUniqueness)(f) + thenHave(in(y, functionDomain(f)) <=> ∃(a, in(pair(y, a), f))) by InstantiateForall(y) + + have(∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z))))))) by Definition( + restrictedFunction, + restrictedFunctionUniqueness + )(f, functionDomain(f)) + val equiv = thenHave(in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))))) by InstantiateForall(t) + + // Prove that the second part of the conjunction is extraneous + val hypo = have(in(t, f) |- in(t, f)) by Hypothesis + have(in(t, f) |- ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z))))) by InstantiateForall(t)(functionalMembership) + have(in(t, f) |- in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z))))) by RightAnd(hypo, lastStep) + val forward = thenHave(in(t, f) ==> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))))) by Restate + + val backward = have(in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))) ==> in(t, f)) by Tautology + + have(in(t, f) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))))) by RightIff(forward, backward) + + // Conclude by extensionnality + have(in(t, g) <=> in(t, f)) by Tautology.from(equiv, lastStep) + thenHave(∀(t, in(t, g) <=> in(t, f))) by RightForall + + have(g === f) by Tautology.from(extensionalityAxiom of (x -> g, y -> f), lastStep) + } + + val restrictedFunctionAbsorption = Theorem( + restrictedFunction(restrictedFunction(f, x), y) === restrictedFunction(f, setIntersection(x, y)) + ) { + sorry + } + + /** + * Theorem --- if `f` is [[functional]] over `x`, then `x` is precisely its + * domain as a relation. + */ + val functionalOverImpliesDomain = Theorem( + functionalOver(f, x) |- (functionDomain(f) === x) + ) { + have(thesis) by Tautology.from(functionalOver.definition) + } + + /** + * Theorem --- if a set is in the range of a function, then there exists atleast + * one element in the domain mapping to it. + */ + val inRangeImpliesPullbackExists = Theorem( + functional(f) /\ in(z, functionRange(f)) |- ∃(t, in(t, functionDomain(f)) /\ (app(f, t) === z)) + ) { + val appIff = have( + (z === app(f, t)) <=> ((functional(f) /\ in(t, functionDomain(f))) ==> in(pair(t, z), f)) /\ ((!functional(f) \/ !in(t, functionDomain(f))) ==> (z === ∅)) + ) by InstantiateForall(z)(app.definition of (x -> t)) + + have(∀(t, in(t, functionRange(f)) <=> ∃(a, in(pair(a, t), f)))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) + thenHave(in(z, functionRange(f)) <=> ∃(a, in(pair(a, z), f))) by InstantiateForall(z) + val elementInDomainExists = thenHave(in(z, functionRange(f)) |- ∃(t, in(pair(t, z), f))) by Weakening + + val toApp = have( + (functional(f), in(t, functionDomain(f)), in(pair(t, z), f)) |- ((functional(f) /\ in(t, functionDomain(f))) ==> in(pair(t, z), f)) /\ ((!functional(f) \/ !in( + t, + functionDomain(f) + )) ==> (z === ∅)) + ) by Restate + val zAppdom = have((functional(f), in(t, functionDomain(f)), in(pair(t, z), f)) |- (z === app(f, t))) by Tautology.from(toApp, appIff) + + val pairInDomain = have(in(pair(t, z), f) |- in(t, functionDomain(f))) subproof { + have(∀(t, in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f)))) by InstantiateForall(functionDomain(f))(functionDomain.definition of (r -> f)) + val domDef = thenHave(in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f))) by InstantiateForall(t) + + have(in(pair(t, z), f) |- in(pair(t, z), f)) by Hypothesis + val pairEx = thenHave(in(pair(t, z), f) |- ∃(a, in(pair(t, a), f))) by RightExists + + have(thesis) by Tautology.from(domDef, pairEx) + } + + val zApp2 = have((functional(f), in(pair(t, z), f)) |- (z === app(f, t))) by Cut(pairInDomain, zAppdom) + have((functional(f), in(pair(t, z), f)) |- in(t, functionDomain(f)) /\ (z === app(f, t))) by RightAnd(pairInDomain, zApp2) + thenHave((functional(f), in(pair(t, z), f)) |- ∃(t, in(t, functionDomain(f)) /\ (z === app(f, t)))) by RightExists + val zAppIfExists = thenHave((functional(f), ∃(t, in(pair(t, z), f))) |- ∃(t, in(t, functionDomain(f)) /\ (z === app(f, t)))) by LeftExists + + have((functional(f), in(z, functionRange(f))) |- ∃(t, in(t, functionDomain(f)) /\ (z === app(f, t)))) by Cut(elementInDomainExists, zAppIfExists) + thenHave(thesis) by Restate + } + + /** + * Theorem --- Union of two functions is a function if they agree on the + * intersection of their domains. + * + * `functional(f) ∧ functional(g) ∧ ∀ x, y. x ∈ dom(f) ∧ x ∈ dom(g) ⟹ (x, y) ∈ f <=> (x, y) ∈ g ⊢ functional(f ∪ g)` + */ + val unionOfFunctionsIsAFunction = Theorem( + functional(f) /\ functional(g) /\ forall(x, forall(y, (in(x, functionDomain(f)) /\ in(x, functionDomain(g))) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- functional(setUnion(f, g)) + ) { + // some renaming for convenience + val domF = functionDomain(f) + val domG = functionDomain(g) + + val h = setUnion(f, g) + val domH = setUnion(domF, domG) + + // is a relation + val isRelation = have(functional(f) /\ functional(g) |- relation(h)) by Tautology.from(functional.definition, functional.definition of f -> g, unionOfTwoRelations) + + // has the uniqueness property + val isFunctional = have( + functional(f) /\ functional(g) /\ forall(x, forall(y, (in(x, functionDomain(f)) /\ in(x, functionDomain(g))) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- forall( + x, + exists(y, in(pair(x, y), h)) ==> existsOne(y, in(pair(x, y), h)) + ) + ) subproof { + // x in domH <=> x in domF \/ x in domG + val domHDef = have(in(x, domH) <=> (in(x, domF) \/ in(x, domG))) by Restate.from(setUnionMembership of (z -> x, x -> domF, y -> domG)) + + // x in domF/G <=> exists y. xy in F/G + have(forall(t, in(t, domF) <=> exists(y, in(pair(t, y), f)))) by InstantiateForall(domF)(functionDomain.definition of r -> f) + val xInDomF = thenHave(in(x, domF) <=> exists(y, in(pair(x, y), f))) by InstantiateForall(x) + val xInDomG = xInDomF of f -> g + + val xInDomFOne = have((functional(f), in(x, domF)) |- existsOne(y, in(pair(x, y), f))) subproof { + have(functional(f) |- forall(x, exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f)))) by Weakening(functional.definition) + thenHave(functional(f) |- exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f))) by InstantiateForall(x) + + have(thesis) by Tautology.from(lastStep, xInDomF) + } + + // x in domH <=> exists y. xy in H OR domH = functionDomain(h) + val domHIsDomain = have(in(x, domH) <=> exists(y, in(pair(x, y), h))) subproof { + have(exists(y, in(pair(x, y), h)) <=> (exists(y, in(pair(x, y), f)) \/ exists(y, in(pair(x, y), g)))) subproof { + have(in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g))) by Restate.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) + thenHave(forall(y, in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g)))) by RightForall + have(exists(y, in(pair(x, y), h)) <=> exists(y, in(pair(x, y), f) \/ in(pair(x, y), g))) by Tautology.from( + lastStep, + existentialEquivalenceDistribution of (P -> lambda(y, in(pair(x, y), h)), Q -> lambda(y, in(pair(x, y), f) \/ in(pair(x, y), g))) + ) + // have(exists(y, in(pair(x, y), h)) <=> (exists(y, in(pair(x, y), f)) \/ exists(y, in(pair(x, y), g)))) by Tautology.from(lastStep, existentialDisjunctionCommutation of (P -> lambda(y, in(pair(x, y), f)), Q -> lambda(y, in(pair(x, y), g)))) // TODO: Possible Tautology Bug + thenHave(exists(y, in(pair(x, y), h)) <=> (exists(y, in(pair(x, y), f)) \/ exists(y, in(pair(x, y), g)))) by Substitution.ApplyRules( + existentialDisjunctionCommutation of (P -> lambda(y, in(pair(x, y), f)), Q -> lambda(y, in(pair(x, y), g))) // BUG: ? + ) + } + + have(thesis) by Tautology.from(lastStep, domHDef, xInDomF, xInDomG) + } + + // x in domF and x not in domG + have(functional(f) |- forall(x, exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f)))) by Weakening(functional.definition) + val exToExOne = thenHave((functional(f), exists(y, in(pair(x, y), f))) |- existsOne(y, in(pair(x, y), f))) by InstantiateForall(x) + + have(forall(y, !in(pair(x, y), g)) |- existsOne(y, in(pair(x, y), f)) <=> existsOne(y, in(pair(x, y), h))) subproof { + val fwd = have(in(pair(x, y), f) |- in(pair(x, y), h)) by Tautology.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) + val notzg = have(forall(y, !in(pair(x, y), g)) |- !in(pair(x, y), g)) by InstantiateForall + have(in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g))) by Restate.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) + + have(forall(y, !in(pair(x, y), g)) |- in(pair(x, y), h) <=> (in(pair(x, y), f))) by Tautology.from(lastStep, notzg, fwd) + thenHave(forall(y, !in(pair(x, y), g)) |- forall(y, in(pair(x, y), h) <=> (in(pair(x, y), f)))) by RightForall + + have(forall(y, !in(pair(x, y), g)) |- existsOne(y, in(pair(x, y), h)) <=> existsOne(y, in(pair(x, y), f))) by Tautology.from( + lastStep, + uniqueExistentialEquivalenceDistribution of (P -> lambda(z, in(pair(x, z), h)), Q -> lambda(z, in(pair(x, z), f))) + ) + } + + val notInG = have((functional(f), in(x, domF), !in(x, domG)) |- existsOne(y, in(pair(x, y), h))) by Tautology.from(lastStep, xInDomFOne, xInDomG) + + // x not in domF and x in domG + val notInF = + have((functional(g), !in(x, domF), in(x, domG)) |- existsOne(y, in(pair(x, y), h))) by Substitution.ApplyRules(unionCommutativity)(notInG of (f -> g, g -> f)) + + // x in domF and in domG + have( + forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- forall( + x, + forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))) + ) + ) by Hypothesis + thenHave( + forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)) + ) by InstantiateForall(x, y) + thenHave((forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (in(pair(x, y), f) <=> in(pair(x, y), g))) by Restate + val FToFG = thenHave( + (forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (in(pair(x, y), f) <=> (in(pair(x, y), g) \/ in(pair(x, y), f))) + ) by Tautology + + have(in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g))) by Restate.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) + + have((forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (in(pair(x, y), f) <=> in(pair(x, y), h))) by Tautology.from( + lastStep, + FToFG + ) + thenHave( + (forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- forall(y, in(pair(x, y), f) <=> in(pair(x, y), h)) + ) by RightForall + have( + (forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (existsOne(y, in(pair(x, y), f)) <=> existsOne( + y, + in(pair(x, y), h) + )) + ) by Tautology.from(lastStep, uniqueExistentialEquivalenceDistribution of (P -> lambda(z, in(pair(x, z), h)), Q -> lambda(z, in(pair(x, z), f)))) + val inFAndG = have( + (functional(f), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (existsOne(y, in(pair(x, y), h))) + ) by Tautology.from(lastStep, xInDomFOne) + + have( + (functional(f), functional(g), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))))) |- in(x, domH) ==> existsOne(y, in(pair(x, y), h)) + ) by Tautology.from(inFAndG, notInF, notInG, domHDef) + thenHave( + (functional(f), functional(g), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))))) |- exists(y, in(pair(x, y), h)) ==> existsOne( + y, + in(pair(x, y), h) + ) + ) by Substitution.ApplyRules(domHIsDomain) + thenHave( + (functional(f), functional(g), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))))) |- forall( + x, + exists(y, in(pair(x, y), h)) ==> existsOne(y, in(pair(x, y), h)) + ) + ) by RightForall + } + + have(thesis) by Tautology.from(functional.definition of f -> h, isRelation, isFunctional) + } + + val unionOfFunctionsWithDisjointDomains = Theorem( + functionalOver(f, a) /\ functionalOver(g, b) /\ (setIntersection(a, b) === emptySet) |- functionalOver(setUnion(f, g), setUnion(a, b)) + ) { + // union is functional + + // domain of union is union of domains + + sorry + } + + /** + * Theorem --- Union of a Set of Functions is a Function + * + * Given a set `z` of functions (weakly or [[reflexive]]ly) totally ordered by + * the [[subset]] relation on the elements' domains ([[functionDomain]]), `∪ + * z` is [[functional]] (in particular, with domain as the union of the + * elements' domains). + */ + val unionOfFunctionSet = Theorem( + forall(t, in(t, z) ==> functional(t)) /\ forall(x, forall(y, (in(x, z) /\ in(y, z)) ==> (subset(x, y) \/ subset(y, x)))) |- functional(union(z)) + ) { + // add assumptions + assume(forall(t, in(t, z) ==> functional(t)), forall(x, forall(y, (in(x, z) /\ in(y, z)) ==> (subset(x, y) \/ subset(y, x))))) + + // assume, towards a contradiction + assume(!functional(union(z))) + + val u = union(z) + + // begin proof ---------------- + + // u is a relation + have(in(t, z) ==> functional(t)) by InstantiateForall + have(in(t, z) ==> relation(t)) by Tautology.from(lastStep, functional.definition of f -> t) + thenHave(forall(t, in(t, z) ==> relation(t))) by RightForall + val relU = have(relation(u)) by Tautology.from(lastStep, unionOfRelationSet) + + // if u is not functional, there exists a violating pair in it + val notFun = have(exists(x, exists(y, in(pair(x, y), u)) /\ !existsOne(y, in(pair(x, y), u)))) by Tautology.from(relU, functional.definition of f -> u) + + // the violating pairs must each come from a function in z + val exfg = have((in(pair(x, y), u), in(pair(x, w), u), !(y === w)) |- exists(f, in(f, z) /\ in(pair(x, y), f)) /\ exists(g, in(g, z) /\ in(pair(x, w), g))) by Tautology.from( + unionAxiom of (x -> z, z -> pair(x, y)), + unionAxiom of (x -> z, z -> pair(x, w)) + ) + + have((exists(f, in(f, z) /\ in(pair(x, y), f)), exists(g, in(g, z) /\ in(pair(x, w), g)), !(y === w)) |- ()) subproof { + have(forall(x, forall(y, (in(x, z) /\ in(y, z)) ==> (subset(x, y) \/ subset(y, x))))) by Restate + val subfg = thenHave((in(f, z) /\ in(g, z)) ==> (subset(f, g) \/ subset(g, f))) by InstantiateForall(f, g) + + have(forall(t, in(t, z) ==> functional(t))) by Restate + val funF = thenHave(in(f, z) ==> functional(f)) by InstantiateForall(f) + val funG = funF of f -> g + + val fg = have((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(f, g)) |- ()) subproof { + have(subset(f, g) |- forall(t, in(t, f) ==> in(t, g))) by Weakening(subsetAxiom of (x -> f, y -> g)) + thenHave(subset(f, g) |- in(pair(x, y), f) ==> in(pair(x, y), g)) by InstantiateForall(pair(x, y)) + thenHave((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(f, g)) |- in(pair(x, y), g) /\ in(pair(x, w), g) /\ !(y === w)) by Tautology + have(thesis) by Tautology.from(lastStep, funG, violatingPairInFunction of (f -> g, z -> w)) + } + + val gf = have((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(g, f)) |- ()) subproof { + have(subset(g, f) |- forall(t, in(t, g) ==> in(t, f))) by Weakening(subsetAxiom of (x -> g, y -> f)) + thenHave(subset(g, f) |- in(pair(x, w), g) ==> in(pair(x, w), f)) by InstantiateForall(pair(x, w)) + thenHave((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(g, f)) |- in(pair(x, w), f) /\ in(pair(x, y), f) /\ !(y === w)) by Tautology + have(thesis) by Tautology.from(lastStep, funF, violatingPairInFunction of (f -> f, z -> w)) + } + + have((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w)) |- ()) by Tautology.from(subfg, fg, gf) + thenHave((exists(f, in(f, z) /\ in(pair(x, y), f)), (in(g, z) /\ in(pair(x, w), g)), !(y === w)) |- ()) by LeftExists + thenHave(thesis) by LeftExists + } + + have((in(pair(x, y), u) /\ in(pair(x, w), u) /\ !(y === w)) |- ()) by Tautology.from(lastStep, exfg) + thenHave(exists(w, in(pair(x, y), u) /\ in(pair(x, w), u) /\ !(y === w)) |- ()) by LeftExists + thenHave(exists(y, exists(w, in(pair(x, y), u) /\ in(pair(x, w), u) /\ !(y === w))) |- ()) by LeftExists + + have(exists(y, in(pair(x, y), u)) /\ !existsOne(y, in(pair(x, y), u)) |- ()) by Tautology.from(lastStep, atleastTwoExist of P -> lambda(y, in(pair(x, y), u))) + thenHave(exists(x, exists(y, in(pair(x, y), u)) /\ !existsOne(y, in(pair(x, y), u))) |- ()) by LeftExists + + // contradiction + have(thesis) by Tautology.from(lastStep, notFun) + } + + /** + * Theorem --- Domain of Functional Union + * + * If the unary union of a set is functional, then its domain is defined + * precisely by the union of the domains of its elements. + * + * functional(\cup z) |- \forall t. t \in dom(U z) <=> \exists y \in z. t + * \in dom(y) + * + * This holds, particularly, as the elements of z must be functions + * themselves, which follows from the assumption. + */ + val domainOfFunctionalUnion = Theorem( + functional(union(z)) |- forall(t, in(t, functionDomain(union(z))) <=> exists(y, in(y, z) /\ in(t, functionDomain(y)))) + ) { + assume(functional(union(z))) + have(relation(union(z))) by Tautology.from(functional.definition of f -> union(z)) + have(thesis) by Tautology.from(lastStep, domainOfRelationalUnion) + } + + val sigmaUniqueness = Theorem( + ∃!(z, ∀(t, in(t, z) <=> ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) + ) { + // we first show that the sigma is a subset of a larger set: Σ(A, B) ⊆ A × ⋃(range(B)) + val inclusion = have(∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))) ==> in(t, cartesianProduct(A, union(relationRange(B))))) subproof { + assume(∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))) + val aw = witness(lastStep) + have(∃(b, (t === pair(aw, b)) /\ in(aw, A) /\ in(b, app(B, aw)))) by Restate + val bw = witness(lastStep) + val isPair = have(t === pair(aw, bw)) by Restate + val inA = have(in(aw, A)) by Restate + val inBApp = have(in(bw, app(B, aw))) by Restate + + val inUnion = have(in(bw, union(relationRange(B)))) subproof { + val inRange = have(in(app(B, aw), relationRange(B))) subproof { + have(in(pair(aw, app(B, aw)), B)) by Tautology.from(inAppIsFunction of (f := B, x := aw, y := bw), inBApp) + val rangeCondition = thenHave(∃(a, in(pair(a, app(B, aw)), B))) by RightExists + + have(∀(t, in(t, relationRange(B)) <=> ∃(a, in(pair(a, t), B)))) by InstantiateForall(relationRange(B))( + relationRange.definition of (r -> B) + ) + val rangeDef = thenHave(in(app(B, aw), relationRange(B)) <=> ∃(a, in(pair(a, app(B, aw)), B))) by InstantiateForall(app(B, aw)) + + have(thesis) by Tautology.from(rangeDef, rangeCondition) + } + + // using the definition of a union with app(B, aw) as the intermediate set + have(in(app(B, aw), relationRange(B)) /\ in(bw, app(B, aw))) by Tautology.from(inRange, inBApp) + thenHave(∃(y, in(y, relationRange(B)) /\ in(bw, y))) by RightExists.withParameters(in(y, relationRange(B)) /\ in(bw, y), y, app(B, aw)) + thenHave(thesis) by Substitution.ApplyRules(unionAxiom of (x := relationRange(B), z := bw)) + } + + have(in(aw, A) /\ in(bw, union(relationRange(B)))) by Tautology.from(inA, inUnion) + have(in(pair(aw, bw), cartesianProduct(A, union(relationRange(B))))) by Tautology.from( + lastStep, + pairInCartesianProduct of (a := aw, b := bw, x := A, y := union(relationRange(B))) + ) + thenHave(in(t, cartesianProduct(A, union(relationRange(B))))) by Substitution.ApplyRules(isPair) + } + + // given belonging in a larger set, we can use unique comprehension + have(thesis) by UniqueComprehension.fromOriginalSet( + cartesianProduct(A, union(relationRange(B))), + lambda(t, ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))), + inclusion + ) + } + + /** + * Dependent Sum (Sigma) -- The generalized product. + * + * Given a set `A` and a function `B`, the dependent sum `Σ(A, B)` + * is the set of all pairs `(a, b)` such that `a` is in `A` and `b` + * is in `B(a)`. + * + * `Σ(A, B) = { (a, b) | a ∈ A, b ∈ B(a) }` + */ + val Sigma = DEF(A, B) --> The( + z, + ∀(t, in(t, z) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) + )(sigmaUniqueness) + + /** + * Theorem --- Sigma with Empty Set is the empty set + * + * The Sigma of the empty set with any function is the empty set. + * + * `Σ(∅, B) = ∅` + */ + val sigmaWithEmptySet = Theorem( + Sigma(∅, B) === ∅ + ) { + have(∀(t, in(t, Sigma(∅, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))))))) by InstantiateForall(Sigma(∅, B))( + Sigma.definition of (A -> ∅) + ) + val sigmaDef = thenHave(in(t, Sigma(∅, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a)))))) by InstantiateForall(t) + + val emptySetDef = have(in(t, ∅) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a)))))) subproof { + val lhs = have(in(t, ∅) |- (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a)))))) by Weakening( + emptySet.definition of (x -> t) + ) + + have(((t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))) |- in(t, ∅)) by Weakening(emptySet.definition of (x -> a)) + thenHave(∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))) |- in(t, ∅)) by LeftExists + val rhs = thenHave(∃(a, ∃(b, ((t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))))) |- in(t, ∅)) by LeftExists + + have(thesis) by Tautology.from(lhs, rhs) + } + + have(in(t, Sigma(∅, B)) <=> in(t, ∅)) by Tautology.from(sigmaDef, emptySetDef) + val ext = thenHave(∀(t, in(t, Sigma(∅, B)) <=> in(t, ∅))) by RightForall + + have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> Sigma(∅, B), y -> ∅)) + } + + /** + * Theorem --- Pairs in Sigma + * + * Describes the pairs inside of a Sigma set. + * + * `(a, b) ∈ Σ(A, B) ⟹ a ∈ A ∧ b ∈ B(a)` + */ + val pairsInSigma = Theorem( + in(p, Sigma(A, B)) |- in(firstInPair(p), A) /\ in(secondInPair(p), app(B, firstInPair(p))) + ) { + val firstInSigma = Theorem( + in(p, Sigma(A, B)) |- in(firstInPair(p), A) + ) { + assume(in(p, Sigma(A, B))) + + have(∀(t, in(t, Sigma(A, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))))) by InstantiateForall(Sigma(A, B))( + Sigma.definition + ) + thenHave(in(p, Sigma(A, B)) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) by InstantiateForall(p) + thenHave(∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A)))) by Tautology + + val aw = witness(lastStep) + thenHave(∃(b, (p === pair(aw, b)) /\ in(aw, A))) by Restate + val bw = witness(lastStep) + val isPairAndInA = thenHave((p === pair(aw, bw)) /\ in(aw, A)) by Restate + val isPair = have(p === pair(aw, bw)) by Weakening(isPairAndInA) + val inA = have(in(aw, A)) by Weakening(isPairAndInA) + + val first = have(firstInPair(pair(aw, bw)) === aw) by Tautology.from(firstInPairReduction of (x := aw, y := bw)) + + have(in(firstInPair(pair(aw, bw)), A)) by Substitution.ApplyRules(first)(inA) + thenHave(thesis) by Substitution.ApplyRules(isPair) + } + + val secondInSigma = Theorem( + in(p, Sigma(A, B)) |- in(secondInPair(p), app(B, firstInPair(p))) + ) { + assume(in(p, Sigma(A, B))) + + have(∀(t, in(t, Sigma(A, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))))) by InstantiateForall(Sigma(A, B))( + Sigma.definition + ) + thenHave(in(p, Sigma(A, B)) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) by InstantiateForall(p) + thenHave(∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))) by Tautology + + val aw = witness(lastStep) + thenHave(∃(b, (p === pair(aw, b)) /\ in(aw, A) /\ in(b, app(B, aw)))) by Restate + val bw = witness(lastStep) + val isPairAndInAAndBInApp = thenHave((p === pair(aw, bw)) /\ in(aw, A) /\ in(bw, app(B, aw))) by Restate + val isPair = have(p === pair(aw, bw)) by Weakening(isPairAndInAAndBInApp) + val inA = have(in(aw, A)) by Weakening(isPairAndInAAndBInApp) + val BInApp = have(in(bw, app(B, aw))) by Weakening(isPairAndInAAndBInApp) + + val first = have(firstInPair(pair(aw, bw)) === aw) by Tautology.from(firstInPairReduction of (x := aw, y := bw)) + val second = have(secondInPair(pair(aw, bw)) === bw) by Tautology.from(secondInPairReduction of (x := aw, y := bw)) + + have(in(secondInPair(pair(aw, bw)), app(B, firstInPair(pair(aw, bw))))) by Substitution.ApplyRules(first, second)(BInApp) + thenHave(thesis) by Substitution.ApplyRules(isPair) + } + + have(thesis) by Tautology.from(firstInSigma, secondInSigma) + } + + val piUniqueness = Theorem( + ∃!(z, ∀(g, in(g, z) <=> (in(g, powerSet(Sigma(x, f))) /\ functionalOver(g, x)))) + ) { + have(thesis) by UniqueComprehension( + powerSet(Sigma(x, f)), + lambda(z, functionalOver(z, x)) + ) + } + + /** + * Dependent Product (Pi) -- The generalized function space. + * + * Given a set `x` and a function `f`, the dependent product `Π(x, f)` + * is the set of all functions `g` such that `g` is a in the powerset + * of `Σ(x, f)` + * + * `Π(x, f) = { g ∈ P(Σ(x, f)) | functionalOver(g, x) }` + */ + val Pi = DEF(x, f) --> The( + z, + ∀(g, in(g, z) <=> (in(g, powerSet(Sigma(x, f))) /\ functionalOver(g, x))) + )(piUniqueness) + + /** + * Theorem --- Pi with Empty Set is the Power Set of the Empty Set + * + * The Pi of the empty set with any function is the power set of the empty set. + * + * `Π(∅, f) = P(∅)` + */ + val piWithEmptySet = Theorem( + Pi(∅, f) === powerSet(∅) + ) { + have(∀(g, in(g, Pi(∅, f)) <=> (in(g, powerSet(Sigma(∅, f))) /\ functionalOver(g, ∅)))) by InstantiateForall(Pi(∅, f))( + Pi.definition of (x -> ∅) + ) + thenHave(∀(g, in(g, Pi(∅, f)) <=> (in(g, powerSet(∅)) /\ functionalOver(g, ∅)))) by Substitution.ApplyRules(sigmaWithEmptySet) + val piDef = thenHave(in(g, Pi(∅, f)) <=> (in(g, powerSet(∅)) /\ functionalOver(g, ∅))) by InstantiateForall(g) + + val lhs = have(in(g, Pi(∅, f)) ==> (in(g, powerSet(∅)))) by Weakening(piDef) + + val rhs = have(in(g, powerSet(∅)) ==> in(g, Pi(∅, f))) subproof { + val assumption = assume(in(g, powerSet(∅))) + + have(in(g, singleton(∅))) by Substitution.ApplyRules(powerSetEmptySet)(assumption) + val gIsEmpty = thenHave(g === ∅) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := g, x := ∅)) + + have(∅ === relationDomain(∅)) by Weakening(domainOfEmptySetIsEmpty) + val isDomain = thenHave(∅ === relationDomain(g)) by Substitution.ApplyRules(gIsEmpty) + + have(functional(∅)) by Weakening(emptySetFunctional) + val isFunctional = thenHave(functional(g)) by Substitution.ApplyRules(gIsEmpty) + + val isFunctionalOver = have(functionalOver(g, ∅)) by Tautology.from( + functionalOver.definition of (f := g, x := ∅), + isFunctional, + isDomain + ) + + val result = have(in(g, Pi(∅, f))) by Tautology.from(piDef, assumption, isFunctionalOver) + + have(thesis) by Tautology.from(assumption, result) + } + + have(in(g, Pi(∅, f)) <=> in(g, powerSet(∅))) by Tautology.from(lhs, rhs) + val ext = thenHave(∀(g, in(g, Pi(∅, f)) <=> in(g, powerSet(∅)))) by RightForall + + have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> Pi(∅, f), y -> powerSet(∅))) + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala new file mode 100644 index 000000000..ec68adf10 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala @@ -0,0 +1,74 @@ +package lisa.maths.settheory + +/** + * Set-theoretic functions library + * + * Develops the set-theoretic functions, their properties, and common theorems. + */ +package object functions { + export lisa.maths.settheory.functions.Functionals.{ + functional, + functionalOver, + functionalMembership, + violatingPairInFunction, + pairSingletonIsFunctional, + setOfFunctionsUniqueness, + setOfFunctions, + functionFrom, + functionFromImpliesFunctional, + functionApplicationUniqueness, + app, + pairInFunctionIsApp, + functionalOverApplication, + elemOfFunctional, + elemOfFunctionalOver, + elemOfFunctionFrom, + functionsEqualIfEqualOnDomain, + functionsSubsetIfEqualOnSubsetDomain, + restrictedFunctionUniqueness, + restrictedFunction, + restrictedFunctionPairMembership, + restrictedFunctionDomain, + restrictedFunctionIsFunctionalOver, + restrictedFunctionApplication, + restrictedFunctionCancellation, + restrictedFunctionAbsorption, + functionalOverImpliesDomain, + functionFromImpliesDomainEq, + functionImpliesRangeSubsetOfCodomain, + inRangeImpliesPullbackExists, + unionOfFunctionsIsAFunction, + unionOfFunctionsWithDisjointDomains, + unionOfFunctionSet, + domainOfFunctionalUnion, + |=>, + functionRange, + functionDomain, + emptySetFunctional, + inAppIsFunction, + functionFromApplication, + sigmaUniqueness, + Sigma, + sigmaWithEmptySet, + pairsInSigma, + piUniqueness, + Pi, + piWithEmptySet + } + export lisa.maths.settheory.functions.FunctionProperties.{ + surjective, + onto, + injective, + oneone, + bijective, + invertibleFunction, + inverseFunctionOf, + surjectiveImpliesRangeIsCodomain, + cantorTheorem, + constantFunction, + constantFunctionDomain, + constantFunctionIsFunctional, + constantFunctionFunctionFrom, + constantFunctionApplication + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala new file mode 100644 index 000000000..7a1be0cfa --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala @@ -0,0 +1,207 @@ +package lisa.maths.settheory.orderings + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.orderings.PartialOrders.* + +object InclusionOrders extends lisa.Main { + + // var defs + private val w = variable + private val x = variable + private val y = variable + private val z = variable + private val h = formulaVariable + private val t = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + + // relation and function symbols + private val r = variable + private val p = variable + private val q = variable + private val f = variable + private val g = variable + private val F = function[1] + private val G = function[2] + + private val P = predicate[1] + private val Q = predicate[1] + private val schemPred = predicate[1] + + val inclusionOnUniqueness = Lemma( + () |- existsOne(z, forall(t, in(t, z) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) + ) { + have(thesis) by UniqueComprehension(cartesianProduct(a, a), lambda(t, exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))) + } + + /** + * The relation induced by inclusion on a set, noted `∈_a`. + * + * `∈_a = {(y, x) ∈ a * a | y ∈ x}` + */ + val inclusionOn = DEF(a) --> The(z, forall(t, in(t, z) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))))(inclusionOnUniqueness) + + /** + * The partial order `(a, ∈_a)` induced by the inclusion relation + * ([[inclusionOn]]) on a set. + */ + val inclusionOrderOn = DEF(a) --> pair(a, inclusionOn(a)) + + /** + * Theorem --- the inclusion order on a set is defined by the meta inclusion [[in]]. + */ + val inclusionOrderElem = Lemma( + () |- (in(b, a) /\ in(c, a) /\ in(b, c)) <=> in(pair(b, c), inclusionOn(a)) + ) { + val prodElem = have((in(b, a) /\ in(c, a)) <=> in(pair(b, c), cartesianProduct(a, a))) by Restate.from(pairInCartesianProduct of (a -> b, b -> c, x -> a, y -> a)) + + val exXY = have(in(b, c) <=> exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x))))) subproof { + val fwd = have(in(b, c) |- exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x))))) subproof { + have(in(b, c) |- in(b, c) /\ (pair(b, c) === pair(b, c))) by Restate + thenHave(in(b, c) |- exists(x, in(b, x) /\ (pair(b, c) === pair(b, x)))) by RightExists + thenHave(thesis) by RightExists + } + val bwd = have(exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x)))) |- in(b, c)) subproof { + val pairExt = have((pair(b, c) === pair(y, x)) |- (b === y) /\ (c === x)) by Weakening(pairExtensionality of (a -> b, b -> c, c -> y, d -> x)) + + have(in(y, x) |- in(y, x)) by Hypothesis + thenHave((in(y, x), b === y, c === x) |- in(b, c)) by Substitution.ApplyRules(b === y, c === x) + have((in(y, x) /\ (pair(b, c) === pair(y, x))) |- in(b, c)) by Tautology.from(pairExt, lastStep) + thenHave(exists(x, in(y, x) /\ (pair(b, c) === pair(y, x))) |- in(b, c)) by LeftExists + thenHave(thesis) by LeftExists + } + + have(thesis) by Tautology.from(fwd, bwd) + } + + have(forall(t, in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(a))(inclusionOn.definition) + thenHave(in(pair(b, c), inclusionOn(a)) <=> (in(pair(b, c), cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x)))))) by InstantiateForall(pair(b, c)) + + have(thesis) by Tautology.from(lastStep, prodElem, exXY) + } + + /** + * Theorem --- the inclusion order on the any set is a relation. + */ + val inclusionIsRelation = Theorem( + () |- relationBetween(inclusionOn(a), a, a) + ) { + have(forall(t, in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(a))(inclusionOn.definition) + thenHave(in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))) by InstantiateForall(t) + thenHave(in(t, inclusionOn(a)) ==> in(t, cartesianProduct(a, a))) by Weakening + thenHave(forall(t, in(t, inclusionOn(a)) ==> in(t, cartesianProduct(a, a)))) by RightForall + // thenHave(forall(z, in(z, inclusionOn(a)) ==> in(z, cartesianProduct(a, a)))) by Restate + val subs = thenHave(subset(inclusionOn(a), cartesianProduct(a, a))) by Substitution.ApplyRules(subsetAxiom of (x -> inclusionOn(a), y -> cartesianProduct(a, a))) + + have(thesis) by Tautology.from(subs, relationBetween.definition of (r -> inclusionOn(a), a -> a, b -> a)) + } + + val inclusionIsAntiReflexive = Theorem( + antiReflexive(inclusionOn(a), a) + ) { + sorry + } + + val inclusionIsAntiSymmetric = Theorem( + antiSymmetric(inclusionOn(a), a) + ) { + sorry + } + + /** + * Theorem --- the inclusion order on the empty set is the empty relation. + */ + val emptySetInclusionEmpty = Lemma( + () |- (inclusionOn(emptySet) === emptySet) + ) { + have(forall(t, in(t, inclusionOn(emptySet)) <=> (in(t, cartesianProduct(emptySet, emptySet)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall( + inclusionOn(emptySet) + )(inclusionOn.definition of (a -> emptySet)) + val incDef = thenHave(in(t, inclusionOn(emptySet)) <=> (in(t, cartesianProduct(emptySet, emptySet)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))) by InstantiateForall(t) + + have(forall(t, in(t, cartesianProduct(emptySet, emptySet)) <=> in(t, emptySet))) by Tautology.from( + productWithEmptySetEmpty of (x -> emptySet), + extensionalityAxiom of (x -> cartesianProduct(emptySet, emptySet), y -> emptySet) + ) + val emp = thenHave(in(t, cartesianProduct(emptySet, emptySet)) <=> in(t, emptySet)) by InstantiateForall(t) + + val impl = have(in(t, inclusionOn(emptySet)) <=> in(t, emptySet)) subproof { + val lhs = have(in(t, inclusionOn(emptySet)) |- in(t, emptySet)) by Tautology.from(incDef, emp) + val rhs = have(in(t, emptySet) |- in(t, inclusionOn(emptySet))) by Weakening(emptySet.definition of (x -> t)) + have(thesis) by Tautology.from(lhs, rhs) + } + + val ext = thenHave(forall(t, in(t, inclusionOn(emptySet)) <=> in(t, emptySet))) by RightForall + have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> inclusionOn(emptySet), y -> emptySet)) + } + + /** + * Theorem --- the inclusion order on the empty set is a reflexive relation. + */ + val emptyInclusionReflexive = Lemma( + () |- reflexive(inclusionOn(emptySet), emptySet) + ) { + have(reflexive(emptySet, emptySet)) by Restate.from(emptyRelationReflexiveOnItself) + thenHave(thesis) by Substitution.ApplyRules(emptySetInclusionEmpty) + } + + /** + * Theorem --- the inclusion order on the empty set is an irreflexive relation. + */ + val emptyInclusionIrreflexive = Lemma( + () |- irreflexive(inclusionOn(emptySet), a) + ) { + have(irreflexive(emptySet, a)) by Restate.from(emptyRelationIrreflexive) + thenHave(thesis) by Substitution.ApplyRules(emptySetInclusionEmpty) + } + + /** + * Theorem --- the inclusion order on the empty set is a transitive relation. + */ + val emptyInclusionTransitive = Lemma( + () |- transitive(inclusionOn(emptySet), a) + ) { + have(transitive(emptySet, a)) by Restate.from(emptyRelationTransitive) + thenHave(thesis) by Substitution.ApplyRules(emptySetInclusionEmpty) + } + + /** + * Theorem --- the empty relation partially orders the empty set + */ + val emptySetPartialOrder = Lemma( + () |- partialOrder(pair(emptySet, emptySet)) + ) { + have( + partialOrder(pair(emptySet, emptySet)) <=> (relationBetween(emptySet, emptySet, emptySet) /\ antiSymmetric(emptySet, emptySet) /\ antiReflexive( + emptySet, + emptySet + ) /\ transitive(emptySet, emptySet)) + ) by Substitution.ApplyRules(firstInPairReduction, secondInPairReduction)( + partialOrder.definition of p -> pair(emptySet, emptySet) + ) + have(thesis) by Tautology.from( + lastStep, + emptySetRelationOnItself, + emptyRelationIrreflexive of a -> emptySet, + emptyRelationTransitive of a -> emptySet, + emptyRelationAntiSymmetric of a -> emptySet + ) + } + + /** + * Theorem --- the empty relation totally orders the empty set + */ + val emptySetTotalOrder = Lemma( + () |- totalOrder(pair(emptySet, emptySet)) + ) { + have(totalOrder(pair(emptySet, emptySet)) <=> (partialOrder(pair(emptySet, emptySet)) /\ total(emptySet, emptySet))) by Substitution.ApplyRules( + firstInPairReduction of (x -> emptySet, y -> emptySet), + secondInPairReduction of (x -> emptySet, y -> emptySet) + )(totalOrder.definition of p -> pair(emptySet, emptySet)) + have(thesis) by Tautology.from(lastStep, emptySetPartialOrder, emptyRelationTotalOnItself) + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala new file mode 100644 index 000000000..b1feffb59 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala @@ -0,0 +1,325 @@ +package lisa.maths.settheory.orderings + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.orderings.InclusionOrders.* +import lisa.maths.settheory.orderings.PartialOrders.* +import lisa.maths.settheory.orderings.Segments.* +import lisa.maths.settheory.orderings.WellOrders.* + +import Ordinals.* + +object Induction extends lisa.Main { + + // var defs + private val a = variable + private val b = variable + private val p = variable + private val r = variable + private val t = variable + private val w = variable + private val x = variable + private val y = variable + private val z = variable + + // relation and function symbols + + private val P = predicate[1] + private val Q = predicate[1] + + /** + * Theorem --- Well Ordered Induction on a Subclass + * + * If `p` is a strict well-ordering, and `Q` is a subclass of the base set of + * `p`, called `A`, then + * + * `\forall x \in A. (A |^ x) \subseteq Q ==> x \in Q |- A = Q` + * + * i.e., if `Q` is a subclass of `A`, and the property `Q` passes to `x` from + * its initial segment, then `A` is `Q`. + */ + val wellOrderedInductionSubclass = Theorem( + { + val A = firstInPair(p) + ( + wellOrder(p), + forall(x, Q(x) ==> in(x, A)), + forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) + ) + |- forall(x, Q(x) <=> in(x, A)) + } + ) { + // renaming + val A = firstInPair(p) + val ` in(x, A)), + forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) + ) + + // assume, towards a contradiction + val contra = !forall(x, Q(x) <=> in(x, A)) + assume(contra) + + val contraDis = have(exists(x, (Q(x) /\ !in(x, A)) \/ (!Q(x) /\ in(x, A)))) by Restate + + val lhs = have(Q(x) /\ !in(x, A) |- ()) subproof { + have(Q(x) ==> in(x, A)) by InstantiateForall + thenHave(thesis) by Tautology + } + + val rhs = have(!Q(x) /\ in(x, A) |- ()) subproof { + val zDef = forall(t, in(t, z) <=> (in(t, A) /\ !Q(t))) + + // z exists by comprehension + val zExists = have(exists(z, zDef)) subproof { + have(existsOne(z, zDef)) by UniqueComprehension(A, lambda(t, !Q(t))) + have(thesis) by Cut(lastStep, existsOneImpliesExists of P -> lambda(z, zDef)) + } + + // z is a subset of A + val zSubset = have(zDef |- subset(z, A)) subproof { + have(zDef |- in(t, z) <=> (in(t, A) /\ !Q(t))) by InstantiateForall + thenHave(zDef |- in(t, z) ==> in(t, A)) by Weakening + thenHave(zDef |- forall(t, in(t, z) ==> in(t, A))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> z, y -> A)) + } + + // there exists a least element y in z + val yDef = in(y, z) /\ forall(w, (!Q(w) /\ in(w, A)) ==> (in(pair(y, w), ` (in(x, A) /\ !Q(x))) by InstantiateForall + thenHave(in(x, z)) by Tautology + val zNonEmpty = have(!(z === emptySet)) by Tautology.from(lastStep, setWithElementNonEmpty of (y -> x, x -> z)) + + have(forall(b, (subset(b, A) /\ !(b === emptySet)) ==> exists(y, in(y, b) /\ forall(w, in(w, b) ==> (in(pair(y, w), ` exists(y, in(y, z) /\ forall(w, in(w, z) ==> (in(pair(y, w), ` (in(pair(y, w), ` (in(w, A) /\ !Q(w))) by InstantiateForall + thenHave(in(w, z) ==> (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` Q(w))) subproof { + assume(zDef, yDef) + + // TODO: assumptions annoy instantiations of external imports, so this is done rather verbosely here + // see https://github.com/epfl-lara/lisa/issues/161 + have(forall(z, (z === initialSegment(p, y)) <=> forall(t, in(t, z) <=> (in(t, A) /\ in(pair(t, y), ` y)) + thenHave(forall(t, in(t, initialSegment(p, y)) <=> (in(t, A) /\ in(pair(t, y), ` (in(w, A) /\ in(pair(w, y), ` (in(pair(y, w), ` (in(pair(y, w), ` !in(pair(y, y), ` ` A)) + } + + thenHave(in(w, A) ==> !in(pair(w, w), ` (y === w)))) subproof { + have(antiSymmetric(` ` A)) + } + + thenHave((in(pair(y, w), ` (y === w)) by InstantiateForall(y, w) + have(thesis) by Tautology.from(lastStep, yw, rhs) + } + + have(thesis) by Tautology.from(lhs, rhs, cases) + } + + have(in(w, initialSegment(p, y)) ==> Q(w)) by Tautology.from(lastStep, wInInit) + thenHave(thesis) by RightForall + } + + // but if the initial segment of y is a subset of Q, then y is in Q + val yInQ = have((zDef, yDef, in(y, A)) |- Q(y)) subproof { + have(in(y, A) ==> (forall(w, in(w, initialSegment(p, y)) ==> Q(w)) ==> Q(y))) by InstantiateForall + thenHave((in(y, A), (forall(w, in(w, initialSegment(p, y)) ==> Q(w)))) |- Q(y)) by Restate + have(thesis) by Cut(yInitInQ, lastStep) + } + + // however, we know y is in z, so !Q(y), hence contradiction + have((zDef, yDef) |- ()) subproof { + assume(zDef, yDef) + val ynotQ = have(in(y, z) <=> (in(y, A) /\ !Q(y))) by InstantiateForall + have(in(y, z)) by Restate + have(thesis) by Tautology.from(lastStep, ynotQ, yInQ) + } + + thenHave((zDef, exists(y, yDef)) |- ()) by LeftExists + have((zDef, !Q(x) /\ in(x, A)) |- ()) by Cut(yExists, lastStep) + thenHave((exists(z, zDef), !Q(x) /\ in(x, A)) |- ()) by LeftExists + have(thesis) by Cut(zExists, lastStep) + } + + have(((Q(x) /\ !in(x, A)) \/ (!Q(x) /\ in(x, A))) |- ()) by Tautology.from(lhs, rhs) + thenHave(exists(x, (Q(x) /\ !in(x, A)) \/ (!Q(x) /\ in(x, A))) |- ()) by LeftExists + + have(thesis) by Tautology.from(lastStep, contraDis) + } + + /** + * Theorem --- Well Ordered Induction + * + * If `p` is a strict well-ordering, `Q` is a class, and `A` the base set of + * `p`, then + * + * `∀ x ∈ A. (A |^ x) ⊆ Q ==> x ∈ Q |- ∀ x ∈ A. x ∈ Q` + * + * i.e., if the property `Q` passes to `x` from its initial segment, then `Q` + * holds for every element of `A`. + */ + val wellOrderedInduction = Theorem( + { + val A = firstInPair(p) + ( + wellOrder(p), + forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) + ) + |- forall(x, in(x, A) ==> Q(x)) + } + ) { + val A = firstInPair(p) + val ` (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) + ) + + // make a subclass out of Q by intersecting with A + def prop(x: Term): Formula = Q(x) /\ in(x, A) + + have(prop(x) ==> in(x, A)) by Restate + val subclassProp = thenHave(forall(x, prop(x) ==> in(x, A))) by Restate + + have(forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x)))) subproof { + have(in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) by InstantiateForall + val fy = thenHave(in(x, A) |- (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) by Restate + // have(forall(y, in(y, initialSegment(p, x)) ==> Q(y)) |- (in(y, initialSegment(p, x)) ==> Q(y))) by InstantiateForall + // val inst = have(in(x, A) |- (in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x)) by Tautology.from(lastStep, fy) + + have(in(y, initialSegment(p, x)) |- in(y, A)) subproof { + have(forall(z, (z === initialSegment(p, x)) <=> forall(t, in(t, z) <=> (in(t, A) /\ in(pair(t, x), ` x)) + thenHave(forall(t, in(t, initialSegment(p, x)) <=> (in(t, A) /\ in(pair(t, x), ` (in(y, A) /\ in(pair(y, x), ` Q(y)) <=> (in(y, initialSegment(p, x)) ==> prop(y))) by Tautology.from(lastStep) + thenHave(forall(y, (in(y, initialSegment(p, x)) ==> Q(y)) <=> (in(y, initialSegment(p, x)) ==> prop(y)))) by RightForall + have(forall(y, in(y, initialSegment(p, x)) ==> Q(y)) <=> forall(y, in(y, initialSegment(p, x)) ==> prop(y))) by Cut( + lastStep, + universalEquivalenceDistribution of (P -> lambda(y, in(y, initialSegment(p, x)) ==> Q(y)), Q -> lambda(y, in(y, initialSegment(p, x)) ==> prop(y))) + ) + + have(in(x, A) |- forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x)) by Tautology.from(lastStep, fy) + thenHave(in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x))) by Restate + thenHave(thesis) by RightForall + } + + have(forall(x, in(x, A) <=> prop(x))) by Tautology.from(lastStep, subclassProp, wellOrderedInductionSubclass of Q -> lambda(x, prop(x))) + thenHave(in(x, A) <=> prop(x)) by InstantiateForall(x) + thenHave(in(x, A) ==> Q(x)) by Tautology + thenHave(thesis) by RightForall + } + + val transfiniteInduction = Theorem( + forall(x, ordinal(x) ==> (forall(y, in(y, x) ==> Q(y)) ==> Q(x))) |- forall(x, ordinal(x) ==> Q(x)) + ) { + + assume(forall(x, ordinal(x) ==> (forall(y, in(y, x) ==> Q(y)) ==> Q(x)))) + assume(exists(x, ordinal(x) /\ !Q(x))) // negated conclusion + + // we assume the negated conjecture and derive a contradiction + + // prop := On \ Q + def prop(x: Term) = ordinal(x) /\ !Q(x) + + // there is a minimal element in prop + val yDef = prop(y) /\ forall(x, prop(x) ==> in(y, x)) + + have(prop(x) ==> ordinal(x)) by Restate + thenHave(forall(x, prop(x) ==> ordinal(x))) + val yExists = have(exists(y, yDef)) by Tautology.from(lastStep, ordinalSubclassHasMinimalElement of (P -> lambda(x, prop(x)))) + + // so everything less than y is not in prop + val fz = have(yDef |- forall(z, in(z, y) ==> !prop(z))) subproof { + assume(yDef) + + // assume z \in y + // but \forall x. prop(x) ==> y \in x + // so prop(z) ==> y \in z + have(forall(x, prop(x) ==> in(y, x))) by Restate + thenHave(prop(z) ==> in(y, z)) by InstantiateForall(z) + + // but inclusion is anti symmetric (regularity) + have(in(z, y) |- !prop(z)) by Tautology.from(lastStep, inclusionAntiSymmetric of (x -> z, y -> y)) + thenHave(in(z, y) ==> !prop(z)) by Restate + thenHave(thesis) by RightForall + } + + // but by assumption, this must mean Q(y) + have(yDef |- Q(y)) subproof { + assume(yDef) + have(forall(z, in(z, y) ==> !prop(z))) by Restate.from(fz) + thenHave(in(z, y) ==> !prop(z)) by InstantiateForall(z) + have(in(z, y) ==> Q(z)) by Tautology.from(lastStep, elementsOfOrdinalsAreOrdinals of (b -> z, a -> y)) + val zy = thenHave(forall(z, in(z, y) ==> Q(z))) by RightForall + have(ordinal(y) ==> (forall(z, in(z, y) ==> Q(z)) ==> Q(y))) by InstantiateForall + have(thesis) by Tautology.from(zy, lastStep) + } + + // contradiction + thenHave(yDef |- ()) by Tautology + thenHave(exists(y, yDef) |- ()) by LeftExists + have(() |- ()) by Cut(yExists, lastStep) + thenHave(thesis) by Restate + } + + val minimalOrdinalCounterexample = Theorem( + exists(x, ordinal(x) /\ !Q(x)) |- exists(x, ordinal(x) /\ !Q(x) /\ forall(y, in(y, x) ==> Q(y))) + ) { + have(thesis) by Restate.from(transfiniteInduction) + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala new file mode 100644 index 000000000..618d1fa26 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala @@ -0,0 +1,5 @@ +package lisa.maths.settheory.orderings + +object Orderings { + // export everything in this package +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala new file mode 100644 index 000000000..5dd1ed309 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala @@ -0,0 +1,338 @@ +package lisa.maths.settheory.orderings + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.orderings.InclusionOrders.* +import lisa.maths.settheory.orderings.PartialOrders.* +import lisa.maths.settheory.orderings.WellOrders.* + +object Ordinals extends lisa.Main { + + // var defs + private val w = variable + private val x = variable + private val y = variable + private val z = variable + private val h = formulaVariable + private val t = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + + // relation and function symbols + private val r = variable + private val p = variable + private val q = variable + private val f = variable + private val g = variable + private val F = function[1] + private val G = function[2] + + private val P = predicate[1] + private val Q = predicate[1] + private val schemPred = predicate[1] + + /** + * A set is an ordinal iff it is transitive ([[transitiveSet]]) and + * well-ordered ([[wellOrder]]) by inclusion. + * + * Since inclusion is not precisely a relation in the sense of set theory, the + * well-ordered clause is explicitly written. + */ + val ordinal = DEF(a) --> transitiveSet(a) /\ wellOrder(inclusionOrderOn(a)) + + /** + * Defining properties of the [[ordinal]] class + * + * - the [[emptySet]] is an ordinal --- [[emptySetOrdinal]] + * - if `a` is an ordinal and `b ∈ a`, then `b` is an ordinal --- [[ordinalInclusionClosure]] + * - if `a`, `b` are ordinals and `b ⊂ a`, then `b ∈ a` --- [[ordinalSubsetClosure]] + * - if `a` and `b` are distinct ordinals, then either `a ⊂ b` or `b ⊂ a` --- [[ordinalSOMETHING]] TODO: + * + * Other properties + * + * - the ordinals form a proper class --- [[noSetOfOrdinals]] + * - every subclass of the ordinals has a minimal element --- [[ordinalSubclassHasMinimalElement]] + */ + + /** + * Theorem --- the empty set is transitive. + */ + val emptySetTransitive = Lemma( + () |- transitiveSet(emptySet) + ) { + val hypo = have(!in(y, emptySet) |- in(y, emptySet) ==> subset(y, emptySet)) by Restate + have(() |- in(y, emptySet) ==> subset(y, emptySet)) by Cut(emptySetAxiom of (x -> y), hypo) + thenHave(() |- forall(y, in(y, emptySet) ==> subset(y, emptySet))) by RightForall + thenHave(thesis) by Substitution.ApplyRules(transitiveSet.definition) + } + + /** + * Theorem --- the empty set is well ordered by inclusion. + */ + val emptySetWellOrderedByInclusion = Lemma( + () |- wellOrder(inclusionOrderOn(emptySet)) + ) { + val incDef = have(inclusionOrderOn(emptySet) === pair(emptySet, inclusionOn(emptySet))) by InstantiateForall(inclusionOrderOn(emptySet))(inclusionOrderOn.definition of a -> emptySet) + have(wellOrder(pair(emptySet, inclusionOn(emptySet)))) by Substitution.ApplyRules(emptySetInclusionEmpty)(emptySetWellOrder) + thenHave(thesis) by Substitution.ApplyRules(incDef) + } + + /** + * Theorem --- the empty set is an ordinal (zero). + */ + val emptySetOrdinal = Theorem( + () |- ordinal(emptySet) + ) { + have(thesis) by Tautology.from(emptySetWellOrderedByInclusion, emptySetTransitive, ordinal.definition of (a -> emptySet)) + } + + val ordinalsHereditarilyTransitive = Lemma( + ordinal(a) |- transitiveSet(a) /\ forall(b, in(b, a) ==> transitiveSet(b)) + ) { + val ordinalTrans = have(ordinal(a) |- transitiveSet(a)) by Weakening(ordinal.definition) + val wellOrdInca = have(ordinal(a) |- wellOrder(inclusionOrderOn(a))) by Weakening(ordinal.definition) + have(inclusionOrderOn(a) === pair(a, inclusionOn(a))) by InstantiateForall(inclusionOrderOn(a))(inclusionOrderOn.definition) + val wellOrda = have(ordinal(a) |- wellOrder(pair(a, inclusionOn(a)))) by Substitution.ApplyRules(lastStep)(wellOrdInca) + + have(transitiveSet(a) |- forall(b, in(b, a) ==> subset(b, a))) by Weakening(transitiveSet.definition of x -> a) + val bIna = thenHave((transitiveSet(a), in(b, a)) |- subset(b, a)) by InstantiateForall(b) + have((transitiveSet(a), in(b, a)) |- forall(z, in(z, b) ==> in(z, a))) by Tautology.from(lastStep, subsetAxiom of (x -> b, y -> a)) + thenHave((transitiveSet(a), in(b, a)) |- in(z, b) ==> in(z, a)) by InstantiateForall(z) + val bcz = have((transitiveSet(a), in(b, a), in(z, b), in(c, z)) |- in(b, a) /\ in(c, a) /\ in(z, a)) by Tautology.from(lastStep, lastStep of (z -> c, b -> z)) + + val cInb = have((in(b, a), in(z, b), in(c, z), in(c, a), in(z, a), wellOrder(pair(a, inclusionOn(a)))) |- in(c, b)) subproof { + val bz = have(in(b, a) /\ in(z, a) /\ in(z, b) |- in(pair(z, b), inclusionOn(a))) by Weakening(inclusionOrderElem of (b -> z, c -> b)) + val zc = have(in(z, a) /\ in(c, a) /\ in(c, z) |- in(pair(c, z), inclusionOn(a))) by Weakening(inclusionOrderElem of (c -> z, b -> c)) + val bc = have(in(pair(c, b), inclusionOn(a)) |- in(b, a) /\ in(c, a) /\ in(c, b)) by Weakening(inclusionOrderElem of (c -> b, b -> c)) + + have(wellOrder(pair(a, inclusionOn(a))) |- forall(w, forall(y, forall(z, (in(pair(w, y), inclusionOn(a)) /\ in(pair(y, z), inclusionOn(a))) ==> in(pair(w, z), inclusionOn(a)))))) by Substitution + .ApplyRules(secondInPairReduction of (x -> a, y -> inclusionOn(a)))(wellOrderTransitivity of p -> pair(a, inclusionOn(a))) + thenHave(wellOrder(pair(a, inclusionOn(a))) |- forall(y, forall(z, (in(pair(c, y), inclusionOn(a)) /\ in(pair(y, z), inclusionOn(a))) ==> in(pair(c, z), inclusionOn(a))))) by InstantiateForall( + c + ) + thenHave(wellOrder(pair(a, inclusionOn(a))) |- forall(w, (in(pair(c, z), inclusionOn(a)) /\ in(pair(z, w), inclusionOn(a))) ==> in(pair(c, w), inclusionOn(a)))) by InstantiateForall(z) + thenHave(wellOrder(pair(a, inclusionOn(a))) |- (in(pair(c, z), inclusionOn(a)) /\ in(pair(z, b), inclusionOn(a))) ==> in(pair(c, b), inclusionOn(a))) by InstantiateForall(b) + + have(thesis) by Tautology.from(lastStep, bz, zc, bc) + } + + have((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a), in(z, b), in(c, z)) |- in(c, b)) by Tautology.from(bcz, cInb) + thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- (in(c, z) /\ in(z, b)) ==> in(c, b)) by Restate + thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- forall(z, (in(c, z) /\ in(z, b)) ==> in(c, b))) by RightForall + thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- forall(c, forall(z, (in(c, z) /\ in(z, b)) ==> in(c, b)))) by RightForall + thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- transitiveSet(b)) by Substitution.ApplyRules(transitiveSetInclusionDef of x -> b) + thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a)))) |- in(b, a) ==> transitiveSet(b)) by Restate + thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a)))) |- forall(b, in(b, a) ==> transitiveSet(b))) by RightForall + + have(thesis) by Tautology.from(lastStep, wellOrda, ordinalTrans) + } + + val elementsOfOrdinalsAreOrdinals = Theorem( + (ordinal(a), in(b, a)) |- ordinal(b) + ) { + assume(ordinal(a)) + assume(in(b, a)) + + // transitive :: + val transitiveB = have(transitiveSet(b)) subproof { + have(forall(b, in(b, a) ==> transitiveSet(b))) by Weakening(ordinalsHereditarilyTransitive) + thenHave(thesis) by InstantiateForall(b) + } + + // and well ordered by inclusion :: + + // what defines \in_b as a subset of \in_a? + // one direction (a ==> b) is sufficient here + val incAToB = have(in(y, b) /\ in(z, b) /\ in(pair(z, y), inclusionOn(a)) |- in(pair(z, y), inclusionOn(b))) subproof { + assume(in(y, b)) + assume(in(z, b)) + assume(in(pair(z, y), inclusionOn(a))) + + // instantiating definition of inclusion (a bit painful with assumes) + have(forall(z, (z === inclusionOn(a)) <=> forall(t, in(t, z) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))))) by Weakening(inclusionOn.definition) + thenHave(forall(t, in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(a)) + val incDefA = + thenHave(in(pair(z, y), inclusionOn(a)) <=> (in(pair(z, y), cartesianProduct(a, a)) /\ exists(d, exists(c, in(d, c) /\ (pair(z, y) === pair(d, c)))))) by InstantiateForall(pair(z, y)) + have(forall(z, (z === inclusionOn(b)) <=> forall(t, in(t, z) <=> (in(t, cartesianProduct(b, b)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))))) by Weakening( + inclusionOn.definition of a -> b + ) + thenHave(forall(t, in(t, inclusionOn(b)) <=> (in(t, cartesianProduct(b, b)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(b)) + val incDefB = + thenHave(in(pair(z, y), inclusionOn(b)) <=> (in(pair(z, y), cartesianProduct(b, b)) /\ exists(d, exists(c, in(d, c) /\ (pair(z, y) === pair(d, c)))))) by InstantiateForall(pair(z, y)) + + have(in(pair(z, y), cartesianProduct(b, b))) by Tautology.from(pairInCartesianProduct of (a -> z, b -> y, x -> b, y -> b)) + have(thesis) by Tautology.from(lastStep, incDefA, incDefB) + } + + val totalB = have(totalOrder(inclusionOrderOn(b))) subproof { + // the totality of \in_b follows from the totality of \in_a and the fact that \in_b does not exclude any elements of b + val totA = have(totalOrder(inclusionOrderOn(a))) by Tautology.from(ordinal.definition, wellOrder.definition of p -> inclusionOrderOn(a)) + + val totalDef = have(totalOrder(p) <=> (partialOrder(p) /\ total(secondInPair(p), firstInPair(p)))) by Weakening(totalOrder.definition) + + // \in_b is a partial order + val inBPartial = have(partialOrder(inclusionOrderOn(b))) by Tautology.from(inclusionOnTransitiveSetIsPartialOrder of a -> b, transitiveB) + + // \in_b is total as a homogeneous relation on b + val inBTotal = have(total(secondInPair(inclusionOrderOn(b)), firstInPair(inclusionOrderOn(b)))) subproof { + val totB = have(total(inclusionOn(b), b)) subproof { + have(forall(z, (z === inclusionOrderOn(a)) <=> (z === pair(a, inclusionOn(a))))) by Weakening(inclusionOrderOn.definition) + val incEq = thenHave(inclusionOrderOn(a) === pair(a, inclusionOn(a))) by InstantiateForall(inclusionOrderOn(a)) + have(total(secondInPair(inclusionOrderOn(a)), firstInPair(inclusionOrderOn(a)))) by Tautology.from(totalDef of p -> inclusionOrderOn(a), totA) + thenHave(total(secondInPair(pair(a, inclusionOn(a))), firstInPair(pair(a, inclusionOn(a))))) by Substitution.ApplyRules(incEq) + val totIncA = + thenHave(total(inclusionOn(a), a)) by Substitution.ApplyRules(secondInPairReduction of (x -> a, y -> inclusionOn(a)), firstInPairReduction of (x -> a, y -> inclusionOn(a))) + + val totRelDef = + have(total(r, x) <=> (relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(y, x) /\ in(z, x)) ==> (in(pair(y, z), r) \/ in(pair(z, y), r) \/ (y === z)))))) by Weakening(total.definition) + + // need to show + // \forall y, z \in b. y \in_b z \/ z \in_b y \/ (z = y) + // y, z \in b ==> y, z \in a + // y, z \in a ==> y \in_a z \/ z \in_a y \/ (z = y) + // but each of these imply a literal above + // done + have(total(inclusionOn(a), a) |- (in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z))) subproof { + assume(total(inclusionOn(a), a)) + assume(in(y, b)) + assume(in(z, b)) + + have(forall(y, in(y, a) ==> subset(y, a))) by Tautology.from(ordinal.definition, transitiveSet.definition of x -> a) + thenHave(in(b, a) ==> subset(b, a)) by InstantiateForall(b) + have(forall(x, in(x, b) ==> in(x, a))) by Tautology.from(lastStep, subsetAxiom of (x -> b, y -> a)) + thenHave(in(x, b) ==> in(x, a)) by InstantiateForall(x) + val yza = have(in(y, a) /\ in(z, a)) by Tautology.from(lastStep of x -> y, lastStep of x -> z) + + have(forall(y, forall(z, (in(y, a) /\ in(z, a)) ==> (in(pair(y, z), inclusionOn(a)) \/ in(pair(z, y), inclusionOn(a)) \/ (y === z))))) by Tautology.from( + totRelDef of (r -> inclusionOn(a), x -> a) + ) + thenHave((in(y, a) /\ in(z, a)) ==> (in(pair(y, z), inclusionOn(a)) \/ in(pair(z, y), inclusionOn(a)) \/ (y === z))) by InstantiateForall(y, z) + have((in(pair(y, z), inclusionOn(a)) \/ in(pair(z, y), inclusionOn(a)) \/ (y === z))) by Tautology.from(lastStep, yza) + + have(thesis) by Tautology.from(lastStep, incAToB, incAToB of (y -> z, z -> y)) + } + + have((in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z))) by Cut(totIncA, lastStep) + thenHave(forall(z, (in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z)))) by RightForall + thenHave(forall(y, forall(z, (in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z))))) by RightForall + + have(thesis) by Tautology.from(lastStep, inclusionIsRelation of a -> b, totRelDef of (r -> inclusionOn(b), x -> b)) + } + + have(forall(z, (z === inclusionOrderOn(b)) <=> (z === pair(b, inclusionOn(b))))) by Weakening(inclusionOrderOn.definition of a -> b) + val incEq = thenHave(inclusionOrderOn(b) === pair(b, inclusionOn(b))) by InstantiateForall(inclusionOrderOn(b)) + + have(secondInPair(pair(b, inclusionOn(b))) === inclusionOn(b)) by Weakening(secondInPairReduction of (x -> b, y -> inclusionOn(b))) + val snd = thenHave(secondInPair(inclusionOrderOn(b)) === inclusionOn(b)) by Substitution.ApplyRules(incEq) + have(firstInPair(pair(b, inclusionOn(b))) === (b)) by Weakening(firstInPairReduction of (x -> b, y -> inclusionOn(b))) + val fst = thenHave(firstInPair(inclusionOrderOn(b)) === (b)) by Substitution.ApplyRules(incEq) + + have(thesis) by Substitution.ApplyRules(snd, fst)(totB) + } + + have(totalOrder(inclusionOrderOn(b)) <=> (partialOrder(inclusionOrderOn(b)) /\ total(secondInPair(inclusionOrderOn(b)), firstInPair(inclusionOrderOn(b))))) by Weakening( + totalDef of p -> inclusionOrderOn(b) + ) + have(thesis) by Tautology.from(lastStep, inBPartial, inBTotal) + } + + val woProp = have(forall(c, (subset(c, b) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))))) subproof { + // painful expansion + // subset c b ==> subset c a + have(forall(y, in(y, a) ==> subset(y, a))) by Tautology.from(ordinal.definition, transitiveSet.definition of x -> a) + thenHave(in(b, a) ==> subset(b, a)) by InstantiateForall(b) + thenHave(subset(b, a)) by Restate + have(subset(c, b) |- subset(c, a)) by Tautology.from(lastStep, subsetTransitivity of (a -> c, c -> a)) + val bToA = thenHave(subset(c, b) /\ !(c === emptySet) |- subset(c, a) /\ !(c === emptySet)) by Tautology + + have(forall(z, (z === inclusionOrderOn(a)) <=> (z === pair(a, inclusionOn(a))))) by Weakening(inclusionOrderOn.definition) + val incDef = thenHave(inclusionOrderOn(a) === pair(a, inclusionOn(a))) by InstantiateForall(inclusionOrderOn(a)) + + // so there exists a minimal element wrt a + have( + forall( + c, + (subset(c, firstInPair(inclusionOrderOn(a))) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(inclusionOrderOn(a))) \/ (z === y)))) + ) + ) by Tautology.from(ordinal.definition, wellOrder.definition of p -> inclusionOrderOn(a)) + thenHave( + forall( + c, + (subset(c, firstInPair(pair(a, inclusionOn(a)))) /\ !(c === emptySet)) ==> exists( + z, + in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(pair(a, inclusionOn(a)))) \/ (z === y))) + ) + ) + ) by Substitution.ApplyRules(incDef) + thenHave(forall(c, (subset(c, a) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y)))))) by Substitution.ApplyRules( + firstInPairReduction of (x -> a, y -> inclusionOn(a)), + secondInPairReduction of (x -> a, y -> inclusionOn(a)) + ) + val caWO = thenHave((subset(c, a) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))))) by InstantiateForall(c) + + // but if this element is minimal wrt \in_a, it is minimal wrt \in_b as well + have( + (subset(c, b), exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))))) |- exists( + z, + in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y))) + ) + ) subproof { + assume(subset(c, b)) + val subCB = have(forall(x, in(x, c) ==> in(x, b))) by Tautology.from(subsetAxiom of (x -> c, y -> b)) + val yb = have(in(y, c) ==> in(y, b)) by InstantiateForall(y)(subCB) + val zb = have(in(z, c) ==> in(z, b)) by InstantiateForall(z)(subCB) + + have(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y)))) by Restate + thenHave(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) by InstantiateForall(y) + have(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y))) by Tautology.from( + lastStep, + incAToB, + yb, + zb + ) + thenHave(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))) by RightForall + thenHave(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))) by Tautology + thenHave( + in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))) + ) by RightExists + thenHave(thesis) by LeftExists + } + + have((subset(c, b) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y))))) by Tautology.from(lastStep, caWO, bToA) + thenHave(thesis) by RightForall + } + + val wo = have(wellOrder(inclusionOrderOn(b))) subproof { + have(forall(z, (z === inclusionOrderOn(b)) <=> (z === pair(b, inclusionOn(b))))) by Weakening(inclusionOrderOn.definition of a -> b) + val incDef = thenHave(inclusionOrderOn(b) === pair(b, inclusionOn(b))) by InstantiateForall(inclusionOrderOn(b)) + + have( + forall( + c, + (subset(c, firstInPair(pair(b, inclusionOn(b)))) /\ !(c === emptySet)) ==> exists( + z, + in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(pair(b, inclusionOn(b)))) \/ (z === y))) + ) + ) + ) by Substitution.ApplyRules(firstInPairReduction of (x -> b, y -> inclusionOn(b)), secondInPairReduction of (x -> b, y -> inclusionOn(b)))(woProp) + thenHave( + forall( + c, + (subset(c, firstInPair(inclusionOrderOn(b))) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(inclusionOrderOn(b))) \/ (z === y)))) + ) + ) by Substitution.ApplyRules(incDef) + have(thesis) by Tautology.from(lastStep, totalB, wellOrder.definition of p -> inclusionOrderOn(b)) + } + + have(thesis) by Tautology.from(wo, transitiveB, ordinal.definition of (a -> b)) + } + + val ordinalSubclassHasMinimalElement = Lemma( + forall(x, P(x) ==> ordinal(x)) /\ exists(x, P(x)) |- exists(y, P(y) /\ ordinal(y) /\ forall(x, P(x) ==> in(y, x))) + ) { + sorry + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala new file mode 100644 index 000000000..eb45f1d5f --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala @@ -0,0 +1,356 @@ +package lisa.maths.settheory.orderings + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.functions.* + +object PartialOrders extends lisa.Main { + + // var defs + private val w = variable + private val x = variable + private val y = variable + private val z = variable + private val h = formulaVariable + private val t = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + + // relation and function symbols + private val r = variable + private val p = variable + private val q = variable + private val f = variable + private val g = variable + private val F = function[1] + private val G = function[2] + + private val P = predicate[1] + private val Q = predicate[1] + private val schemPred = predicate[1] + + /** + * Linear and Partial Ordering + */ + + /** + * (Strict) Partial Order --- `p` is a partial order on `x` if it is a pair `(x, r)`, + * and `r` is an [[antiReflexive]], [[antiSymmetric]], and [[transitive]] binary + * [[relation]] on `x`. + */ + val partialOrder = + DEF(p) --> relationBetween(secondInPair(p), firstInPair(p), firstInPair(p)) /\ antiSymmetric(secondInPair(p), firstInPair(p)) /\ antiReflexive(secondInPair(p), firstInPair(p)) /\ transitive( + secondInPair(p), + firstInPair(p) + ) + + /** + * Linear Order --- a partial order `p = (r, x)` is called a linear order if + * `r` is [[total]] as a [[relation]] on `x`. + */ + val totalOrder = DEF(p) --> partialOrder(p) /\ total(secondInPair(p), firstInPair(p)) + + /** + * Properties of elements under partial orders + */ + + /** + * Maximal Element --- `a` is a maximal element of `y` with respect to `r`, + * where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. + * + * `∀ b ∈ y. ! a r b` + */ + val maximalElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (!in(pair(a, b), secondInPair(p)))) + + /** + * Minimal Element --- `a` is a minimal element of `y` with respect to `r`, + * where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. + * + * `∀ b ∈ y. ! b r a` + */ + val minimalElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (!in(pair(b, a), secondInPair(p)))) + + /** + * Greatest Element --- `a` is the greatest element of `y` with respect to + * `r`, where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. + * + * `∀ b ∈ y. b r a ⋁ b = a` + */ + val greatestElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (in(pair(b, a), secondInPair(p)) \/ (a === b))) + + /** + * Least Element --- `a` is the least element of `y` with respect to `r`, + * where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. + * + * `∀ b ∈ y. a r b ⋁ b = a` + */ + val leastElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (in(pair(a, b), secondInPair(p)) \/ (a === b))) + + /** + * Upper Bound --- `a` is an upper bound on `y` with respect to `r`, where `p + * = (r, x)` is a partial order on `x`, and `y ⊆ x`. + * + * `∀ b ∈ y. b r a ⋁ b = a` + * + * Note that as opposed to the greatest element, `a` is not enforced to be an + * element of `y`. + */ + val upperBound = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ ∀(b, in(b, y) ==> (in(pair(b, a), secondInPair(p)) \/ (a === b))) + + /** + * Lower Bound --- `a` is a lower bound on `y` with respect to `r`, where `p = + * (r, x)` is a partial order on `x`, and `y ⊆ x`. + * + * `∀ b ∈ y. a r b ⋁ b = a` + * + * Note that as opposed to the least element, `a` is not enforced to be an + * element of `y` + */ + val lowerBound = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ ∀(b, in(b, y) ==> (in(pair(a, b), secondInPair(p)) \/ (a === b))) + + val setOfLowerBoundsUniqueness = Theorem( + () |- ∃!(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ lowerBound(t, y, p)))) + ) { + have(thesis) by UniqueComprehension(secondInPair(p), lambda(t, lowerBound(t, y, p))) + } + + /** + * The set of all lower bounds of a set `y` under a partial order `p`. Used to define [[greatestLowerBound]] + */ + val setOfLowerBounds = DEF(y, p) --> The(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ lowerBound(t, y, p))))(setOfLowerBoundsUniqueness) + + /** + * Greatest Lower Bound --- `a` is the greatest lower bound on `y ⊆ x` + * under a partial order `p = (r, x)` if it is the greatest element in the + * [[setOfLowerBounds]] of `y` under `p`. + */ + val greatestLowerBound = DEF(a, y, p) --> greatestElement(a, setOfLowerBounds(y, p), p) + + /** + * Alias for [[greatestLowerBound]] + */ + val infimum = greatestLowerBound + + val setOfUpperBoundsUniqueness = Theorem( + () |- ∃!(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ upperBound(t, y, p)))) + ) { + have(thesis) by UniqueComprehension(secondInPair(p), lambda(t, upperBound(t, y, p))) + } + + /** + * The set of all upper bounds of a set `y` under a partial order `p`. Used to define [[leastUpperBound]] + */ + val setOfUpperBounds = DEF(y, p) --> The(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ upperBound(t, y, p))))(setOfUpperBoundsUniqueness) + + /** + * Least Upper Bound --- `a` is the least upper bound on `y ⊆ x` under + * a partial order `p = (r, x)` if it is the least element in the + * [[setOfUpperBounds]] of `y` under `p`. + */ + val greatestUpperBound = DEF(a, y, p) --> leastElement(a, setOfUpperBounds(y, p), p) + + /** + * Alias for [[greatestUpperBound]] + */ + val supremum = greatestUpperBound + + val predecessor = DEF(p, x, y) --> totalOrder(p) /\ in(x, firstInPair(p)) /\ in(y, firstInPair(p)) /\ in(pair(x, y), secondInPair(p)) /\ forall( + z, + !(in(pair(x, z), secondInPair(p)) /\ in(pair(z, y), secondInPair(p))) + ) + + val limitElement = DEF(p, x) --> totalOrder(p) /\ in(x, firstInPair(p)) /\ !exists(y, predecessor(p, y, x)) + + val successorElement = DEF(p, x) --> totalOrder(p) /\ in(x, firstInPair(p)) /\ exists(y, predecessor(p, y, x)) + + val everyElemInTotalOrderLimitOrSuccessor = Lemma( + totalOrder(p) /\ in(x, firstInPair(p)) |- (limitElement(p, x) \/ successorElement(p, x)) + ) { + // limit and successor are just negation of each other + have(thesis) by Tautology.from(successorElement.definition, limitElement.definition) + } + + val initialSegmentUnionForLimitElementsIsComplete = Lemma( + totalOrder(p) /\ limitElement(p, x) |- in(pair(t, x), secondInPair(p)) <=> exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p))) + ) { + assume(totalOrder(p)) + assume(limitElement(p, x)) + + val p1 = firstInPair(p) + val p2 = secondInPair(p) + + val fwd = have(in(pair(t, x), p2) |- exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2))) subproof { + assume(in(pair(t, x), p2)) + assume(forall(y, !(in(pair(t, y), p2) /\ in(pair(y, x), p2)))) // assume negated + + have(forall(y, !predecessor(p, y, x))) by Tautology.from(limitElement.definition) + thenHave(!predecessor(p, t, x)) by InstantiateForall(t) + val notInp1 = have(!in(t, p1)) by Tautology.from(lastStep, limitElement.definition, predecessor.definition of (x -> t, y -> x)) // y is free here, so instantiate it to x + + val inst = have(!(in(pair(t, y), p2) /\ in(pair(y, x), p2))) by InstantiateForall + + have(in(t, p1)) subproof { + have(relationBetween(p2, p1, p1)) by Tautology.from(totalOrder.definition, partialOrder.definition) + have(subset(p2, cartesianProduct(p1, p1))) by Tautology.from(lastStep, relationBetween.definition of (r -> p2, a -> p1, b -> p1)) + have(forall(z, in(z, p2) ==> in(z, cartesianProduct(p1, p1)))) by Tautology.from(lastStep, subsetAxiom of (x -> p2, y -> cartesianProduct(p1, p1))) + thenHave(in(pair(t, x), cartesianProduct(p1, p1))) by InstantiateForall(pair(t, x)) + have(in(t, p1)) by Tautology.from(lastStep, pairInCartesianProduct of (a -> t, b -> x, x -> p1, y -> p1)) + } + + have(bot) by Tautology.from(lastStep, notInp1) + } + + val bwd = have(exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)) |- in(pair(t, x), p2)) subproof { + have(in(pair(t, y), p2) /\ in(pair(y, x), p2) |- in(pair(t, x), p2)) subproof { + // total orders are transitive + have(forall(t, forall(y, forall(x, (in(pair(t, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(t, x), p2))))) by Tautology.from( + totalOrder.definition, + partialOrder.definition, + transitive.definition of (r -> p2, x -> p1) + ) + thenHave(thesis) by InstantiateForall(t, y, x) + } + + thenHave(thesis) by LeftExists + } + + have(thesis) by Tautology.from(fwd, bwd) + } + show + + val initialSegmentUnionForSuccessorElementsIsIncomplete = Lemma( + totalOrder(p) /\ successorElement(p, x) |- in(pair(t, x), secondInPair(p)) <=> (predecessor(p, t, x) \/ exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p)))) + ) { + assume(totalOrder(p)) + assume(successorElement(p, x)) + + val p1 = firstInPair(p) + val p2 = secondInPair(p) + + val fwd = have(in(pair(t, x), p2) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)))) subproof { + assume(in(pair(t, x), p2)) + + // t < x means t, x \in p1 + val txInp1 = have(in(t, p1) /\ in(x, p1)) subproof { + have(relationBetween(p2, p1, p1)) by Tautology.from(totalOrder.definition, partialOrder.definition) + have(subset(p2, cartesianProduct(p1, p1))) by Tautology.from(lastStep, relationBetween.definition of (r -> p2, a -> p1, b -> p1)) + have(forall(z, in(z, p2) ==> in(z, cartesianProduct(p1, p1)))) by Tautology.from(lastStep, subsetAxiom of (x -> p2, y -> cartesianProduct(p1, p1))) + thenHave(in(pair(t, x), cartesianProduct(p1, p1))) by InstantiateForall(pair(t, x)) + have(thesis) by Tautology.from(lastStep, pairInCartesianProduct of (a -> t, b -> x, x -> p1, y -> p1)) + } + + have(predecessor(p, y, x) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p))))) subproof { + assume(predecessor(p, y, x)) + + have(forall(z, !(in(pair(y, z), p2) /\ in(pair(z, x), p2)))) by Tautology.from(predecessor.definition of (x -> y, y -> x)) + thenHave(!(in(pair(y, t), p2) /\ in(pair(t, x), p2))) by InstantiateForall(t) + val yNLTt = thenHave(!in(pair(y, t), p2)) by Tautology + + have(forall(y, forall(t, (in(y, p1) /\ in(t, p1)) ==> (in(pair(y, t), p2) \/ in(pair(t, y), p2) \/ (y === t))))) by Tautology.from( + totalOrder.definition, + total.definition of (r -> p2, x -> p1) + ) + thenHave((in(y, p1) /\ in(t, p1)) ==> (in(pair(y, t), p2) \/ in(pair(t, y), p2) \/ (y === t))) by InstantiateForall(y, t) + val cases = have(in(pair(t, y), p2) \/ (y === t)) by Tautology.from(lastStep, predecessor.definition of (x -> y, y -> x), txInp1, yNLTt) + + val ltCase = have(in(pair(t, y), p2) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)))) subproof { + have(in(pair(t, y), p2) |- in(pair(t, y), p2) /\ in(pair(y, x), p2)) by Tautology.from(predecessor.definition of (x -> y, y -> x)) + thenHave(in(pair(t, y), p2) |- exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2))) by RightExists + thenHave(thesis) by Weakening + } + + val eqCase = have((y === t) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)))) subproof { + have(predecessor(p, y, x)) by Restate + thenHave((y === t) |- predecessor(p, t, x)) by Substitution.ApplyRules(y === t) + thenHave(thesis) by Weakening + } + + have(thesis) by Tautology.from(cases, ltCase, eqCase) + } + + thenHave(exists(y, predecessor(p, y, x)) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p))))) by LeftExists + have(thesis) by Tautology.from(lastStep, successorElement.definition) + } + + val bwd = have((predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2))) |- in(pair(t, x), p2)) subproof { + val predCase = have(predecessor(p, t, x) |- in(pair(t, x), p2)) by Tautology.from(predecessor.definition of (x -> t, y -> x)) + have(in(pair(t, y), p2) /\ in(pair(y, x), p2) |- in(pair(t, x), p2)) subproof { + // transitivity of p + have(forall(t, forall(y, forall(x, (in(pair(t, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(t, x), p2))))) by Tautology.from( + totalOrder.definition, + partialOrder.definition, + transitive.definition of (r -> p2, x -> p1) + ) + thenHave(thesis) by InstantiateForall(t, y, x) + } + thenHave(exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)) |- in(pair(t, x), p2)) by LeftExists + + have(thesis) by LeftOr(lastStep, predCase) + } + + have(thesis) by Tautology.from(fwd, bwd) + } + show + + /** + * Properties of functions under partial orders + */ + + /** + * Order Preserving Function --- a function `f` between `P` and `Q` such that + * `p = (P, <_p)` and `q = (Q, <_q)` are partially ordered is order-preserving + * if + * + * `∀ x y. x <_p y ⟹ f(x) <_q f(y)` + */ + val orderPreserving = DEF(f, p, q) --> partialOrder(p) /\ partialOrder(q) /\ functionFrom(f, firstInPair(p), firstInPair(q)) /\ ∀( + x, + ∀(y, in(pair(x, y), secondInPair(p)) ==> in(pair(app(f, x), app(f, y)), secondInPair(q))) + ) + + /** + * Increasing Function --- an order preserving function ([[orderPreserving]]) + * between two partially ordered sets is increasing if the two sets are + * linearly ordered ([[totalOrder]]). + */ + val increasing = DEF(f, p, q) --> totalOrder(p) /\ totalOrder(q) /\ orderPreserving(f, p, q) + + /** + * Isomorphism of Partially Ordered Sets --- a function `f` is an isomorphism + * between two partially ordered sets `p = (P, <_p)` and `q = (Q, <_q)` if it + * is an [[injective]] function from `P` to `Q`, and both `f` and `f^-1` are + * [[orderPreserving]]. + */ + // val isomorphismOfPartialOrders = DEF (f, p, q) --> injective(f, firstInPair(p), firstInPair(q)) /\ orderPreserving(f, p, q) /\ orderPreserving(inverseFunction(f), p, q) + + private val pA = variable // order + private val pB = variable // order + val orderIsomorphism = DEF(pA, pB, f) --> { + val A = firstInPair(pA) + val B = firstInPair(pB) + val ` ∀( + y, + in(y, A) ==> + (in(pair(x, y), ` in(pair(app(f, x), app(f, y)), ` (app(g, a) === F(orderedRestriction(g, a, p))))) + def prop(t: Term): Formula = in(t, p1) /\ existsOne(g, fun(g, t)) + + // Lemmas: + + /** + * Theorem --- Unique Recursive Function + * + * If a theorem as defined by [[wellOrderedRecursion]] exists, it is unique + */ + val uniqueRecursiveFunction = Lemma( + wellOrder(p) /\ exists(g, fun(g, t)) /\ in(t, p1) |- existsOne(g, fun(g, t)) + ) { + assume(wellOrder(p)) + assume(in(t, p1)) + + // pt is a well order over t, which is needed for induction + val pt = pair(initialSegment(p, t), initialSegmentOrder(p, t)) + val ptWO = have(wellOrder(pt)) by Weakening(initialSegmentWellOrdered of a -> t) + + // suppose there exist two such distinct functions g1 and g2 + val g1 = variable + val g2 = variable + + // expansion of ordered restriction + val ordResDef = have(orderedRestriction(g, z, p) === restrictedFunction(g, initialSegment(p, z))) subproof { + have(forall(b, (b === orderedRestriction(g, z, p)) <=> (b === restrictedFunction(g, initialSegment(p, z))))) by Weakening(orderedRestriction.definition of (f -> g, a -> z)) + thenHave(thesis) by InstantiateForall(orderedRestriction(g, z, p)) + } + + // if g1 and g2 agree on the initial segment of an element < z, they must agree on z + val initToz = have( + fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) /\ in(z, initialSegment(p, t)) /\ forall( + b, + in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b)) + ) |- (app(g1, z) === app(g2, z)) + ) subproof { + assume(fun(g1, t)) + assume(fun(g2, t)) + assume(!(g1 === g2)) + assume(in(z, initialSegment(p, t))) + assume(forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b)))) + + // the ordered restriction of g1 has domain initialSegment(p, z) + // it is functional, too + val restrictionIsFunction = have(fun(g, t) |- functionalOver(orderedRestriction(g, z, p), initialSegment(p, z))) subproof { + assume(fun(g, t)) + + // g_z has dom g, x -> initialSegment(p, z)), + functionalOver.definition of (f -> g, x -> initialSegment(p, t)) + ) + thenHave(thesis) by Substitution.ApplyRules(ordResDef) + } + + // but dom g is g, x -> initialSegment(p, t))) + + have(thesis) by Substitution.ApplyRules(lastStep)(domrestriction) + } + + // forall(b, in(b, z) <=> (in(b, p1) /\ in(pair(b, t), p2))))) by Weakening(initialSegment.definition of a -> t) + thenHave(forall(b, in(b, initialSegment(p, t)) <=> (in(b, p1) /\ in(pair(b, t), p2)))) by InstantiateForall(initialSegment(p, t)) + thenHave(in(z, initialSegment(p, t)) <=> (in(z, p1) /\ in(pair(z, t), p2))) by InstantiateForall(z) + val zLTt = thenHave(in(pair(z, t), p2)) by Tautology + + have(partialOrder(p)) by Tautology.from(wellOrder.definition, totalOrder.definition) + + have(thesis) by Tautology.from(lastStep, zLTt, initialSegmentsSubset of (x -> z, y -> t), pIsAPartialOrder) + } + + // so dom g = initialSegment(p, z), y -> initialSegment(p, t)) + ) + + have(thesis) by Substitution.ApplyRules(lastStep)(domgz) + } + + // the double initial segment is redundant + val initPTEqual = have(initialSegment(pt, z) === initialSegment(p, z)) subproof { + + // expand defs + have(forall(z, (z === initialSegment(x, y)) <=> forall(t, in(t, z) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x)))))) by Weakening( + initialSegment.definition of (p -> x, a -> y) + ) + thenHave(forall(t, in(t, initialSegment(x, y)) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x))))) by InstantiateForall(initialSegment(x, y)) + val initXY = thenHave(in(c, initialSegment(x, y)) <=> (in(c, firstInPair(x)) /\ in(pair(c, y), secondInPair(x)))) by InstantiateForall(c) + + // forward + val fwd = have(in(b, initialSegment(pt, z)) |- in(b, initialSegment(p, z))) subproof { + assume(in(b, initialSegment(pt, z))) + + have(in(b, firstInPair(pt))) by Tautology.from(initXY of (x -> pt, y -> z, c -> b)) + val bpt = thenHave(in(b, initialSegment(p, t))) by Substitution.ApplyRules(firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) + have(in(b, initialSegment(p, t)) ==> in(b, p1)) by Tautology.from(initXY of (x -> p, y -> t, c -> b)) + val bInP1 = have(in(b, p1)) by Tautology.from(lastStep, bpt) + + val bzInP2 = have(in(pair(b, z), p2)) subproof { + have(in(z, initialSegment(p, t))) by Restate + val zt = have(in(pair(z, t), p2)) by Tautology.from(lastStep, initXY of (x -> p, y -> t, c -> z)) + + have(in(pair(b, z), secondInPair(pt))) by Tautology.from(initXY of (x -> pt, y -> z, c -> b)) + val bzpt = thenHave(in(pair(b, z), initialSegmentOrder(p, t))) by Substitution.ApplyRules(secondInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) + + have(thesis) subproof { + have( + forall( + z, + (z === initialSegmentOrder(p, t)) <=> forall(a, in(a, z) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) + ) + ) by Weakening(initialSegmentOrder.definition of a -> t) + thenHave( + forall(a, in(a, initialSegmentOrder(p, t)) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) + ) by InstantiateForall(initialSegmentOrder(p, t)) + thenHave( + in(pair(b, z), initialSegmentOrder(p, t)) <=> (in(pair(b, z), secondInPair(p)) /\ (in(firstInPair(pair(b, z)), initialSegment(p, t)) /\ in( + secondInPair(pair(b, z)), + initialSegment(p, t) + ))) + ) by InstantiateForall(pair(b, z)) + have(thesis) by Tautology.from(lastStep, bzpt) + } + } + + have(thesis) by Tautology.from(bInP1, bzInP2, initXY of (x -> p, y -> z, c -> b)) + } + + // backward + val bwd = have(in(b, initialSegment(p, z)) |- in(b, initialSegment(pt, z))) subproof { + assume(in(b, initialSegment(p, z))) + + val bpt = have(in(b, initialSegment(p, t))) subproof { + val bInP1 = have(in(b, p1)) by Tautology.from(initXY of (x -> p, y -> z, c -> b)) + + val bz = have(in(pair(b, z), p2)) by Tautology.from(initXY of (x -> p, y -> z, c -> b)) + val zt = have(in(pair(z, t), p2)) by Tautology.from(initXY of (x -> p, y -> t, c -> z)) + + have(forall(w, forall(y, forall(z, (in(pair(w, y), p2) /\ in(pair(y, z), p2)) ==> in(pair(w, z), p2))))) by Weakening(wellOrderTransitivity) + thenHave((in(pair(b, z), p2) /\ in(pair(z, t), p2)) ==> in(pair(b, t), p2)) by InstantiateForall(b, z, t) + + have(in(pair(b, t), p2)) by Tautology.from(lastStep, bz, zt) + have(thesis) by Tautology.from(lastStep, bInP1, initXY of (x -> p, y -> t, c -> b)) + } + + val bzInP2 = have(in(pair(b, z), initialSegmentOrder(p, t))) subproof { + have( + forall( + z, + (z === initialSegmentOrder(p, t)) <=> forall(a, in(a, z) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) + ) + ) by Weakening(initialSegmentOrder.definition of a -> t) + thenHave( + forall(a, in(a, initialSegmentOrder(p, t)) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) + ) by InstantiateForall(initialSegmentOrder(p, t)) + thenHave( + in(pair(b, z), initialSegmentOrder(p, t)) <=> (in(pair(b, z), secondInPair(p)) /\ (in(firstInPair(pair(b, z)), initialSegment(p, t)) /\ in( + secondInPair(pair(b, z)), + initialSegment(p, t) + ))) + ) by InstantiateForall(pair(b, z)) + val ordDef = thenHave(in(pair(b, z), initialSegmentOrder(p, t)) <=> (in(pair(b, z), secondInPair(p)) /\ (in(b, initialSegment(p, t)) /\ in(z, initialSegment(p, t))))) by Substitution + .ApplyRules(firstInPairReduction of (x -> b, y -> z), secondInPairReduction of (x -> b, y -> z)) + + val bz = have(in(pair(b, z), p2)) by Tautology.from(initXY of (x -> p, y -> z, c -> b)) + have(thesis) by Tautology.from(ordDef, bz, bpt) + } + + have(in(b, initialSegment(pt, z)) <=> (in(b, initialSegment(p, t)) /\ in(pair(b, z), initialSegmentOrder(p, t)))) by Substitution.ApplyRules( + firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t)), + secondInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t)) + )(initXY of (x -> pt, y -> z, c -> b)) + have(thesis) by Tautology.from(lastStep, bpt, bzInP2) + } + + // combine + have(in(b, initialSegment(p, z)) <=> in(b, initialSegment(pt, z))) by Tautology.from(fwd, bwd) + thenHave(forall(b, in(b, initialSegment(p, z)) <=> in(b, initialSegment(pt, z)))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> initialSegment(p, z), y -> initialSegment(pt, z))) + } + + // on the restricted domain, app(orderedRestriction(g, z, p), b) = app(g, b) + val ordApp = have(forall(b, in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g, z, p), b) === app(g, b)))) subproof { + // b < z ==> g_z(b) = g(b) + val bToApp = have(in(b, initialSegment(p, z)) ==> (app(orderedRestriction(g, z, p), b) === app(g, b))) subproof { + have(in(b, initialSegment(p, z)) ==> (app(restrictedFunction(g, initialSegment(p, z)), b) === app(g, b))) by Tautology.from( + restrictedFunctionApplication of (f -> g, x -> initialSegment(p, z), y -> b) + ) + thenHave(thesis) by Substitution.ApplyRules(ordResDef) + } + + // b <_t z ==> b < z + val btTobz = have(in(b, initialSegment(pt, z)) ==> in(b, initialSegment(p, z))) subproof { + have(in(b, initialSegment(pt, z)) ==> in(b, initialSegment(pt, z))) by Restate + thenHave(thesis) by Substitution.ApplyRules(initPTEqual) + } + + // so b <_t z ==> g_z(b) = g(b) + have(in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g, z, p), b) === app(g, b))) by Tautology.from(bToApp, btTobz) + + // quantify + thenHave(thesis) by RightForall + } + + // for every element in the restricted domain, g1_z(b) = g2_z(b) + val eqOnDom = have(forall(b, in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g1, z, p), b) === app(orderedRestriction(g2, z, p), b)))) subproof { + val unquantified = have(in(b, initialSegment(pt, z)) |- (app(orderedRestriction(g1, z, p), b) === app(orderedRestriction(g2, z, p), b))) subproof { + assume(in(b, initialSegment(pt, z))) + + val instOrd = have((app(orderedRestriction(g, z, p), b) === app(g, b))) by InstantiateForall(b)(ordApp) + + val eqTg2zg1 = equalityTransitivity of (x -> app(orderedRestriction(g2, z, p), b), z -> app(orderedRestriction(g1, z, p), b), y -> app(g1, b)) + val eqTg1g2 = equalityTransitivity of (x -> app(orderedRestriction(g2, z, p), b), y -> app(g2, b), z -> app(g1, b)) + + have(in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) by InstantiateForall + thenHave(app(g1, b) === app(g2, b)) by Tautology + have(thesis) by Tautology.from(lastStep, instOrd of g -> g1, instOrd of g -> g2, eqTg2zg1, eqTg1g2) + } + + thenHave(in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g1, z, p), b) === app(orderedRestriction(g2, z, p), b))) by Restate + thenHave(thesis) by RightForall + } + + // but then g1_z = g2_z + val orderedRestrictionsEqual = have(orderedRestriction(g1, z, p) === orderedRestriction(g2, z, p)) subproof { + have(fun(g, t) |- functionalOver(orderedRestriction(g, z, p), initialSegment(p, z))) by Restate.from(restrictionIsFunction) + + // but initialSegment pt z = initialSegment p z + val fung = thenHave(fun(g, t) |- functionalOver(orderedRestriction(g, z, p), initialSegment(pt, z))) by Substitution.ApplyRules(initPTEqual) + + have(thesis) by Tautology.from( + fung of g -> g1, + fung of g -> g2, + eqOnDom, + functionsEqualIfEqualOnDomain of (f -> orderedRestriction(g1, z, p), g -> orderedRestriction(g2, z, p), a -> initialSegment(pt, z)) + ) + } + + // and thus F(g1_z) = F(g2_z) + val fg1g2eq = have(F(orderedRestriction(g1, z, p)) === F(orderedRestriction(g2, z, p))) subproof { + have(F(orderedRestriction(g1, z, p)) === F(orderedRestriction(g1, z, p))) by Restate + thenHave(thesis) by Substitution.ApplyRules(orderedRestrictionsEqual) + } + + // but then app(g1, z) = F (g1_z) = F(g1_z) = app(g2, z) + have(thesis) subproof { + val gzf = have(fun(g, t) |- app(g, z) === F(orderedRestriction(g, z, p))) subproof { + assume(fun(g, t)) + have(forall(a, in(a, initialSegment(p, t)) ==> (app(g, a) === F(orderedRestriction(g, a, p))))) by Restate + thenHave(in(z, initialSegment(p, t)) ==> (app(g, z) === F(orderedRestriction(g, z, p)))) by InstantiateForall(z) + thenHave(thesis) by Tautology + } + + // g1(z) = F(g1_z) + val g1f = gzf of g -> g1 + + // g2(z) = F(g2_z) + val g2f = gzf of g -> g2 + + // F(g1_z) = F(g2_z) + // fg1g2eq + + val fg1fg2Tog1 = equalityTransitivity of (x -> F(orderedRestriction(g1, z, p)), y -> F(orderedRestriction(g2, z, p)), z -> app(g2, z)) + val g2fg2Tog1 = equalityTransitivity of (x -> app(g2, z), y -> F(orderedRestriction(g1, z, p)), z -> app(g1, z)) + + // g1(z) = g2(z) + have(thesis) by Tautology.from(fg1fg2Tog1, g2fg2Tog1, g1f, g2f, fg1g2eq) + } + } + + // thus, they must agree on the whole domain + val eqZ = have(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) |- forall(z, in(z, initialSegment(p, t)) ==> (app(g1, z) === app(g2, z)))) subproof { + assume(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2)) + have(in(z, initialSegment(p, t)) |- forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z))) by Weakening( + initToz + ) + thenHave( + in(z, firstInPair(pt)) |- forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z)) + ) by Substitution.ApplyRules(firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) + thenHave( + in(z, firstInPair(pt)) ==> (forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z))) + ) by Restate + thenHave( + forall(z, in(z, firstInPair(pt)) ==> (forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z)))) + ) by RightForall + have( + forall(z, in(z, firstInPair(pt)) ==> (app(g1, z) === app(g2, z))) + ) by Tautology.from(lastStep, ptWO, wellOrderedInduction of (p -> pt, Q -> lambda(x, app(g1, x) === app(g2, x)))) + thenHave(thesis) by Substitution.ApplyRules(firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) + } + + // so g1 = g2, but this is a contradiction + val contra = have(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) |- ()) subproof { + assume(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2)) + have((g1 === g2)) by Tautology.from(eqZ, functionsEqualIfEqualOnDomain of (f -> g1, g -> g2, a -> initialSegment(p, t))) + thenHave(thesis) by Restate + } + + // so there exists a unique one, if there exists one at all + have(!exists(g, fun(g, t)) \/ existsOne(g, fun(g, t))) subproof { + have(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) |- ()) by Restate.from(contra) + thenHave(exists(g2, fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2)) |- ()) by LeftExists + thenHave(exists(g1, exists(g2, fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2))) |- ()) by LeftExists + have(thesis) by Tautology.from(lastStep, atleastTwoExist of (P -> lambda(x, fun(x, t)))) + } + + thenHave(thesis) by Restate + } + + // EXISTENCE ---------------------------------------- + + // if there exists a unique function `g` for the initial segment of some `x`, get the set of these + val wDef = forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) + + // take its union + // this is a function `g` for `x` (almost) + val uw = union(w) // + `(predecessor x, F(U w))` in the successor case + + // properties of w / uw + + val elemsFunctional = Lemma( + wDef |- + forall(t, in(t, w) ==> functional(t)) + ) { + assume(wDef) + have(in(t, w) |- functional(t)) subproof { + assume(in(t, w)) + have(in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) by InstantiateForall + val exy = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) by Tautology + + have(exists(y, in(y, initialSegment(p, x)) /\ fun(t, y)) |- functional(t)) subproof { + have(fun(t, y) |- functional(t)) by Tautology.from(functionalOver.definition of (f -> t, x -> initialSegment(p, y))) + thenHave((in(y, initialSegment(p, x)) /\ fun(t, y)) |- functional(t)) by Weakening + thenHave(thesis) by LeftExists + } + + have(thesis) by Cut(exy, lastStep) + } + thenHave(in(t, w) ==> functional(t)) by Restate + thenHave(thesis) by RightForall + } + + val elemsSubset = Lemma( + wellOrder(p) /\ wDef |- + forall(t1, forall(t2, (in(t1, w) /\ in(t2, w)) ==> (subset(t1, t2) \/ subset(t2, t1)))) + ) { + assume(wDef, wellOrder(p)) + + have(in(t1, w) /\ in(t2, w) |- subset(t1, t2) \/ subset(t2, t1)) subproof { + assume(in(t1, w)) + assume(in(t2, w)) + + // given t1 and t2 + // they must come from y1 and y2 + + // if t1 == t2 + // done + val t1EQt2 = have((t1 === t2) |- subset(t1, t2) \/ subset(t2, t1)) by Weakening(subsetEqualitySymmetry of (x -> t1, y -> t2)) + + // if t1 != t2 + val t1NEQt2 = have(!(t1 === t2) |- subset(t1, t2) \/ subset(t2, t1)) subproof { + assume(!(t1 === t2)) + def ytDef(y: Term, t: Term) = in(y, initialSegment(p, x)) /\ fun(t, y) + val y1 = variable + val y2 = variable + + val initMemToP1 = have(in(y, initialSegment(p, a)) |- in(y, p1)) subproof { + have(forall(y, in(y, initialSegment(p, a)) <=> (in(y, p1) /\ in(pair(y, a), p2)))) by InstantiateForall(initialSegment(p, a))(initialSegment.definition) + thenHave(in(y, initialSegment(p, a)) <=> (in(y, p1) /\ in(pair(y, a), p2))) by InstantiateForall(y) + thenHave(thesis) by Tautology + } + + have(ytDef(y1, t1) /\ ytDef(y2, t2) |- subset(t1, t2) \/ subset(t2, t1)) subproof { + assume(ytDef(y1, t1)) + assume(ytDef(y2, t2)) + // cases: + // y1 == y2 + // done by the uniqueness lemma above + val yeq = have((y1 === y2) |- subset(t1, t2)) subproof { + assume(y1 === y2) + have(fun(t1, y1) /\ fun(t2, y2)) by Restate + thenHave(fun(t1, y1) /\ fun(t2, y1)) by Substitution.ApplyRules(y1 === y2) + thenHave(fun(t1, y1) /\ fun(t2, y1) /\ !(t1 === t2)) by Tautology + thenHave(exists(t2, fun(t1, y1) /\ fun(t2, y1) /\ !(t1 === t2))) by RightExists + thenHave(exists(t1, exists(t2, fun(t1, y1) /\ fun(t2, y1) /\ !(t1 === t2)))) by RightExists + have(exists(t1, fun(t1, y1)) /\ !existsOne(t1, fun(t1, y1))) by Tautology.from(lastStep, atleastTwoExist of P -> lambda(t1, fun(t1, y1))) + have(bot) by Tautology.from(lastStep, uniqueRecursiveFunction of t -> y1, initMemToP1 of (y -> y1, a -> x)) + thenHave(thesis) by Weakening + } + + // y1 != y2 + // real work to be done here :- + val neq = have(!(y1 === y2) |- subset(t1, t2) \/ subset(t2, t1)) subproof { + assume(!(y1 === y2)) + + // y1 < y2 or y2 < y1? + // we prove it in the generic case + val a1 = variable + val a2 = variable + val k1 = variable + val k2 = variable + val ltToSubset = have(ytDef(a1, k1) /\ ytDef(a2, k2) /\ in(pair(a1, a2), p2) |- subset(k1, k2)) subproof { + assume(ytDef(a1, k1)) + assume(ytDef(a2, k2)) + assume(in(pair(a1, a2), p2)) + // fun(k1, a1) + // fun(k2, a2) + // a1 < a2 + // we should have k1 \subseteq k2 + + // dom k1 \subseteq dom k2 + val domSubset = + have(subset(initialSegment(p, a1), initialSegment(p, a2))) by Tautology.from(initialSegmentsSubset of (x -> a1, y -> a2), pIsAPartialOrder) + + // suppose there is a minimal n such that k1 n != k2 n + val n = variable + val nDef = + in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall(b, (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b))) + + // if k1 and k2 disagree at all + val k1k2disagree = exists(n, in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n))) + + val nExists = have(k1k2disagree |- exists(n, nDef)) subproof { + assume(k1k2disagree) + + // B defined by x => x < a1 /\ k1 x != k2 x exists + val B = variable + val BDef = forall(x, in(x, B) <=> (in(x, initialSegment(p, a1)) /\ !(app(k1, x) === app(k2, x)))) + val BExists = have(exists(B, BDef)) by Weakening(comprehensionSchema of (z -> initialSegment(p, a1), φ -> lambda(x, !(app(k1, x) === app(k2, x))))) + + // B forms a subset of p1 + val subsetB = have(BDef |- subset(B, p1)) subproof { + assume(BDef) + have(in(y, B) <=> (in(y, initialSegment(p, a1)) /\ !(app(k1, y) === app(k2, y)))) by InstantiateForall + have(in(y, B) ==> in(y, p1)) by Tautology.from(lastStep, initMemToP1 of a -> a1) + thenHave(forall(y, in(y, B) ==> in(y, p1))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> B, y -> p1)) + } + + // B is non-empty + val nonEmptyB = have(BDef |- !(B === emptySet)) subproof { + assume(BDef) + have(in(n, B) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)))) by InstantiateForall + thenHave((in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n))) |- in(n, B)) by Weakening + have((in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n))) |- !(B === emptySet)) by Cut(lastStep, setWithElementNonEmpty of (y -> n, x -> B)) + thenHave(thesis) by LeftExists + } + + // so it has a minimal element + val minimalB = have(BDef |- exists(n, nDef)) subproof { + assume(BDef) + have(forall(B, (subset(B, p1) /\ !(B === emptySet)) ==> exists(n, in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b)))))) by Tautology.from( + wellOrder.definition + ) + thenHave((subset(B, p1) /\ !(B === emptySet)) ==> exists(n, in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))))) by InstantiateForall(B) + val exN = have(exists(n, in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))))) by Tautology.from(lastStep, nonEmptyB, subsetB) + + // transform n \in B to n < a1 /\ k1 n != k2 n + have(in(b, B) <=> (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b)))) by InstantiateForall + thenHave( + (in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))) <=> ((in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b))) + ) by Tautology + thenHave( + forall(b, (in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))) <=> ((in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)))) + ) by RightForall + val bEq = have( + forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))) <=> forall( + b, + (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) + ) + ) by Tautology.from( + lastStep, + universalEquivalenceDistribution of (P := lambda(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))), Q := lambda( + b, + (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) + )) + ) + + have(in(n, B) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)))) by InstantiateForall + have( + (in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b)))) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall( + b, + (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) + )) + ) by Tautology.from(lastStep, bEq) + thenHave( + forall( + n, + (in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b)))) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall( + b, + (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) + )) + ) + ) by RightForall + + have(thesis) by Tautology.from( + lastStep, + exN, + existentialEquivalenceDistribution of (P -> lambda(n, (in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))))), Q -> lambda( + n, + (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall( + b, + (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) + )) + )) + ) + } + + thenHave(exists(B, BDef) |- exists(n, nDef)) by LeftExists + have(thesis) by Cut(BExists, lastStep) + } + + // but k1 n == F(k1 |^ n) and k2 n == F(k2 |^ n) + val fK1 = have(nDef |- app(k1, n) === F(orderedRestriction(k1, n, p))) subproof { + // n < a1 ==> k1 n = F(k1 |^ n) + have(forall(b, in(b, initialSegment(p, a1)) ==> (app(k1, b) === F(orderedRestriction(k1, b, p))))) by Tautology + thenHave(in(n, initialSegment(p, a1)) ==> (app(k1, n) === F(orderedRestriction(k1, n, p)))) by InstantiateForall(n) + + // we know n < a1, so result + thenHave(thesis) by Tautology + } + val fK2 = have(nDef |- app(k2, n) === F(orderedRestriction(k2, n, p))) subproof { + // n < a2 ==> k2 n = F(k2 |^ n) + have(forall(b, in(b, initialSegment(p, a2)) ==> (app(k2, b) === F(orderedRestriction(k2, b, p))))) by Tautology + val impl = thenHave(in(n, initialSegment(p, a2)) ==> (app(k2, n) === F(orderedRestriction(k2, n, p)))) by InstantiateForall(n) + + // n < a1 and a1 < a2, so n < a2 + have(forall(b, in(b, initialSegment(p, a1)) ==> in(b, initialSegment(p, a2)))) by Tautology.from( + domSubset, + subsetAxiom of (x -> initialSegment(p, a1), y -> initialSegment(p, a2)) + ) + thenHave(in(n, initialSegment(p, a1)) ==> in(n, initialSegment(p, a2))) by InstantiateForall(n) + + // so result + have(thesis) by Tautology.from(lastStep, impl) + } + + // k1 |^ n == k2 |^ n by minimality of n + // so F(k1 |^ n) == F(k2 |^ n) + val ordResEq = have(nDef |- F(orderedRestriction(k1, n, p)) === F(orderedRestriction(k2, n, p))) subproof { + assume(nDef) + + val k1k2 = have(orderedRestriction(k1, n, p) === orderedRestriction(k2, n, p)) subproof { + // suppose not + assume(!(orderedRestriction(k1, n, p) === orderedRestriction(k2, n, p))) + + // there must exist an element where they disagree, say m + val m = variable + val mDef = in(m, initialSegment(p, n)) /\ !(app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)) + val mExists = have(exists(m, mDef)) subproof { + // k1 |^ n != k2 |^ n by assumption + val k1k2unequal = have(!(orderedRestriction(k1, n, p) === orderedRestriction(k2, n, p))) by Restate + + // they are functions on the same domain + val k1k2functional = have(functionalOver(orderedRestriction(k1, n, p), initialSegment(p, n)) /\ functionalOver(orderedRestriction(k2, n, p), initialSegment(p, n))) subproof { + // n < a1 by definition + val nLTa1 = have(in(n, initialSegment(p, a1))) by Restate + + // but a1 < a2, so n < a2 + have(forall(n, in(n, initialSegment(p, a1)) ==> in(n, initialSegment(p, a2)))) by Tautology.from( + domSubset, + subsetAxiom of (x -> initialSegment(p, a1), y -> initialSegment(p, a2)) + ) + thenHave(in(n, initialSegment(p, a1)) ==> in(n, initialSegment(p, a2))) by InstantiateForall(n) + val nLTa2 = have(in(n, initialSegment(p, a2))) by Tautology.from(lastStep, nLTa1) + + // k1 functional over k1, a -> n, b -> a1)) + val k2n = + have(functionalOver(orderedRestriction(k2, n, p), initialSegment(p, n))) by Tautology.from(k2fun, nLTa2, orderedRestrictionFunctionalOverInit of (f -> k2, a -> n, b -> a2)) + + have(thesis) by Tautology.from(k1n, k2n) + } + + // so there is a violating element + have(!forall(m, in(m, initialSegment(p, n)) ==> (app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)))) by Tautology.from( + k1k2unequal, + k1k2functional, + functionsEqualIfEqualOnDomain of (f -> orderedRestriction(k1, n, p), g -> orderedRestriction(k2, n, p), a -> initialSegment(p, n)) + ) + thenHave(thesis) by Restate + } + + // we must have m < n + val mViolatesRestricted = + have(mDef |- in(m, initialSegment(p, a1)) /\ !(app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)) /\ in(pair(m, n), p2)) subproof { + assume(mDef) + // we have n < a1 + have(forall(z, (z === initialSegment(p, a)) <=> (forall(t, in(t, z) <=> (in(t, p1) /\ in(pair(t, a), p2)))))) by Weakening(initialSegment.definition) + val initSegMembership = thenHave((forall(t, in(t, initialSegment(p, a)) <=> (in(t, p1) /\ in(pair(t, a), p2))))) by InstantiateForall(initialSegment(p, a)) + + have(in(n, initialSegment(p, a1)) <=> (in(n, p1) /\ in(pair(n, a1), p2))) by InstantiateForall(n)(initSegMembership of a -> a1) + val nLTa1 = thenHave(in(pair(n, a1), p2)) by Tautology + + // and m < n + have(in(m, initialSegment(p, n)) <=> (in(m, p1) /\ in(pair(m, n), p2))) by InstantiateForall(m)(initSegMembership of a -> n) + val mLTn = thenHave(in(m, p1) /\ in(pair(m, n), p2)) by Tautology + + // by transitivity, m < a1 as well + have(forall(w, forall(y, forall(z, (in(pair(w, y), p2) /\ in(pair(y, z), p2)) ==> in(pair(w, z), p2))))) by Weakening(wellOrderTransitivity) + thenHave((in(pair(m, n), p2) /\ in(pair(n, a1), p2)) ==> in(pair(m, a1), p2)) by InstantiateForall(m, n, a1) + val mLTa1 = have(in(m, p1) /\ in(pair(m, a1), p2)) by Tautology.from(lastStep, nLTa1, mLTn) + + have(in(m, initialSegment(p, a1)) <=> (in(m, p1) /\ in(pair(m, a1), p2))) by InstantiateForall(m)(initSegMembership of a -> a1) + have(thesis) by Tautology.from(lastStep, mLTa1, mLTn) + } + + val mViolates = have(mDef |- in(m, initialSegment(p, a1)) /\ !(app(k1, m) === app(k2, m)) /\ in(pair(m, n), p2)) subproof { + assume(mDef) + + val mInDom1 = have(in(m, relationDomain(k1))) subproof { + val domEQ = have(initialSegment(p, a1) === relationDomain(k1)) by Tautology.from(functionalOver.definition of (f -> k1, x -> initialSegment(p, a1))) + + have(in(m, initialSegment(p, a1))) by Tautology.from(mViolatesRestricted) + thenHave(thesis) by Substitution.ApplyRules(domEQ) + } + + val mInDom2 = have(in(m, relationDomain(k2))) subproof { + val domEQ = have(initialSegment(p, a2) === relationDomain(k2)) by Tautology.from(functionalOver.definition of (f -> k2, x -> initialSegment(p, a2))) + + val mLTa1 = have(in(m, initialSegment(p, a1))) by Tautology.from(mViolatesRestricted) + have(forall(m, in(m, initialSegment(p, a1)) ==> in(m, initialSegment(p, a2)))) by Tautology.from( + subsetAxiom of (x -> initialSegment(p, a1), y -> initialSegment(p, a2)), + domSubset + ) + thenHave(in(m, initialSegment(p, a1)) ==> in(m, initialSegment(p, a2))) by InstantiateForall(m) + have(in(m, initialSegment(p, a2))) by Tautology.from(lastStep, mLTa1) + + thenHave(thesis) by Substitution.ApplyRules(domEQ) + } + + // if the application is equal on the ordered restriction, it must be equal on the entire functions + have((app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)) <=> (app(k1, m) === app(k2, m))) by Tautology.from( + orderedRestrictionAgreement of (f -> k1, g -> k2, a -> n, b -> m), + pIsAPartialOrder, + mInDom1, + mInDom2 + ) + have(thesis) by Tautology.from(mViolatesRestricted, lastStep) + } + + // but n was the minimal violation + // contradiction + have(mDef |- bot) subproof { + assume(mDef) + // m < a1 and k1 m != k2 m ==> n < m \/ n = m + have(forall(b, (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)))) by Restate + thenHave((in(m, initialSegment(p, a1)) /\ !(app(k1, m) === app(k2, m))) ==> (in(pair(n, m), p2) \/ (n === m))) by InstantiateForall(m) + val mLeqn = have((in(pair(n, m), p2) \/ (n === m))) by Tautology.from(lastStep, mViolates) + + // we had m < n, and the order is anti-symmetric, so n = m + have(forall(n, forall(m, (in(pair(m, n), p2) /\ in(pair(n, m), p2)) ==> (n === m)))) by Tautology.from( + wellOrder.definition, + totalOrder.definition, + partialOrder.definition, + antiSymmetric.definition of (r -> p2, x -> p1) + ) + thenHave((in(pair(m, n), p2) /\ in(pair(n, m), p2)) ==> (n === m)) by InstantiateForall(n, m) + val nEQm = have((n === m)) by Tautology.from(lastStep, mViolates, mLeqn) + + // however, that means n < n, but the order is anti-reflexive + have(in(pair(m, n), p2)) by Weakening(mViolates) + val nLTn = thenHave(in(pair(n, n), p2)) by Substitution.ApplyRules(nEQm) + + have(forall(n, in(n, p1) ==> !in(pair(n, n), p2))) by Tautology.from(pIsAPartialOrder, partialOrder.definition, antiReflexive.definition of (r -> p2, x -> p1)) + thenHave(in(n, p1) ==> !in(pair(n, n), p2)) by InstantiateForall(n) + have(!in(pair(n, n), p2)) by Tautology.from(lastStep, initialSegmentBaseElement of (x -> n, y -> a1), pIsAPartialOrder) + + // this is a contradiction + have(thesis) by Tautology.from(lastStep, nLTn) + } + thenHave(exists(m, mDef) |- bot) by LeftExists + have(bot) by Cut(mExists, lastStep) + } + + have(F(orderedRestriction(k1, n, p)) === F(orderedRestriction(k1, n, p))) by Restate + thenHave(thesis) by Substitution.ApplyRules(k1k2) + } + + // // finally k1 n == k2 n + // // this is a contradiction + val appEq = have(nDef |- app(k1, n) === app(k2, n)) subproof { + assume(nDef) + val k1ToFK2 = equalityTransitivity of (x -> app(k1, n), y -> F(orderedRestriction(k1, n, p)), z -> F(orderedRestriction(k2, n, p))) + val k1ToK2 = equalityTransitivity of (x -> app(k1, n), y -> F(orderedRestriction(k2, n, p)), z -> app(k2, n)) + + have(thesis) by Tautology.from(fK1, fK2, ordResEq, k1ToFK2, k1ToK2) + } + + thenHave(nDef |- ()) by Tautology + thenHave(exists(n, nDef) |- ()) by LeftExists + val disagreeCase = have(k1k2disagree |- ()) by Cut(nExists, lastStep) + + have(!k1k2disagree |- subset(k1, k2)) subproof { + assume(!k1k2disagree) + val impl = have(forall(b, in(b, initialSegment(p, a1)) ==> (app(k1, b) === app(k2, b)))) by Restate + + // k1 k2 are functional + val funK1 = have(functionalOver(k1, initialSegment(p, a1))) by Tautology + val funK2 = have(functionalOver(k2, initialSegment(p, a2))) by Tautology + + // // and a1, y -> a2), pIsAPartialOrder) + + have(thesis) by Tautology.from(impl, funK1, funK2, domSubset, functionsSubsetIfEqualOnSubsetDomain of (f -> k1, g -> k2, a -> initialSegment(p, a1), b -> initialSegment(p, a2))) + } + + have(thesis) by Tautology.from(lastStep, disagreeCase) + } + + // finally, instantiate to y1 < y2 and to y2 < y1 + val y1LTy2 = have(in(pair(y1, y2), p2) |- subset(t1, t2)) by Restate.from(ltToSubset of (a1 -> y1, k1 -> t1, a2 -> y2, k2 -> t2)) + val y2LTy1 = have(in(pair(y2, y1), p2) |- subset(t2, t1)) by Restate.from(ltToSubset of (a1 -> y2, k1 -> t2, a2 -> y1, k2 -> t1)) + + // totality of the order means y1 < y2 or y2 < y1 + have(in(pair(y1, y2), p2) \/ in(pair(y2, y1), p2)) subproof { + have(forall(y1, forall(y2, (in(y1, p1) /\ in(y2, p1)) ==> (in(pair(y1, y2), p2) \/ in(pair(y2, y1), p2) \/ (y1 === y2))))) by Tautology.from( + wellOrder.definition, + totalOrder.definition, + total.definition of (r -> p2, x -> p1) + ) + val impl = thenHave((in(y1, p1) /\ in(y2, p1)) ==> (in(pair(y1, y2), p2) \/ in(pair(y2, y1), p2) \/ (y1 === y2))) by InstantiateForall(y1, y2) + + // expand defs + have(forall(z, (z === initialSegment(x, y)) <=> forall(t, in(t, z) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x)))))) by Weakening( + initialSegment.definition of (p -> x, a -> y) + ) + thenHave(forall(t, in(t, initialSegment(x, y)) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x))))) by InstantiateForall(initialSegment(x, y)) + val initXY = thenHave(in(c, initialSegment(x, y)) <=> (in(c, firstInPair(x)) /\ in(pair(c, y), secondInPair(x)))) by InstantiateForall(c) + + have(in(y1, p1) /\ in(y2, p1)) by Tautology.from(initialSegmentBaseElement of (y -> x, x -> y1), initialSegmentBaseElement of (y -> x, x -> y2), pIsAPartialOrder) + have(thesis) by Tautology.from(lastStep, impl) + } + + have(thesis) by Tautology.from(lastStep, y1LTy2, y2LTy1) + // sorry + } + + have(thesis) by Tautology.from(yeq, neq) + } + + thenHave((ytDef(y1, t1), ytDef(y2, t2)) |- subset(t1, t2) \/ subset(t2, t1)) by Restate + thenHave((ytDef(y1, t1), exists(y2, ytDef(y2, t2))) |- subset(t1, t2) \/ subset(t2, t1)) by LeftExists + val exToRes = thenHave((exists(y1, ytDef(y1, t1)), exists(y2, ytDef(y2, t2))) |- subset(t1, t2) \/ subset(t2, t1)) by LeftExists + + // wDef + val exy = have(in(b, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(b, y))) by InstantiateForall + + have(thesis) by Tautology.from(exToRes, exy of b -> t1, exy of b -> t2) + } + + have(thesis) by Tautology.from(t1EQt2, t1NEQt2) + } + + thenHave(in(t1, w) /\ in(t2, w) ==> subset(t1, t2) \/ subset(t2, t1)) by Restate + thenHave(forall(t2, in(t1, w) /\ in(t2, w) ==> subset(t1, t2) \/ subset(t2, t1))) by RightForall + thenHave(forall(t1, forall(t2, in(t1, w) /\ in(t2, w) ==> subset(t1, t2) \/ subset(t2, t1)))) by RightForall + } + + val uwfunctional = Lemma( + wDef /\ wellOrder(p) |- functional(uw) + ) { + have(thesis) by Tautology.from(elemsFunctional, elemsSubset, unionOfFunctionSet of z -> w) + } + + val uwRestrictedEq = Lemma( + wellOrder(p) /\ wDef /\ in(z, relationDomain(uw)) |- app(uw, z) === F(orderedRestriction(uw, z, p)) + ) { + assume(wellOrder(p), wDef) + assume(in(z, relationDomain(uw))) + + // \exists g \in w. uw z = F g |^ z + val gExists = have(exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))))) subproof { + // dom uw = < x + // val domUEq = have(relationDomain(uw) === initialSegment(p, x)) by Tautology.from(uwFunctionalOver, functionalOverImpliesDomain of (f -> uw, x -> initialSegment(p, x))) + + // z in dom uw + // have(in(z, initialSegment(p, x))) by Restate + // val zInDom = thenHave(in(z, relationDomain(uw))) by EqualityReasoning.ApplyRules(domUEq) + + // so exists g \in w, z \in dom g + have(functional(uw) /\ in(z, relationDomain(uw)) |- exists(g, in(g, w) /\ in(z, relationDomain(g)))) by InstantiateForall(z)(domainOfFunctionalUnion of z -> w) + val gExists = have(exists(g, in(g, w) /\ in(z, relationDomain(g)))) by Tautology.from(lastStep, uwfunctional) + + have((in(g, w), in(z, relationDomain(g))) |- app(uw, z) === F(orderedRestriction(g, z, p))) subproof { + assume(in(g, w)) + assume(in(z, relationDomain(g))) + + // given such a g, g(z) = uw(z) + val gEqU = have(app(g, z) === app(uw, z)) subproof { + have( + (b === app(f, z)) <=> ((functional(f) /\ in(z, relationDomain(f))) ==> in(pair(z, b), f)) /\ ((!functional(f) \/ !in(z, relationDomain(f))) ==> (b === ∅)) + ) by InstantiateForall(b)(app.definition of x -> z) + val appDef = thenHave(functional(f) /\ in(z, relationDomain(f)) |- (b === app(f, z)) <=> in(pair(z, b), f)) by Tautology + + // for g z + val gb = have((b === app(g, z)) <=> in(pair(z, b), g)) subproof { + // g is functional + have(in(g, w) ==> functional(g)) by InstantiateForall(g)(elemsFunctional) + have(thesis) by Tautology.from(lastStep, appDef of f -> g) + } + + // for uw z + val uwb = have((b === app(uw, z)) <=> in(pair(z, b), uw)) by Tautology.from(appDef of f -> uw, uwfunctional) + + // in g ==> in uw + have(in(t, g) ==> in(t, uw)) subproof { + assume(in(t, g)) + + // suffices to show the existence of g + val unionAx = have(in(t, uw) <=> exists(g, in(g, w) /\ in(t, g))) by Weakening(unionAxiom of (z -> t, x -> w)) + + have(in(g, w) /\ in(t, g)) by Restate + thenHave(exists(g, in(g, w) /\ in(t, g))) by RightExists + + have(thesis) by Tautology.from(lastStep, unionAx) + } + + // equal + have((b === app(g, z)) |- (b === app(uw, z))) by Tautology.from(lastStep of t -> pair(z, b), gb, uwb) + have(thesis) by Restate.from(lastStep of b -> app(g, z)) + } + + // we must also have g(z) = F(g |^ z) + have(app(g, z) === F(orderedRestriction(g, z, p))) subproof { + have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall + val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology + + have(fun(g, y) |- app(g, z) === F(orderedRestriction(g, z, p))) subproof { + assume(fun(g, y)) + + // dom g = < y + val domEq = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(functionalOverImpliesDomain of (f -> g, x -> initialSegment(p, y))) + + have(forall(a, in(a, initialSegment(p, y)) ==> (app(g, a) === F(orderedRestriction(g, a, p))))) by Restate + thenHave(in(z, initialSegment(p, y)) ==> (app(g, z) === F(orderedRestriction(g, z, p)))) by InstantiateForall(z) + thenHave(in(z, relationDomain(g)) ==> (app(g, z) === F(orderedRestriction(g, z, p)))) by Substitution.ApplyRules(domEq) + thenHave(thesis) by Restate + } + thenHave(in(y, initialSegment(p, x)) /\ fun(g, y) |- app(g, z) === F(orderedRestriction(g, z, p))) by Weakening + thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- app(g, z) === F(orderedRestriction(g, z, p))) by LeftExists + have(thesis) by Cut(yExists, lastStep) + } + + thenHave(thesis) by Substitution.ApplyRules(gEqU) + } + + thenHave((in(g, w) /\ in(z, relationDomain(g))) |- in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p)))) by Tautology + thenHave((in(g, w) /\ in(z, relationDomain(g))) |- exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))))) by RightExists + thenHave(exists(g, in(g, w) /\ in(z, relationDomain(g))) |- exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))))) by LeftExists + + have(thesis) by Cut(gExists, lastStep) + } + + // but g \in w ==> g |^ z = uw |^ z + val gRestrictedEq = have(in(g, w) /\ in(z, relationDomain(g)) |- orderedRestriction(g, z, p) === orderedRestriction(uw, z, p)) subproof { + assume(in(g, w)) + assume(in(z, relationDomain(g))) + + val og = orderedRestriction(g, z, p) + val ou = orderedRestriction(uw, z, p) + + // we prove this for a generic element t + val ogDef = have(in(t, og) <=> (in(t, g) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(orderedRestrictionMembership of (f -> g, a -> z, b -> t), pIsAPartialOrder) + + val ouDef = have(in(t, ou) <=> (in(t, uw) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(orderedRestrictionMembership of (f -> uw, a -> z, b -> t), pIsAPartialOrder) + + // t \in g |^ z ==> t \in uw |^ z + val fwd = have(in(t, og) |- in(t, ou)) subproof { + assume(in(t, og)) + val tInG = have((in(t, g) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(ogDef) + + // but g is a subset of uw + have(in(t, g) ==> in(t, uw)) subproof { + assume(in(t, g)) + + have(in(g, w) /\ in(t, g)) by Restate + thenHave(exists(g, in(g, w) /\ in(t, g))) by RightExists + have(thesis) by Tautology.from(lastStep, unionAxiom of (x -> w, z -> t)) + } + + have(thesis) by Tautology.from(lastStep, tInG, ouDef) + } + + // t \in uw |^ z ==> t \in g |^ z + val bwd = have(in(t, ou) |- in(t, og)) subproof { + assume(in(t, ou)) + val tInU = have((in(t, uw) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(ouDef) + + // if t \in uw and t1 < z + have(in(t, uw) /\ in(firstInPair(t), initialSegment(p, z)) |- in(t, g)) subproof { + assume(in(t, uw)) + assume(in(firstInPair(t), initialSegment(p, z))) + + // suppose ! t \in g + have(!in(t, g) |- ()) subproof { + assume(!in(t, g)) + + // exists f \in w, t \in f by union axiom on uw + val fExists = have(exists(f, in(f, w) /\ in(t, f))) by Tautology.from(unionAxiom of (x -> w, z -> t)) + + // if such an f exists + have(in(f, w) /\ in(t, f) |- ()) subproof { + assume(in(f, w)) + assume(in(t, f)) + + // f \subseteq g or g \subseteq f + val cases = have(subset(f, g) \/ subset(g, f)) subproof { + have((in(g, w) /\ in(f, w)) ==> (subset(g, f) \/ subset(f, g))) by InstantiateForall(g, f)(elemsSubset) + thenHave(thesis) by Tautology + } + + // f \subseteq g ==> contradiction directly + val fg = have(subset(f, g) |- ()) subproof { + assume(subset(f, g)) + + have(forall(t, in(t, f) ==> in(t, g))) by Tautology.from(subsetAxiom of (x -> f, y -> g)) + thenHave(in(t, f) ==> in(t, g)) by InstantiateForall(t) + thenHave(thesis) by Tautology + } + + // g \subseteq f + val gf = have(subset(g, f) |- ()) subproof { + assume(subset(g, f)) + + val t1 = firstInPair(t) + val t2 = secondInPair(t) + + // t1 \in dom og + val t1InDomOG = have(in(t1, relationDomain(og))) subproof { + // t \in ou + // so t1 \in exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall + val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology + + have(in(y, initialSegment(p, x)) /\ fun(g, y) |- relationDomain(og) === initialSegment(p, z)) subproof { + assume(in(y, initialSegment(p, x))) + assume(fun(g, y)) + + // dom g = < y + have(functionalOver(g, initialSegment(p, y))) by Restate + val domEq = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(lastStep, functionalOver.definition of (f -> g, x -> initialSegment(p, y))) + + // but z in dom g, so z < y + have(in(pair(z, y), p2)) subproof { + have(in(z, relationDomain(g))) by Restate + thenHave(in(z, initialSegment(p, y))) by Substitution.ApplyRules(domEq) + have(thesis) by Tautology.from(lastStep, initialSegmentElement of x -> z, pIsAPartialOrder) + } + + // so < z \subseteq < y + val zySubset = have(subset(initialSegment(p, z), initialSegment(p, y))) by Tautology.from(lastStep, initialSegmentsSubset of (x -> z, y -> y), pIsAPartialOrder) + + // dom og = < y \cap < z = < z + have(thesis) subproof { + // dom og = < y \cap < z + val domEQ = have(relationDomain(og) === setIntersection(initialSegment(p, z), initialSegment(p, y))) subproof { + val ogExpand = have(restrictedFunction(g, initialSegment(p, z)) === og) by InstantiateForall(og)(orderedRestriction.definition of (f -> g, a -> z)) + + have(relationDomain(restrictedFunction(g, initialSegment(p, z))) === setIntersection(initialSegment(p, z), relationDomain(g))) by Weakening( + restrictedFunctionDomain of (f -> g, x -> initialSegment(p, z)) + ) + thenHave(thesis) by Substitution.ApplyRules(ogExpand, domEq) + } + + have(setIntersection(initialSegment(p, z), initialSegment(p, y)) === initialSegment(p, z)) by Tautology.from( + lastStep, + zySubset, + intersectionOfSubsets of (x -> initialSegment(p, z), y -> initialSegment(p, y)) + ) + + have(thesis) by Substitution.ApplyRules(lastStep)(domEQ) + } + } + + thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- relationDomain(og) === initialSegment(p, z)) by LeftExists + have(thesis) by Cut(yExists, lastStep) + } + + // t1 \in dom g + have(thesis) by Substitution.ApplyRules(lastStep)(t1LTz) + } + + // since t1 \in dom g, exists a. (t, a) \in g + val aExists = have(exists(a, in(pair(t1, a), g))) subproof { + have(forall(t, in(t, relationDomain(g)) <=> exists(a, in(pair(t, a), g)))) by InstantiateForall(relationDomain(g))(relationDomain.definition of r -> g) + val domIff = thenHave(in(t1, relationDomain(g)) <=> exists(a, in(pair(t1, a), g))) by InstantiateForall(t1) + + // t1 is in dom og, so it is in dom g + have(in(t1, relationDomain(g))) subproof { + val ordEQ = have(og === restrictedFunction(g, initialSegment(p, z))) by InstantiateForall(og)(orderedRestriction.definition of (f -> g, a -> z)) + + have(relationDomain(restrictedFunction(g, initialSegment(p, z))) === setIntersection(initialSegment(p, z), relationDomain(g))) by Tautology.from( + restrictedFunctionDomain of (f -> g, x -> initialSegment(p, z)) + ) + thenHave(relationDomain(og) === setIntersection(initialSegment(p, z), relationDomain(g))) by Substitution.ApplyRules(ordEQ) + + have(forall(b, in(b, relationDomain(og)) <=> in(b, setIntersection(initialSegment(p, z), relationDomain(g))))) by Tautology.from( + lastStep, + extensionalityAxiom of (x -> relationDomain(og), y -> setIntersection(initialSegment(p, z), relationDomain(g))) + ) + thenHave(in(t1, relationDomain(og)) <=> in(t1, setIntersection(initialSegment(p, z), relationDomain(g)))) by InstantiateForall(t1) + have(in(t1, setIntersection(initialSegment(p, z), relationDomain(g)))) by Tautology.from(lastStep, t1InDomOG) + have(thesis) by Tautology.from(lastStep, setIntersectionMembership of (t -> t1, x -> initialSegment(p, z), y -> relationDomain(g))) + } + + have(thesis) by Tautology.from(lastStep, domIff) + } + + have(in(pair(t1, a), g) |- ()) subproof { + assume(in(pair(t1, a), g)) + + // (t1, a) \in f + have(forall(t, in(t, g) ==> in(t, f))) by Tautology.from(subsetAxiom of (x -> g, y -> f)) + thenHave(in(pair(t1, a), g) ==> in(pair(t1, a), f)) by InstantiateForall(pair(t1, a)) + val t1aInF = thenHave(in(pair(t1, a), f)) by Tautology + + // t must be a pair + val tIsPair = have(exists(a, exists(b, pair(a, b) === t))) subproof { + have(forall(t, in(t, uw) ==> exists(a, exists(b, (pair(a, b) === t) /\ in(a, relationDomain(uw)))))) by Tautology.from(uwfunctional, functionalMembership of (f -> uw)) + val exIn = thenHave(exists(a, exists(b, (pair(a, b) === t) /\ in(a, relationDomain(uw))))) by InstantiateForall(t) + + // eliminate extra terms inside exists + have(exists(a, exists(b, (pair(a, b) === t) /\ in(a, relationDomain(uw)))) |- exists(a, exists(b, (pair(a, b) === t)))) subproof { + have((pair(c, b) === t) /\ in(c, relationDomain(uw)) |- (pair(c, b) === t)) by Restate + thenHave((pair(c, b) === t) /\ in(c, relationDomain(uw)) |- exists(b, (pair(c, b) === t))) by RightExists + thenHave((pair(c, b) === t) /\ in(c, relationDomain(uw)) |- exists(c, exists(b, (pair(c, b) === t)))) by RightExists + thenHave(exists(b, (pair(c, b) === t) /\ in(c, relationDomain(uw))) |- exists(c, exists(b, (pair(c, b) === t)))) by LeftExists + thenHave(exists(c, exists(b, (pair(c, b) === t) /\ in(c, relationDomain(uw)))) |- exists(c, exists(b, (pair(c, b) === t)))) by LeftExists + } + have(thesis) by Cut(exIn, lastStep) + } + val tEqt1t2 = have(t === pair(t1, t2)) by Tautology.from(tIsPair, pairReconstruction of x -> t) + + // but (t1, t2) \in f + val t1t2InF = have(in(pair(t1, t2), f)) subproof { + // t in f + val tInF = have(in(t, f)) by Restate + + // so (t1, t2) = t + have(thesis) by Substitution.ApplyRules(tEqt1t2)(tInF) + } + + // t2 = a + val t2Eqa = have(t2 === a) subproof { + // f is functional + have(in(f, w) ==> functional(f)) by InstantiateForall(f)(elemsFunctional) + + // given t1, there must be a unique element in ran f it maps to + have(forall(t, exists(b, in(pair(t, b), f)) ==> existsOne(b, in(pair(t, b), f)))) by Tautology.from(lastStep, functional.definition) + val exToExOne = thenHave(exists(b, in(pair(t1, b), f)) |- existsOne(b, in(pair(t1, b), f))) by InstantiateForall(t1) + + have(in(pair(t1, a), f)) by Weakening(t1aInF) + val ex = thenHave(exists(b, in(pair(t1, b), f))) by RightExists + val exOne = have(existsOne(b, in(pair(t1, b), f))) by Cut(lastStep, exToExOne) + + have(forall(a, forall(b, !in(pair(t1, a), f) \/ !in(pair(t1, b), f) \/ (a === b)))) by Tautology.from(atleastTwoExist of P -> lambda(a, in(pair(t1, a), f)), ex, exOne) + thenHave(!in(pair(t1, a), f) \/ !in(pair(t1, t2), f) \/ (a === t2)) by InstantiateForall(a, t2) + have(thesis) by Tautology.from(lastStep, t1aInF, t1t2InF) + } + + // but then (t1, t2) = t \in g + have(in(pair(t1, a), g)) by Restate + thenHave(in(pair(t1, t2), g)) by Substitution.ApplyRules(t2Eqa) + thenHave(in(t, g)) by Substitution.ApplyRules(tEqt1t2) + + // this is a contradiction + thenHave(thesis) by Tautology + } + + thenHave(exists(a, in(pair(t1, a), g)) |- ()) by LeftExists + have(thesis) by Tautology.from(lastStep, aExists) + } + + have(thesis) by Tautology.from(cases, gf, fg) + } + + thenHave(exists(f, in(f, w) /\ in(t, f)) |- ()) by LeftExists + have(thesis) by Tautology.from(lastStep, fExists) + } + } + + have(thesis) by Tautology.from(lastStep, tInU, ogDef) + } + + have(in(t, ou) <=> in(t, og)) by Tautology.from(fwd, bwd) + thenHave(forall(t, in(t, ou) <=> in(t, og))) by RightForall + have(ou === og) by Tautology.from(lastStep, extensionalityAxiom of (x -> ou, y -> og)) + } + + have(in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))) |- app(uw, z) === F(orderedRestriction(uw, z, p))) subproof { + have(in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))) |- (app(uw, z) === F(orderedRestriction(g, z, p)))) by Restate + thenHave(thesis) by Substitution.ApplyRules(gRestrictedEq) + } + + thenHave(exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p)))) |- app(uw, z) === F(orderedRestriction(uw, z, p))) by LeftExists + have(thesis) by Tautology.from(gExists, lastStep) + + } + + val uniquenessFromExistsOne = Theorem( + existsOne(x, P(x)) |- forall(y, forall(z, (P(y) /\ P(z)) ==> (y === z))) + ) { + val a1 = assume(exists(x, forall(y, P(y) <=> (y === x)))) + val xe = witness(a1) + have(P(y) <=> (y === xe)) by InstantiateForall(y)(xe.definition) + val py = thenHave(P(y) |- y === xe) by Weakening + have(P(z) <=> (z === xe)) by InstantiateForall(z)(xe.definition) + val pz = thenHave(P(z) |- z === xe) by Weakening + have((P(y), P(z)) |- (y === z)) by Substitution.ApplyRules(pz)(py) + have((P(y) /\ P(z)) ==> (y === z)) by Tautology.from(lastStep) + thenHave(forall(z, (P(y) /\ P(z)) ==> (y === z))) by RightForall + thenHave(thesis) by RightForall + + } + + private val A = variable + private val B = variable + private val R = predicate[2] + val strictReplacementSchema = Theorem( + forall(x, in(x, A) ==> existsOne(y, R(x, y))) + |- exists(B, forall(y, in(y, B) <=> exists(x, in(x, A) /\ R(x, y)))) + ) { + assume(forall(x, in(x, A) ==> existsOne(y, R(x, y)))) + thenHave(in(x, A) ==> existsOne(y, R(x, y))) by InstantiateForall(x) + have(in(x, A) ==> ∀(y, ∀(z, (R(x, y) /\ R(x, z)) ==> (y === z)))) by Tautology.from(uniquenessFromExistsOne of (P := lambda(y, R(x, y))), lastStep) + thenHave(forall(x, in(x, A) ==> ∀(y, ∀(z, (R(x, y) /\ R(x, z)) ==> (y === z))))) by RightForall + have(thesis) by Tautology.from(lastStep, replacementSchema of (SchematicPredicateLabel("P", 2) -> R)) + } + + /** + * Theorem --- Recursive Function Exists + * + * Given that for each element of the initial segment of `x`, a function as + * defined by [[wellOrderedRecursion]] exists, then a function with the same + * properties exists for `x`. + */ + val recursiveFunctionExistencePropagates = Theorem( + wellOrder(p) /\ in(x, p1) /\ forall(y, in(y, initialSegment(p, x)) ==> prop(y)) |- exists(g, fun(g, x)) + ) { + assume(wellOrder(p)) + assume(in(x, p1)) + assume(forall(y, in(y, initialSegment(p, x)) ==> prop(y))) + + // if w is as defined, there is a function g as required + have(wDef |- exists(g, fun(g, x))) subproof { + assume(wDef) + // properties of the elements of w + // 1. t \in w ==> functional t + // 2. t1, t2 \in w ==> t1 \subseteq t2 \/ t2 \subseteq t1 + + // 1. t \in w ==> functional t + // see [[elemsFunctional]] and [[elemsSubset]] + + // working with orderedRestrictions + val ordBreakdown = have(orderedRestriction(a, b, p) === restrictedFunction(a, initialSegment(p, b))) by InstantiateForall(orderedRestriction(a, b, p))( + orderedRestriction.definition of (f -> a, a -> b) + ) + + // see [[uwfunctional]] + + // now, from w, we will construct the requisite function g for x + // see [[uwRestrictedEq]] + + // common subproof + val zyInDomUw = have(in(z, initialSegment(p, y)) /\ in(y, initialSegment(p, x)) |- in(z, relationDomain(uw))) subproof { + assume(in(z, initialSegment(p, y)), in(y, initialSegment(p, x))) + + have(in(y, initialSegment(p, x)) ==> (in(y, p1) /\ existsOne(g, fun(g, y)))) by InstantiateForall + val gExists = have(exists(g, fun(g, y))) by Tautology.from(lastStep, existsOneImpliesExists of (P -> lambda(g, fun(g, y)))) + + have(fun(g, y) |- in(z, relationDomain(uw))) subproof { + assume(fun(g, y)) + + val zIng = have(in(z, relationDomain(g))) subproof { + have(functionalOver(g, initialSegment(p, y))) by Tautology + val domEQ = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(lastStep, functionalOver.definition of (f -> g, x -> initialSegment(p, y))) + + have(in(z, initialSegment(p, y))) by Restate + thenHave(thesis) by Substitution.ApplyRules(domEQ) + } + + val gInw = have(in(g, w)) subproof { + have(in(y, initialSegment(p, x)) /\ fun(g, y)) by Restate + val yEx = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by RightExists + + have(forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y)))) by Restate + thenHave(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall(g) + have(thesis) by Tautology.from(lastStep, yEx) + } + + have(forall(z, in(z, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(z, relationDomain(g))))) by Tautology.from(uwfunctional, domainOfFunctionalUnion of z -> w) + val zInUiff = thenHave(in(z, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(z, relationDomain(g)))) by InstantiateForall(z) + + have(in(g, w) /\ in(z, relationDomain(g))) by Tautology.from(zIng, gInw) + thenHave(exists(g, in(g, w) /\ in(z, relationDomain(g)))) by RightExists + have(in(z, relationDomain(uw))) by Tautology.from(lastStep, zInUiff) + } + + thenHave(exists(g, fun(g, y)) |- in(z, relationDomain(uw))) by LeftExists + have(in(z, relationDomain(uw))) by Cut.withParameters(exists(g, fun(g, y)))(gExists, lastStep) + } + + // there are two cases, either x has a predecessor, or it doesn't + val limSuccCases = have(limitElement(p, x) \/ successorElement(p, x)) by Tautology.from(everyElemInTotalOrderLimitOrSuccessor, wellOrder.definition) + + // if x has no predecessor, i.e., it is a limit element, w is the required function + val limitCase = have(limitElement(p, x) |- exists(g, fun(g, x))) subproof { + assume(limitElement(p, x)) + + // exists(y, in(pair(y, x), p2) /\ in(pair(elem, y), p2))) by Tautology.from(initialSegmentUnionForLimitElementsIsComplete of t -> elem, pIsATotalOrder) + + have(forall(t, in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g))))) by Tautology.from(domainOfFunctionalUnion of z -> w, uwfunctional) + val domDef = thenHave(in(elem, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by InstantiateForall(elem) + + have(exists(g, in(g, w) /\ in(elem, relationDomain(g))) <=> in(elem, initialSegment(p, x))) subproof { + val fwd = have(exists(g, in(g, w) /\ in(elem, relationDomain(g))) |- in(elem, initialSegment(p, x))) subproof { + have(in(g, w) /\ in(elem, relationDomain(g)) |- in(elem, initialSegment(p, x))) subproof { + assume(in(g, w)) + assume(in(elem, relationDomain(g))) + + // there must be a y < x that g is a recursive function for + have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall + val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology + + // elem is in dom g, which is g, x -> initialSegment(p, y))) + have(forall(elem, in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y)))) by Tautology.from( + lastStep, + extensionalityAxiom of (x -> relationDomain(g), y -> initialSegment(p, y)) + ) + thenHave(in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y))) by InstantiateForall(elem) + val eLTy = have(in(pair(elem, y), p2)) by Tautology.from(lastStep, initialSegmentElement of x -> elem, pIsAPartialOrder) + val yLTx = have(in(pair(y, x), p2)) by Tautology.from(initialSegmentElement of (x -> y, y -> x), pIsAPartialOrder) + + // apply transitivity + have(forall(elem, forall(y, forall(x, (in(pair(elem, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(elem, x), p2))))) by Weakening(wellOrderTransitivity) + thenHave((in(pair(elem, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(elem, x), p2)) by InstantiateForall(elem, y, x) + have(in(pair(elem, x), p2)) by Tautology.from(lastStep, eLTy, yLTx) + have(thesis) by Tautology.from(lastStep, initialSegmentElement of (y -> x, x -> elem), pIsAPartialOrder) + } + + thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- in(elem, initialSegment(p, x))) by LeftExists + have(thesis) by Tautology.from(yExists, lastStep) + } + + thenHave(thesis) by LeftExists + } + + val bwd = have(in(elem, initialSegment(p, x)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) subproof { + assume(in(elem, initialSegment(p, x))) + + // find a y s.t. elem < y < x + // this exists since x is a limit element + val yExists = have(exists(y, in(pair(elem, y), p2) /\ in(pair(y, x), p2))) by Tautology.from(limitProp, initialSegmentElement of (y -> x, x -> elem), pIsAPartialOrder) + + // given y, there is a g which is defined recursively for y + val gExists = have(in(pair(y, x), p2) |- exists(g, fun(g, y))) subproof { + have(in(y, initialSegment(p, x)) ==> (in(y, p1) /\ existsOne(g, fun(g, y)))) by InstantiateForall + have(thesis) by Tautology.from(lastStep, initialSegmentElement of (x -> y, y -> x), existsOneImpliesExists of P -> lambda(g, fun(g, y)), pIsAPartialOrder) + } + + // these g and y give us the required witness for elem + have((in(pair(elem, y), p2) /\ in(pair(y, x), p2), fun(g, y)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) subproof { + assume(in(pair(elem, y), p2) /\ in(pair(y, x), p2)) + assume(fun(g, y)) + + have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(functionalOver.definition of (f -> g, x -> initialSegment(p, y))) + have(forall(elem, in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y)))) by Tautology.from( + lastStep, + extensionalityAxiom of (x -> relationDomain(g), y -> initialSegment(p, y)) + ) + thenHave(in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y))) by InstantiateForall(elem) + + val elemInDom = have(in(elem, relationDomain(g))) by Tautology.from(lastStep, initialSegmentElement of x -> elem, pIsAPartialOrder) + + have(in(y, initialSegment(p, x)) /\ fun(g, y)) by Tautology.from(initialSegmentElement of (x -> y, y -> x), pIsAPartialOrder) + val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by RightExists + + have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall + have(in(g, w) /\ in(elem, relationDomain(g))) by Tautology.from(lastStep, yExists, elemInDom) + thenHave(thesis) by RightExists + } + + thenHave((in(pair(elem, y), p2) /\ in(pair(y, x), p2), exists(g, fun(g, y))) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by LeftExists + have((in(pair(elem, y), p2) /\ in(pair(y, x), p2)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by Tautology.from(lastStep, gExists) + thenHave(exists(y, in(pair(elem, y), p2) /\ in(pair(y, x), p2)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by LeftExists + have(thesis) by Cut(yExists, lastStep) + } + + have(thesis) by Tautology.from(fwd, bwd) + } + + have(in(elem, relationDomain(uw)) <=> in(elem, initialSegment(p, x))) by Tautology.from(lastStep, domDef) + thenHave(forall(elem, in(elem, relationDomain(uw)) <=> in(elem, initialSegment(p, x)))) by RightForall + have(relationDomain(uw) === initialSegment(p, x)) by Tautology.from(lastStep, extensionalityAxiom of (x -> relationDomain(uw), y -> initialSegment(p, x))) + + have(thesis) by Tautology.from(functionalOver.definition of (f -> uw, x -> initialSegment(p, x)), lastStep, uwfunctional) + } + + // z < x ==> uw z = F uw |^ z + have(in(z, initialSegment(p, x)) |- app(uw, z) === F(orderedRestriction(uw, z, p))) subproof { + assume(in(z, initialSegment(p, x))) + + // z in dom uw + have(in(z, relationDomain(uw))) subproof { + // \exists y. z < y < x as x is limit + // \exists g \in w. fun g y + // z in dom g + // so z in dom uw + have(in(pair(z, x), p2)) by Tautology.from(initialSegmentElement of (x -> z, y -> x), pIsAPartialOrder) + val yExists = have(exists(y, in(pair(z, y), p2) /\ in(pair(y, x), p2))) by Tautology.from(lastStep, initialSegmentUnionForLimitElementsIsComplete of t -> z, pIsATotalOrder) + + have(in(pair(z, y), p2) /\ in(pair(y, x), p2) |- in(z, relationDomain(uw))) subproof { + assume(in(pair(z, y), p2), in(pair(y, x), p2)) + + val zLTy = have(in(z, initialSegment(p, y))) by Tautology.from(initialSegmentElement of (x -> z, y -> y), pIsAPartialOrder) + val yLTx = have(in(y, initialSegment(p, x))) by Tautology.from(initialSegmentElement of (x -> y, y -> x), pIsAPartialOrder) + + have(thesis) by Tautology.from(zLTy, yLTx, zyInDomUw) + } + + thenHave(exists(y, in(pair(z, y), p2) /\ in(pair(y, x), p2)) |- in(z, relationDomain(uw))) by LeftExists + have(thesis) by Tautology.from(yExists, lastStep) + } + + have(thesis) by Tautology.from(lastStep, uwRestrictedEq) + } + + thenHave(in(z, initialSegment(p, x)) ==> (app(uw, z) === F(orderedRestriction(uw, z, p)))) by Restate + thenHave(forall(z, in(z, initialSegment(p, x)) ==> (app(uw, z) === F(orderedRestriction(uw, z, p))))) by RightForall + have(fun(uw, x)) by Tautology.from(lastStep, uwFunctionalOver) + thenHave(exists(g, fun(g, x))) by RightExists + } + + // if x has a predecessor, then we need to add an element to uw, giving us v as the requisite function + val successorCase = have(successorElement(p, x) |- exists(g, fun(g, x))) subproof { + assume(successorElement(p, x)) + // the right function is v = Uw \cup {(pred x, F Uw)} + // i.e., Uw with a recursive addition for the predecessor of x + // which is not included in any initial segment below x (! (pred x < y) for y < x) + // define pr as the predecessor of x + val pr = variable + val prFun = singleton(pair(pr, F(uw))) + val v = setUnion(uw, prFun) + + val vFun = have(predecessor(p, pr, x) |- fun(v, x)) subproof { + assume(predecessor(p, pr, x)) + // to this end, we show: + // 1. v is functional over (relationDomain(uw) === initialSegment(p, pr))) by Tautology.from( + functionalOver.definition of (f -> uw, x -> initialSegment(p, pr)), + uwfunctional + ) + + have(relationDomain(uw) === initialSegment(p, pr)) subproof { + val fwd = have(in(t, relationDomain(uw)) |- in(t, initialSegment(p, pr))) subproof { + assume(in(t, relationDomain(uw))) + + have(forall(t, in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g))))) by Tautology.from(uwfunctional, domainOfFunctionalUnion of z -> w) + thenHave(in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g)))) by InstantiateForall(t) + val gExists = thenHave(exists(g, in(g, w) /\ in(t, relationDomain(g)))) by Tautology + + have(in(g, w) /\ in(t, relationDomain(g)) |- in(t, initialSegment(p, pr))) subproof { + assume(in(g, w)) + assume(in(t, relationDomain(g))) + + have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall + val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology + + have(in(y, initialSegment(p, x)) /\ fun(g, y) |- in(t, initialSegment(p, pr))) subproof { + assume(in(y, initialSegment(p, x)), fun(g, y)) + // t < y + val domEQ = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(functionalOver.definition of (f -> g, x -> initialSegment(p, y))) + have(in(t, relationDomain(g))) by Restate + val tLTy = thenHave(in(t, initialSegment(p, y))) by Substitution.ApplyRules(domEQ) + + // y <= pr + val cases = have((y === pr) \/ in(y, initialSegment(p, pr))) by Tautology.from(initialSegmentPredecessorSplit of (y -> pr, z -> y), pIsATotalOrder) + + val eqCase = have((y === pr) |- in(t, initialSegment(p, pr))) by Substitution.ApplyRules(y === pr)(tLTy) + val initCase = have(in(y, initialSegment(p, pr)) |- in(t, initialSegment(p, pr))) by Tautology.from(tLTy, initialSegmentTransitivity of (x -> t, y -> y, z -> pr), pIsAPartialOrder) + + have(thesis) by Tautology.from(cases, eqCase, initCase) + } + + thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- in(t, initialSegment(p, pr))) by LeftExists + have(thesis) by Tautology.from(lastStep, yExists) + } + + thenHave(exists(g, in(g, w) /\ in(t, relationDomain(g))) |- in(t, initialSegment(p, pr))) by LeftExists + have(thesis) by Cut(gExists, lastStep) + } + + val bwd = have(in(t, initialSegment(p, pr)) |- in(t, relationDomain(uw))) subproof { + assume(in(t, initialSegment(p, pr))) + + have(in(pair(pr, x), p2)) by Tautology.from(predecessor.definition of (x -> pr, y -> x)) + val prLTx = have(in(pr, initialSegment(p, x))) by Tautology.from(lastStep, pIsAPartialOrder, initialSegmentElement of (y -> x, x -> pr)) + + have(forall(y, in(y, initialSegment(p, x)) ==> existsOne(g, fun(g, y)))) by Restate + thenHave(in(pr, initialSegment(p, x)) ==> existsOne(g, fun(g, pr))) by InstantiateForall(pr) + val gExists = have(exists(g, fun(g, pr))) by Tautology.from(lastStep, prLTx, existsOneImpliesExists of P -> lambda(g, fun(g, pr))) + + have(fun(g, pr) |- in(g, w) /\ in(t, relationDomain(g))) subproof { + assume(fun(g, pr)) + + have(in(pr, initialSegment(p, x)) /\ fun(g, pr)) by Tautology.from(prLTx) + val exPR = thenHave(exists(pr, in(pr, initialSegment(p, x)) /\ fun(g, pr))) by RightExists + + have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall + val gW = have(in(g, w)) by Tautology.from(lastStep, exPR) + + have(functionalOver(g, initialSegment(p, pr))) by Tautology + val domEQ = have(relationDomain(g) === initialSegment(p, pr)) by Tautology.from(lastStep, functionalOver.definition of (f -> g, x -> initialSegment(p, pr))) + + have(in(t, initialSegment(p, pr))) by Restate + thenHave(in(t, relationDomain(g))) by Substitution.ApplyRules(domEQ) + have(thesis) by Tautology.from(lastStep, gW) + } + + thenHave(fun(g, pr) |- exists(g, in(g, w) /\ in(t, relationDomain(g)))) by RightExists + thenHave(exists(g, fun(g, pr)) |- exists(g, in(g, w) /\ in(t, relationDomain(g)))) by LeftExists + val gInW = have(exists(g, in(g, w) /\ in(t, relationDomain(g)))) by Cut(gExists, lastStep) + + have(forall(t, in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g))))) by Tautology.from(uwfunctional, domainOfFunctionalUnion of z -> w) + thenHave(in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g)))) by InstantiateForall(t) + have(thesis) by Tautology.from(lastStep, gInW) + } + + have(in(t, relationDomain(uw)) <=> in(t, initialSegment(p, pr))) by Tautology.from(fwd, bwd) + thenHave(forall(t, in(t, relationDomain(uw)) <=> in(t, initialSegment(p, pr)))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> relationDomain(uw), y -> initialSegment(p, pr))) + } + + have(thesis) by Tautology.from(lastStep, iffDom) + } + + // 1. v is functional over pr, y -> F(uw)), functionalOver.definition of (f -> prFun, x -> singleton(pr))) + + // 3. (t === pr)) by Weakening(singletonHasNoExtraElements of (y -> t, x -> pr)) + + val initMembership = have(in(t, initialSegment(p, pr)) <=> in(pair(t, pr), p2)) by Tautology.from(initialSegmentElement of (x -> t, y -> pr), pIsAPartialOrder) + + have(in(t, singleton(pr)) /\ in(t, initialSegment(p, pr)) |- ()) subproof { + assume(in(t, singleton(pr))) + assume(in(t, initialSegment(p, pr))) + + val tEQpr = have(t === pr) by Tautology.from(singletonMembership) + val tLTpr = have(in(pair(t, pr), p2)) by Tautology.from(initMembership) + val prprp2 = thenHave(in(pair(pr, pr), p2)) by Substitution.ApplyRules(tEQpr) + + // but the order is anti reflexive + have(forall(pr, in(pr, p1) ==> !in(pair(pr, pr), p2))) by Tautology.from(pIsAPartialOrder, partialOrder.definition, antiReflexive.definition of (r -> p2, x -> p1)) + thenHave(in(pr, p1) ==> !in(pair(pr, pr), p2)) by InstantiateForall(pr) + have(!in(pair(pr, pr), p2)) by Tautology.from(lastStep, predecessor.definition of (x -> pr, y -> x)) + + have(thesis) by Tautology.from(lastStep, prprp2) + } + + val inEmpty = thenHave((in(t, singleton(pr)) /\ in(t, initialSegment(p, pr))) ==> in(t, emptySet)) by Weakening + + have(in(t, setIntersection(initialSegment(p, pr), singleton(pr))) <=> (in(t, singleton(pr)) /\ in(t, initialSegment(p, pr)))) by Tautology.from( + setIntersectionMembership of (x -> initialSegment(p, pr), y -> singleton(pr)) + ) + have(in(t, setIntersection(initialSegment(p, pr), singleton(pr))) ==> in(t, emptySet)) by Substitution.ApplyRules(lastStep)(inEmpty) + thenHave(forall(t, in(t, setIntersection(initialSegment(p, pr), singleton(pr))) ==> in(t, emptySet))) by RightForall + have(subset(setIntersection(initialSegment(p, pr), singleton(pr)), emptySet)) by Tautology.from( + lastStep, + subsetAxiom of (x -> setIntersection(initialSegment(p, pr), singleton(pr)), y -> emptySet) + ) + have(thesis) by Tautology.from(lastStep, emptySetIsItsOwnOnlySubset of x -> setIntersection(initialSegment(p, pr), singleton(pr))) + } + + // 4. So v is functional over uw, a -> initialSegment(p, pr), g -> prFun, b -> singleton(pr)) + ) + + // 5. and = pr \/ < pr + val initBreakdown = + have(in(t, initialSegment(p, x)) <=> ((t === pr) \/ in(t, initialSegment(p, pr)))) by Tautology.from(initialSegmentPredecessorSplit of (z -> t, y -> pr), pIsATotalOrder) + + val singletonMembership = have(in(t, singleton(pr)) <=> (t === pr)) by Tautology.from(singletonHasNoExtraElements of (y -> t, x -> pr)) + + val initMembership = have(in(t, initialSegment(p, x)) <=> (in(t, singleton(pr)) \/ in(t, initialSegment(p, pr)))) by Substitution.ApplyRules(singletonMembership)(initBreakdown) + + have(in(t, setUnion(initialSegment(p, pr), singleton(pr))) <=> (in(t, singleton(pr)) \/ in(t, initialSegment(p, pr)))) by Tautology.from( + setUnionMembership of (z -> t, x -> initialSegment(p, pr), y -> singleton(pr)) + ) + have(in(t, initialSegment(p, x)) <=> in(t, setUnion(initialSegment(p, pr), singleton(pr)))) by Substitution.ApplyRules(lastStep)(initMembership) + thenHave(forall(t, in(t, initialSegment(p, x)) <=> in(t, setUnion(initialSegment(p, pr), singleton(pr))))) by RightForall + + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> initialSegment(p, x), y -> setUnion(initialSegment(p, pr), singleton(pr)))) + } + + have(thesis) by Substitution.ApplyRules(unionEQ)(vFunctionalOverUnion) + } + + // 2. v is recursive + val vRecursive = have(forall(z, in(z, initialSegment(p, x)) ==> (app(v, z) === F(orderedRestriction(v, z, p))))) subproof { + have(in(z, initialSegment(p, x)) |- (app(v, z) === F(orderedRestriction(v, z, p)))) subproof { + assume(in(z, initialSegment(p, x))) + + // z is in dom v + val zInDom = have(in(z, relationDomain(v))) subproof { + val domEQ = have(relationDomain(v) === initialSegment(p, x)) by Tautology.from(functionalOver.definition of (f -> v, x -> initialSegment(p, x)), vFunctionalOver) + have(in(z, initialSegment(p, x))) by Restate + thenHave(thesis) by Substitution.ApplyRules(domEQ) + } + + // app v z is well defined + val vAppDef = have((a === app(v, z)) <=> in(pair(z, a), v)) subproof { + have(functional(v)) by Tautology.from(vFunctionalOver, functionalOver.definition of (f -> v, x -> initialSegment(p, x))) + have(thesis) by Tautology.from(lastStep, zInDom, pairInFunctionIsApp of (f -> v, a -> z, b -> a)) + } + + // z is either the predecessor or below it + val zSplit = have(((z === pr) \/ in(z, initialSegment(p, pr)))) by Tautology.from(pIsATotalOrder, initialSegmentPredecessorSplit of (y -> pr)) + + // uw is actually the ordered restriction of v to pr + val uwEq = have((uw === orderedRestriction(v, pr, p))) subproof { + val vpr = orderedRestriction(v, pr, p) + + // uw has domain v, x -> initialSegment(p, x))) + have(functionalOver(restrictedFunction(v, initialSegment(p, pr)), setIntersection(initialSegment(p, pr), relationDomain(v)))) by Tautology.from( + lastStep, + restrictedFunctionIsFunctionalOver of (f -> v, x -> initialSegment(p, pr)) + ) + val vprFun = thenHave(functionalOver(vpr, setIntersection(initialSegment(p, pr), relationDomain(v)))) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> pr)) + + have(relationDomain(v) === initialSegment(p, x)) by Tautology.from(vFunctionalOver, functionalOver.definition of (f -> v, x -> initialSegment(p, x))) + val initIntersection = have(functionalOver(vpr, setIntersection(initialSegment(p, pr), initialSegment(p, x)))) by Substitution.ApplyRules(lastStep)(vprFun) + + have(setIntersection(initialSegment(p, pr), initialSegment(p, x)) === initialSegment(p, pr)) by Tautology.from( + predecessorInInitialSegment of y -> pr, + initialSegmentIntersection of y -> pr, + pIsAPartialOrder, + pIsATotalOrder + ) + have(thesis) by Substitution.ApplyRules(lastStep)(initIntersection) + } + + // and they agree on their domain + have(in(b, initialSegment(p, pr)) |- (app(uw, b) === app(vpr, b))) subproof { + assume(in(b, initialSegment(p, pr))) + + val appDefw = have((a === app(uw, b)) <=> in(pair(b, a), uw)) by Tautology.from(functionalOverApplication of (f -> uw, x -> initialSegment(p, pr), b -> a, a -> b), domUW) + val appDefv = have((a === app(vpr, b)) <=> in(pair(b, a), vpr)) by Tautology.from(functionalOverApplication of (f -> vpr, x -> initialSegment(p, pr), b -> a, a -> b), domRV) + + val fwd = have(in(pair(b, a), uw) |- in(pair(b, a), vpr)) subproof { + assume(in(pair(b, a), uw)) + + have(in(pair(b, a), v)) by Tautology.from(setUnionMembership of (z -> pair(b, a), x -> uw, y -> prFun)) + have(in(pair(b, a), restrictedFunction(v, initialSegment(p, pr)))) by Tautology.from( + lastStep, + restrictedFunctionPairMembership of (f -> v, x -> initialSegment(p, pr), t -> b, a -> a) + ) + thenHave(thesis) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> pr)) + } + + val bwd = have(in(pair(b, a), vpr) |- in(pair(b, a), uw)) subproof { + assume(in(pair(b, a), vpr)) + have(in(pair(b, a), vpr)) by Restate + thenHave(in(pair(b, a), restrictedFunction(v, initialSegment(p, pr)))) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> pr)) + have(in(pair(b, a), v)) by Tautology.from(lastStep, restrictedFunctionPairMembership of (f -> v, x -> initialSegment(p, pr), t -> b, a -> a)) + val cases = have(in(pair(b, a), uw) \/ in(pair(b, a), prFun)) by Tautology.from(lastStep, setUnionMembership of (z -> pair(b, a), x -> uw, y -> prFun)) + + have(!in(pair(b, a), prFun)) subproof { + // towards a contradiction, assume otherwise + assume(in(pair(b, a), prFun)) + + have(pair(b, a) === pair(pr, F(uw))) by Tautology.from(singletonHasNoExtraElements of (y -> pair(b, a), x -> pair(pr, F(uw)))) + val bEQpr = have(b === pr) by Tautology.from(lastStep, pairExtensionality of (a -> b, b -> a, c -> pr, d -> F(uw))) + + have(in(b, initialSegment(p, pr))) by Restate + thenHave(in(pr, initialSegment(p, pr))) by Substitution.ApplyRules(bEQpr) + have(bot) by Tautology.from(lastStep, initialSegmentIrreflexivity of (x -> pr), pIsAPartialOrder) + } + + have(thesis) by Tautology.from(lastStep, cases) + } + + have(in(pair(b, a), uw) <=> in(pair(b, a), vpr)) by Tautology.from(fwd, bwd) + have((a === app(uw, b)) <=> (a === app(vpr, b))) by Tautology.from(appDefw, appDefv, lastStep) + have(thesis) by Restate.from(lastStep of a -> app(uw, b)) + } + + thenHave(in(b, initialSegment(p, pr)) ==> (app(uw, b) === app(vpr, b))) by Restate + thenHave(forall(b, in(b, initialSegment(p, pr)) ==> (app(uw, b) === app(vpr, b)))) by RightForall + + have(thesis) by Tautology.from(functionsEqualIfEqualOnDomain of (f -> uw, g -> vpr, a -> initialSegment(p, pr)), lastStep, domUW, domRV) + } + + // the property holds for the predecessor + val prCase = have((z === pr) |- (app(v, z) === F(orderedRestriction(v, z, p)))) subproof { + have(app(v, pr) === F(orderedRestriction(v, pr, p))) subproof { + val fuwEq = have(F(uw) === F(orderedRestriction(v, pr, p))) subproof { + have(F(uw) === F(uw)) by Restate + thenHave(thesis) by Substitution.ApplyRules(uwEq) + } + + // app v pr = uw + val appEq = have(app(v, pr) === F(uw)) subproof { + val pairInV = have(in(pair(pr, F(uw)), v)) by Tautology.from( + setUnionMembership of (z -> pair(pr, F(uw)), x -> uw, y -> prFun), + singletonHasNoExtraElements of (x -> pair(pr, F(uw)), y -> pair(pr, F(uw))) + ) + have(in(pr, initialSegment(p, x))) by Tautology.from(predecessorInInitialSegment of y -> pr, pIsATotalOrder) + have(thesis) by Tautology.from(vAppDef of (a -> F(uw), z -> pr), lastStep, pairInV) + } + + have(thesis) by Tautology.from(equalityTransitivity of (x -> app(v, pr), y -> F(uw), z -> F(orderedRestriction(v, pr, p))), fuwEq, appEq) + } + + thenHave(thesis) by Substitution.ApplyRules(z === pr) + } + + // the property holds for elements < pr + val wCase = have(in(z, initialSegment(p, pr)) |- (app(v, z) === F(orderedRestriction(v, z, p)))) subproof { + assume(in(z, initialSegment(p, pr))) + + // uw is functional over z, x -> pr), pIsAPartialOrder) + + thenHave(thesis) by Substitution.ApplyRules(setIntersectionCommutativity of (x -> initialSegment(p, z), y -> initialSegment(p, pr))) + } + + have(orderedRestriction(orderedRestriction(v, pr, p), z, p) === orderedRestriction(orderedRestriction(v, pr, p), z, p)) by Restate + thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(orderedRestriction(v, pr, p), initialSegment(p, z))) by Substitution.ApplyRules( + ordBreakdown of (a -> orderedRestriction(v, pr, p), b -> z) + ) + thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(restrictedFunction(v, initialSegment(p, pr)), initialSegment(p, z))) by Substitution + .ApplyRules( + ordBreakdown of (a -> v, b -> pr) + ) + thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(v, setIntersection(initialSegment(p, pr), initialSegment(p, z)))) by Substitution + .ApplyRules( + restrictedFunctionAbsorption of (f -> v, x -> initialSegment(p, pr), y -> initialSegment(p, z)) + ) + thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(v, initialSegment(p, z))) by Substitution.ApplyRules(intersectionEQ) + thenHave(thesis) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> z)) + } + + have(thesis) by Tautology.from( + lastStep, + doubleRestriction, + equalityTransitivity of (x -> orderedRestriction(uw, z, p), y -> orderedRestriction(orderedRestriction(v, pr, p), z, p), z -> orderedRestriction(v, z, p)) + ) + } + + val restrictionFVW = have(F(orderedRestriction(v, z, p)) === F(orderedRestriction(uw, z, p))) subproof { + have(F(orderedRestriction(v, z, p)) === F(orderedRestriction(v, z, p))) by Restate + thenHave(thesis) by Substitution.ApplyRules(restrictionVW) + } + + // app v z = app uw z + val appVW = have(app(v, z) === app(uw, z)) subproof { + have(app(uw, z) === app(uw, z)) by Restate + val uwToOrd = thenHave(app(uw, z) === app(orderedRestriction(v, pr, p), z)) by Substitution.ApplyRules(uwEq) + + have(orderedRestriction(v, pr, p) === restrictedFunction(v, initialSegment(p, pr))) by InstantiateForall(orderedRestriction(v, pr, p))( + orderedRestriction.definition of (f -> v, a -> pr) + ) + val uwToRest = have(app(uw, z) === app(restrictedFunction(v, initialSegment(p, pr)), z)) by Substitution.ApplyRules(lastStep)(uwToOrd) + + have(app(restrictedFunction(v, initialSegment(p, pr)), z) === app(v, z)) by Tautology.from(restrictedFunctionApplication of (f -> v, x -> initialSegment(p, pr), y -> z)) + have(app(uw, z) === app(v, z)) by Substitution.ApplyRules(lastStep)(uwToRest) + + } + + // app uw z = F (uw |^ z) + val appRestrictionUW = have(app(uw, z) === F(orderedRestriction(uw, z, p))) subproof { + // z in dom uw + have(in(z, relationDomain(uw))) subproof { + // z < pr < x + // \exists g \in w. fun g pr + // z in dom g + // so z in dom uw + val zLTpr = have(in(z, initialSegment(p, pr))) by Restate + val prLTx = have(in(pr, initialSegment(p, x))) by Tautology.from(predecessorInInitialSegment of y -> pr, pIsATotalOrder) + + have(thesis) by Tautology.from(zLTpr, prLTx, zyInDomUw of y -> pr) + } + + have(thesis) by Tautology.from(lastStep, uwRestrictedEq) + } + + // equality transitivity + have(app(v, z) === F(orderedRestriction(uw, z, p))) by Tautology.from( + equalityTransitivity of (x -> app(v, z), y -> app(uw, z), z -> F(orderedRestriction(uw, z, p))), + appVW, + appRestrictionUW + ) + have(app(v, z) === F(orderedRestriction(v, z, p))) by Tautology.from( + equalityTransitivity of (x -> app(v, z), y -> F(orderedRestriction(uw, z, p)), z -> F(orderedRestriction(v, z, p))), + lastStep, + restrictionFVW + ) + } + + have(thesis) by Tautology.from(zSplit, prCase, wCase) + } + + thenHave(in(z, initialSegment(p, x)) ==> (app(v, z) === F(orderedRestriction(v, z, p)))) by Restate + thenHave(thesis) by RightForall + } + + have(fun(v, x)) by Tautology.from(vRecursive, vFunctionalOver) + } + + val prExists = have(exists(pr, predecessor(p, pr, x))) by Tautology.from(successorElement.definition) + + have(predecessor(p, pr, x) |- exists(g, fun(g, x))) by RightExists(vFun) + thenHave(exists(pr, predecessor(p, pr, x)) |- exists(g, fun(g, x))) by LeftExists + have(thesis) by Cut(prExists, lastStep) + } + + have(thesis) by Tautology.from(limSuccCases, limitCase, successorCase) + } + + val funExists = thenHave(exists(w, wDef) |- exists(g, fun(g, x))) by LeftExists + + have(exists(w, wDef)) subproof { + // restating the definition of w + // val wDef = forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) + // forall t. t \in w <=> \exists y < x. fun(t, y) + + // we know that there exists a *unique* g for each y in the initial segment + // so, there is a functional map from the initial segment that produces these values + // by replacement, we can take the image of the initial segment + // restating replacement: + // forall(a, in(a, x) ==> existsOne(b, sPsi(x, a, b))) ==> + // exists(y, forall(b, in(b, y) <=> exists(a, in(a, x) /\ sPsi(x, a, b)))) + val sPsi = SchematicPredicateLabel("P", 3) + + have(forall(y, in(y, initialSegment(p, x)) ==> existsOne(g, fun(g, y)))) by Restate + have(exists(w, forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))))) by Tautology.from( + lastStep, + strictReplacementSchema of (A -> initialSegment(p, x), R -> lambda((y, g), fun(g, y))) + ) + } + + have(thesis) by Tautology.from(lastStep, funExists) + } + + /** + * Theorem --- Well-Ordered Recursion + * + * Given any element `t` of a well order `p`, and a class function `F`, there + * exists a unique function `g` with ` existsOne(g, (functionalOver(g, initialSegment(p, t)) /\ forall(a, in(a, initialSegment(p, t)) ==> (app(g, a) === F(orderedRestriction(g, a, p)))))) + ) + ) { + assume(wellOrder(p)) + + val pIsAPartialOrder = have(partialOrder(p)) by Tautology.from(wellOrder.definition, totalOrder.definition) + val pIsATotalOrder = have(totalOrder(p)) by Tautology.from(wellOrder.definition) + + // the existence of g propagates up from initial segments + val initPropagate = have(in(x, p1) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x))) subproof { + + assume( + in(x, p1), + forall(y, in(y, initialSegment(p, x)) ==> prop(y)) + ) + + // see [[uniqueRecursiveFunction]] and [[recursiveFunctionExistencePropagates]] + + have(thesis) by Tautology.from(recursiveFunctionExistencePropagates, uniqueRecursiveFunction of t -> x) + } + + // so we induct on the well-ordering + thenHave(forall(x, in(x, p1) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x)))) by RightForall + have(thesis) by Tautology.from(lastStep, wellOrderedInduction of Q -> lambda(x, prop(x))) + } + show + + /** + * Theorem --- Transfinite Recursion + * + * Given any ordinal `a` and a class function `F`, there exists a unique + * function `g` with `a` as its domain, defined recursively as + * + * `\forall b < a. g(b) = F(g |^ b)` + * + * where `g |^ b` is `g` with its domain restricted to `b`. + */ + val transfiniteRecursion = Theorem( + ordinal(a) |- existsOne(g, functionalOver(g, a) /\ forall(b, in(b, a) ==> (app(g, b) === F(restrictedFunction(g, b))))) + ) { + sorry + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala new file mode 100644 index 000000000..00c85ab76 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala @@ -0,0 +1,225 @@ +package lisa.maths.settheory.orderings + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.functions.* +import lisa.maths.settheory.orderings.InclusionOrders.* +import lisa.maths.settheory.orderings.PartialOrders.* +import lisa.maths.settheory.orderings.WellOrders.* + +import Ordinals.* + +object Segments extends lisa.Main { + + // var defs + private val w = variable + private val x = variable + private val y = variable + private val z = variable + private val h = formulaVariable + private val t = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + + // relation and function symbols + private val r = variable + private val p = variable + private val q = variable + private val f = variable + private val g = variable + private val F = function[1] + private val G = function[2] + + private val P = predicate[1] + private val Q = predicate[1] + private val schemPred = predicate[1] + + val initialSegmentUniqueness = Lemma( + existsOne(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ in(pair(t, a), secondInPair(p))))) + ) { + have(thesis) by UniqueComprehension(firstInPair(p), lambda(t, in(pair(t, a), secondInPair(p)))) + } + + val initialSegment = DEF(p, a) --> The(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ in(pair(t, a), secondInPair(p)))))(initialSegmentUniqueness) + + val initialSegmentLeqUniqueness = Lemma( + existsOne(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ (in(pair(t, a), secondInPair(p)) \/ (t === a))))) + ) { + have(thesis) by UniqueComprehension(firstInPair(p), lambda(t, (in(pair(t, a), secondInPair(p)) \/ (t === a)))) + } + + val initialSegmentLeq = DEF(p, a) --> The(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ (in(pair(t, a), secondInPair(p)) \/ (t === a)))))(initialSegmentLeqUniqueness) + + val initialSegmentOrderUniqueness = Lemma( + existsOne(z, forall(t, in(t, z) <=> (in(t, secondInPair(p)) /\ (in(firstInPair(t), initialSegment(p, a)) /\ in(secondInPair(t), initialSegment(p, a)))))) + ) { + have(thesis) by UniqueComprehension(secondInPair(p), lambda(t, (in(firstInPair(t), initialSegment(p, a)) /\ in(secondInPair(t), initialSegment(p, a))))) + } + + val initialSegmentOrder = + DEF(p, a) --> The(z, forall(t, in(t, z) <=> (in(t, secondInPair(p)) /\ (in(firstInPair(t), initialSegment(p, a)) /\ in(secondInPair(t), initialSegment(p, a))))))(initialSegmentOrderUniqueness) + + val initialSegmentElement = Lemma( + partialOrder(p) |- in(pair(x, y), secondInPair(p)) <=> in(x, initialSegment(p, y)) + ) { + assume(partialOrder(p)) + + val p1 = firstInPair(p) + val p2 = secondInPair(p) + + val fwd = have(in(pair(x, y), p2) |- in(x, initialSegment(p, y))) subproof { + assume(in(pair(x, y), p2)) + + // p2 is a relation on p1 + have(relationBetween(p2, p1, p1)) by Tautology.from(partialOrder.definition) + val xInp1 = have(in(x, p1)) by Tautology.from(lastStep, pairInRelation of (r -> p2, a -> p1, b -> p1)) + have(forall(x, in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2)))) by InstantiateForall(initialSegment(p, y))(initialSegment.definition of a -> y) + thenHave(in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2))) by InstantiateForall(x) + + have(thesis) by Tautology.from(xInp1, lastStep) + } + + val bwd = have(in(x, initialSegment(p, y)) |- in(pair(x, y), p2)) subproof { + have(forall(x, in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2)))) by InstantiateForall(initialSegment(p, y))(initialSegment.definition of a -> y) + thenHave(in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2))) by InstantiateForall(x) + thenHave(thesis) by Tautology + } + + have(thesis) by Tautology.from(fwd, bwd) + } + + val initialSegmentBaseElement = Lemma( + partialOrder(p) /\ in(x, initialSegment(p, y)) |- in(x, firstInPair(p)) + ) { + assume(partialOrder(p)) + assume(in(x, initialSegment(p, y))) + + val p1 = firstInPair(p) + val p2 = secondInPair(p) + + val pairInp2 = have(in(pair(x, y), p2)) by Tautology.from(initialSegmentElement) + have(relationBetween(p2, p1, p1)) by Tautology.from(partialOrder.definition) + have(in(x, p1)) by Tautology.from(lastStep, pairInp2, pairInRelation of (r -> p2, a -> p1, b -> p1)) + } + + val initialSegmentIrreflexivity = Lemma( + partialOrder(p) |- !in(x, initialSegment(p, x)) + ) { + sorry + } + + val predecessorInInitialSegment = Lemma( + totalOrder(p) /\ predecessor(p, y, x) |- in(y, initialSegment(p, x)) + ) { + assume(totalOrder(p)) + assume(predecessor(p, y, x)) + + have(in(pair(y, x), secondInPair(p))) by Tautology.from(predecessor.definition of (x -> y, y -> x)) + have(in(y, initialSegment(p, x))) by Tautology.from(lastStep, totalOrder.definition, initialSegmentElement of (x -> y, y -> x)) + } + + val initialSegmentsSubset = Lemma( + partialOrder(p) /\ in(pair(x, y), secondInPair(p)) |- subset(initialSegment(p, x), initialSegment(p, y)) + ) { + // t \in t \in x, y -> y)) + val yz = have(in(pair(y, z), secondInPair(p))) by Tautology.from(initialSegmentElement of (x -> y, y -> z)) + + have(forall(x, forall(y, forall(z, (in(pair(x, y), secondInPair(p)) /\ in(pair(y, z), secondInPair(p))) ==> in(pair(x, z), secondInPair(p)))))) by Tautology.from( + partialOrder.definition, + transitive.definition of (r -> secondInPair(p), x -> firstInPair(p)) + ) + thenHave((in(pair(x, y), secondInPair(p)) /\ in(pair(y, z), secondInPair(p))) ==> in(pair(x, z), secondInPair(p))) by InstantiateForall(x, y, z) + have(in(pair(x, z), secondInPair(p))) by Tautology.from(lastStep, xy, yz) + + have(thesis) by Tautology.from(lastStep, initialSegmentElement of (x -> x, y -> z)) + } + + val initialSegmentLeqBreakdown = Lemma( + in(t, initialSegmentLeq(p, a)) <=> (in(t, initialSegment(p, a)) \/ (t === a)) + ) { + sorry + } + + val initialSegmentOrderTotal = Lemma( + totalOrder(p) /\ in(a, firstInPair(p)) |- total(initialSegmentOrder(p, a), initialSegment(p, a)) + ) { + sorry + } + + // initial segment of well order is well ordered under the restricted order + val initialSegmentWellOrdered = Lemma( + wellOrder(p) /\ in(a, firstInPair(p)) |- wellOrder(pair(initialSegment(p, a), initialSegmentOrder(p, a))) + ) { + + sorry + } + + val initialSegmentPredecessorSplit = Lemma( + totalOrder(p) /\ predecessor(p, y, x) |- in(z, initialSegment(p, x)) <=> ((z === y) \/ in(z, initialSegment(p, y))) + ) { + sorry + } + + val initialSegmentIntersection = Lemma( + partialOrder(p) /\ in(y, initialSegment(p, x)) |- setIntersection(initialSegment(p, y), initialSegment(p, x)) === initialSegment(p, y) + ) { + sorry + } + + /** + * The restriction of a function `f` with respect to `a` relative to a + * partial order `p = (X, <)`. The result is `f` with its domain restricted + * to the elements less than `a` wrt `<`. + */ + val orderedRestriction = DEF(f, a, p) --> restrictedFunction(f, initialSegment(p, a)) + + /** + * Theorem --- Ordered Restriction Membership + * + * A pair `b` is in the ordered restriction of a function `f` to the initial + * segment of `a` under a partial order `p` iff it is in `f` and its first element + * (the one in the domain) is in the initial segment of `a` + */ + val orderedRestrictionMembership = Lemma( + partialOrder(p) |- in(b, orderedRestriction(f, a, p)) <=> (in(b, f) /\ in(firstInPair(b), initialSegment(p, a))) + ) { + sorry + } + + val orderedRestrictionFunctionalOverInit = Lemma( + in(a, initialSegment(p, b)) /\ functionalOver(f, initialSegment(p, b)) |- functionalOver(orderedRestriction(f, a, p), initialSegment(p, a)) + ) { + sorry + } + + val orderedRestrictionAgreement = Lemma( + partialOrder(p) /\ in(b, initialSegment(p, a)) /\ in(b, relationDomain(f)) /\ in(b, relationDomain(g)) |- (app(orderedRestriction(f, a, p), b) === app(orderedRestriction(g, a, p), b)) <=> (app( + f, + b + ) === app(g, b)) + ) { + sorry + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala new file mode 100644 index 000000000..01e68326c --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala @@ -0,0 +1,140 @@ +package lisa.maths.settheory.orderings + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.orderings.InclusionOrders.* +import lisa.maths.settheory.orderings.PartialOrders.* + +object WellOrders extends lisa.Main { + + // var defs + private val w = variable + private val x = variable + private val y = variable + private val z = variable + private val h = formulaVariable + private val t = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + + // relation and function symbols + private val r = variable + private val p = variable + private val q = variable + private val f = variable + private val g = variable + private val F = function[1] + private val G = function[2] + + private val P = predicate[1] + private val Q = predicate[1] + private val schemPred = predicate[1] + + /** + * Well-Order --- a partial order `p = (A, <)` is said to be a well-order if + * it is total and if every subset of `A` has a least element under `<`. + */ + val wellOrder = DEF(p) --> { + val A = firstInPair(p) + val B = variable + val ` exists(z, in(z, B) /\ forall(x, in(x, B) ==> (in(pair(z, x), ` (totalOrder(pair(emptySet, emptySet)) /\ forall( + b, + (subset(b, emptySet) /\ !(b === emptySet)) ==> exists(z, in(z, b) /\ forall(x, in(x, b) ==> (in(pair(z, x), secondInPair(pair(emptySet, emptySet))) \/ (z === x)))) + )) + ) by Substitution.ApplyRules(firstInPairReduction of (x -> emptySet, y -> emptySet))(wellOrder.definition of p -> pair(emptySet, emptySet)) + + have((subset(b, emptySet) /\ !(b === emptySet)) ==> exists(z, in(z, b) /\ forall(x, in(x, b) ==> (in(pair(z, x), secondInPair(pair(emptySet, emptySet))) \/ (z === x))))) by Tautology.from( + emptySetIsItsOwnOnlySubset of x -> b + ) + thenHave( + forall( + b, + (subset(b, emptySet) /\ !(b === emptySet)) ==> exists(z, in(z, b) /\ forall(x, in(x, b) ==> (in(pair(z, x), secondInPair(pair(emptySet, emptySet))) \/ (z === x)))) + ) + ) by RightForall + + have(thesis) by Tautology.from(lastStep, woDef, emptySetTotalOrder) + } + + /** + * Useful inherited properties + */ + + /** + * Well-orders inherit relational-transitivity from being partial orders. + */ + val wellOrderTransitivity = Lemma( + wellOrder(p) |- forall(w, forall(y, forall(z, (in(pair(w, y), secondInPair(p)) /\ in(pair(y, z), secondInPair(p))) ==> in(pair(w, z), secondInPair(p))))) + ) { + have(thesis) by Tautology.from(wellOrder.definition, totalOrder.definition, partialOrder.definition, transitive.definition of (r -> secondInPair(p), x -> firstInPair(p))) + } + + val transitiveSet = DEF(x) --> forall(y, in(y, x) ==> subset(y, x)) + + /** + * Theorem --- the definition of a transitive set in terms of inclusion is + * equivalent to the subset based definition. + */ + val transitiveSetInclusionDef = Theorem( + () |- transitiveSet(x) <=> forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) + ) { + // prove defs equal + have(forall(y, in(y, x) ==> subset(y, x)) <=> forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) subproof { + val lhs = have(forall(y, in(y, x) ==> subset(y, x)) |- forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) subproof { + have(forall(y, in(y, x) ==> subset(y, x)) |- forall(y, in(y, x) ==> subset(y, x))) by Hypothesis + thenHave((forall(y, in(y, x) ==> subset(y, x)), in(y, x)) |- subset(y, x)) by InstantiateForall(y) + thenHave((forall(y, in(y, x) ==> subset(y, x)), in(y, x)) |- forall(z, in(z, y) ==> in(z, x))) by Substitution.ApplyRules(subsetAxiom of (x -> y, y -> x)) + thenHave((forall(y, in(y, x) ==> subset(y, x)), in(y, x)) |- in(z, y) ==> in(z, x)) by InstantiateForall(z) + thenHave((forall(y, in(y, x) ==> subset(y, x))) |- (in(z, y) /\ in(y, x)) ==> in(z, x)) by Restate + thenHave((forall(y, in(y, x) ==> subset(y, x))) |- forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) by RightForall + thenHave((forall(y, in(y, x) ==> subset(y, x))) |- forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) by RightForall + } + + val rhs = have(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- forall(y, in(y, x) ==> subset(y, x))) subproof { + have(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) by Hypothesis + thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) by InstantiateForall(z) + thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) /\ in(y, x) |- (in(z, y)) ==> in(z, x)) by InstantiateForall(y) + thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) /\ in(y, x) |- forall(z, in(z, y) ==> in(z, x))) by RightForall + thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) /\ in(y, x) |- subset(y, x)) by Substitution.ApplyRules(subsetAxiom of (x -> y, y -> x)) + thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- in(y, x) ==> subset(y, x)) by Restate + thenHave(thesis) by RightForall + } + + have(thesis) by Tautology.from(lhs, rhs) + } + + have(thesis) by Tautology.from(lastStep, transitiveSet.definition) + } + + val inclusionOnTransitiveSetIsPartialOrder = Theorem( + transitiveSet(a) |- partialOrder(inclusionOrderOn(a)) + ) { + + // inclusion is a relation + // anti reflexive + // anti symmetric + have(relationBetween(inclusionOn(a), a, a) /\ antiReflexive(inclusionOn(a), a) /\ antiSymmetric(inclusionOn(a), a)) by Tautology.from( + inclusionIsRelation, + inclusionIsAntiReflexive, + inclusionIsAntiSymmetric + ) + + // and transitive + + sorry + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala new file mode 100644 index 000000000..0fbc4b1c1 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala @@ -0,0 +1,530 @@ +package lisa.maths.settheory.types + +import lisa.prooflib.ProofTacticLib.* +import lisa.fol.FOL +import lisa.automation.Tautology +import lisa.fol.FOL.{*, given} +import lisa.prooflib.BasicStepTactic.* +import lisa.prooflib.SimpleDeducedSteps.* +import lisa.SetTheoryLibrary.{given, *} +import lisa.SetTheoryLibrary +import lisa.kernel.proof.SequentCalculus.SCProofStep +import lisa.prooflib.OutputManager +import lisa.maths.settheory.SetTheory.singleton +import lisa.maths.settheory.functions.{functional, app, functionFromApplication, |=>} + +import annotation.nowarn + +object TypeLib extends lisa.Main { + import TypeSystem.* + + val f = variable + val x = variable + val y = variable + val a = variable + val z = variable + val A = variable + val B = variable + val F = function[1] + val any = DEF(x) --> top + + + // A |=> B is the set of functions from A to B + // C |> D is the functional class of functionals from the class C to the class D + // F is C |> D desugars into ∀(x, (x is C) => (F(x) is D)) + + val testTheorem = Theorem((x `is` A, f `is` (A |=> B), F `is` (A |=> B) |> (A |=> B) ) |- (F(f)*(x) `is` B)) { + have(thesis) by TypeChecker.prove + } + + val 𝔹 = DEF() --> unorderedPair(∅, singleton(∅)) + + val empty_in_B = Theorem( (∅ :: 𝔹) ) { + have( ∅ :: unorderedPair(∅, singleton(∅))) by Tautology.from(pairAxiom of (z := ∅, x := ∅, y := singleton(∅))) + thenHave(thesis ) by Substitution.ApplyRules(𝔹.shortDefinition) + } + + val sing_empty_in_B = Theorem( (singleton(∅) :: 𝔹) ) { + have( singleton(∅) :: unorderedPair(∅, singleton(∅))) by Tautology.from(pairAxiom of (z := singleton(∅), x := ∅, y := singleton(∅))) + thenHave(thesis ) by Substitution.ApplyRules(𝔹.shortDefinition) + } + + val Zero = DEF() --> (∅.typedWith(𝔹)(empty_in_B), 𝔹) + + val One = { + val One = DEF() --> singleton(∅) + val One_in_B = Theorem( (One :: 𝔹) ) { + have(thesis) by Substitution.ApplyRules(One.shortDefinition)(sing_empty_in_B) + } + One.typedWith(𝔹)(One_in_B) + } + + val zero_in_B = Theorem( (Zero :: 𝔹) ) { + have( Zero :: 𝔹) by TypeChecker.prove + } + + +} + +object TypeSystem { + + import TypeLib.{ + f, x, y, a, any, definition, given + } + + type Class = Term | (Term**1 |-> Formula) + + type SmallClass = Term + + case class FunctionalClass(in: Seq[Class], args: Seq[Variable], out: Class, arity: Int) { + def formula[N <: Arity](f: (Term**N |-> Term)): Formula = + val inner = (args.zip(in.toSeq).map((term, typ) => (term `is` typ).asFormula).reduceLeft((a, b) => a /\ b)) ==> (f.applySeq(args) `is` out) + args.foldRight(inner)((v, form) => forall(v, form)) + + override def toString(): String = in.map(_.toStringSeparated()).mkString("(", ", ", ")") + " |> " + out.toStringSeparated() + } + object FunctionalClass { + def apply(in: Seq[Class], out: Class, arity: Int): FunctionalClass = FunctionalClass(in, Seq.empty, out, arity) + } + + extension [N <: Arity](f: (Term**N |-> Term)) { + def is (typ: FunctionalClass): FunctionalTypeAssignment[N] & Formula = FunctionalTypeAssignment[N](f, typ).asInstanceOf + } + + extension[A <: Class] (c: Class) { + //(1 to arity).map(i => freshVariable(in.toSeq :+ out, "%x" + i)) + def |>(out: Class): FunctionalClass = FunctionalClass(Seq(c),Seq(freshVariable(Seq(out), "$x")), out, 1) + def |>(out: FunctionalClass): FunctionalClass = + val newVar = freshVariable(out.in.toSeq :+ out.out, "$x") + FunctionalClass(c +: out.in, newVar +: out.args, out.out, out.arity+1) + def toStringSeparated(): String = c match + case t: Term => t.toStringSeparated() + case f: (Term**1 |-> Formula) @unchecked => f.toString() + } + + extension [A <: Class](t: Term) { + def is(clas:A): Formula & TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula & TypeAssignment[A]] + def ::(clas:A): Formula & TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula & TypeAssignment[A]] + def @@(t2: Term): AppliedFunction = AppliedFunction(t, t2) + def *(t2: Term): AppliedFunction = AppliedFunction(t, t2) + } + + + object * {def unapply(t: Term): Option[(Term, Term)] = t match { + case AppliedFunction(f, a) => Some((f, a)) + case app(f, a) => Some((f, a)) + case _ => None + }} + + + /** + * A type assumption is a pair of a variable and a type. + * It is also a formula, equal to the type applied to the variable. + */ + sealed trait TypeAssignment[A <: Class]{ + this: Formula => + val t: Term + val typ: A + val asFormula: Formula = this + + override def toString() = + t.toStringSeparated() + "::" + typ.toStringSeparated() + override def toStringSeparated(): String = "(" + toString() + ")" + } + object TypeAssignment { + + /** + * A type assumption is a pair of a variable and a type. + * It is also a formula, equal to the type applied to the variable. + */ + def apply[A <: Class](t: Term, typ:A): TypeAssignment[A] = + val form = typ match + case f: Term => in(t, f) + case f : (Term**1 |-> Formula) @unchecked => + ((f: Term**1 |-> Formula)(t): Formula) + form match + case f: VariableFormula => + throw new IllegalArgumentException("Class formula cannot be a variable formula, as we require a type to have no free variable.") + case f: ConstantFormula => new TypeAssignmentConstant(t, typ, f) + case f: AppliedPredicate => new TypeAssignmentPredicate(t, typ, f) + case f: AppliedConnector => new TypeAssignmentConnector(t, typ, f) + case f: BinderFormula => new TypeAssignmentBinder(t, typ, f) + + def unapply(ta: Formula): Option[(Term, Class)] = ta match + case ta: TypeAssignment[?] => Some((ta.t, ta.typ)) + case in(x, set) => Some((x, set)) + case AppliedPredicate(label, args) if label.arity == 1 => Some((args.head, label.asInstanceOf[Term**1 |-> Formula])) + case _ => None + + } + val is = TypeAssignment + + given [A <: Class]: Conversion[TypeAssignment[A], Formula] = _.asInstanceOf[Formula] + /* + given [A <: Class]: Conversion[TypeAssignment[A], Sequent] = ta => Sequent(Set.empty, Set(ta)) + given [A <: Class]: FormulaSetConverter[TypeAssignment[A]] with { + override def apply(f: TypeAssignment[A]): Set[Formula] = Set(f.asInstanceOf[Formula]) + } +*/ + + + private class TypeAssignmentConstant[A <: Class](val t: Term, val typ:A, formula: ConstantFormula) extends ConstantFormula(formula.id) with TypeAssignment[A] + private class TypeAssignmentPredicate[A <: Class](val t: Term, val typ:A, formula: AppliedPredicate) extends AppliedPredicate(formula.label, formula.args) with TypeAssignment[A] { + + override def substituteUnsafe(map: Map[FOL.SchematicLabel[?], FOL.LisaObject[?]]): FOL.Formula = + if map.keySet.exists(_ == typ) then super.substituteUnsafe(map) + else + val newArgs = args.map(_.substituteUnsafe(map)) + if newArgs == args then this else + val newTyp = (typ: LisaObject[?]).substituteUnsafe(map).asInstanceOf[A] + + TypeAssignmentPredicate(t.substituteUnsafe(map), newTyp, formula.copy(args = newArgs)) + } + private class TypeAssignmentConnector[A <: Class](val t: Term, val typ:A, formula: AppliedConnector) extends AppliedConnector(formula.label, formula.args) with TypeAssignment[A] + private class TypeAssignmentBinder[A <: Class](val t: Term, val typ:A, formula: BinderFormula) extends BinderFormula(formula.f, formula.bound, formula.body) with TypeAssignment[A] + + + type TypingContext = Iterable[TypeAssignment[?]] + + + sealed trait FunctionalTypeAssignment[N <: Arity]{ + this: Formula => + val functional: Term**N |-> Term + val typ: FunctionalClass + val asFormula: Formula = this + + + override def toString() = functional.toString() + " : " + typ.toString() + override def toStringSeparated(): String = "(" + toString + ")" + + } + object FunctionalTypeAssignment { + def apply[N <: Arity](functional: Term**N |-> Term, typ: FunctionalClass): FunctionalTypeAssignment[N] = + val form = typ.formula(functional) + form match + case fo: VariableFormula => + throw new IllegalArgumentException("Class formula cannot be a variable formula, as we require a type to have no free variable.") + case fo: ConstantFormula => new FunctionalTypeAssignmentConstant(functional, typ, fo) + case fo: AppliedPredicate => new FunctionalTypeAssignmentPredicate(functional, typ, fo) + case fo: AppliedConnector => new FunctionalTypeAssignmentConnector(functional, typ, fo) + case fo: BinderFormula => new FunctionalTypeAssignmentBinder(functional, typ, fo) + + def unapply[N <: Arity](f: Formula): Option[((Term ** N) |-> Term, FunctionalClass)] = + f match + case ta: FunctionalTypeAssignment[N] @unchecked => Some((ta.functional, ta.typ)) + case _ => None + } + private class FunctionalTypeAssignmentConstant[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: ConstantFormula) extends ConstantFormula(formula.id) with FunctionalTypeAssignment[N] + private class FunctionalTypeAssignmentPredicate[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: AppliedPredicate) extends AppliedPredicate(formula.label, formula.args) with FunctionalTypeAssignment[N] + private class FunctionalTypeAssignmentConnector[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: AppliedConnector) extends AppliedConnector(formula.label, formula.args) with FunctionalTypeAssignment[N] + private class FunctionalTypeAssignmentBinder[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: BinderFormula) extends BinderFormula(formula.f, formula.bound, formula.body) with FunctionalTypeAssignment[N] + + + + + + class TypedConstant[A <: Class] + (id: Identifier, val typ: A, val justif: JUSTIFICATION) extends Constant(id) with LisaObject[TypedConstant[A]] { + val formula = TypeAssignment(this, typ) + assert(justif.statement.left.isEmpty && (justif.statement.right.head == formula)) + + override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): TypedConstant[A] = this + } + + // Function Labels + + + + + + class TypedConstantFunctional[N <: Arity]( + id : Identifier, + arity: N, + val typ: FunctionalClass, + val justif: JUSTIFICATION + ) extends ConstantFunctionLabel[N](id, arity) with LisaObject[TypedConstantFunctional[N]] { + + override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): TypedConstantFunctional[N] = this + } + + + + + + class AppliedFunction(val func: Term, val arg: Term) extends AppliedFunctional(app, Seq(func, arg)) with LisaObject[AppliedFunction] { + + override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): AppliedFunction = AppliedFunction(func.substituteUnsafe(map), arg.substituteUnsafe(map)) + + override def toString(): String = + func match + case AppliedFunction(af @ AppliedFunctional(label, args), t1) if label.id.name == "=:=" => + s"(${t1.toStringSeparated()} =:=_${args.head.toStringSeparated()} ${arg.toStringSeparated()})" + case _ => + func.toStringSeparated() + "*(" + arg.toStringSeparated() + ")" + override def toStringSeparated(): String = toString() + } + object AppliedFunction { + def unapply(af: AppliedFunctional): Option[(Term, Term)] = af match + case AppliedFunctional(label, Seq(func, arg)) if label == app => Some((func, arg)) + case _ => None + } + + + + /////////////////////// + ///// Definitions ///// + /////////////////////// + + + class TypedSimpleConstantDefinition[A <: Class](using om: OutputManager)(fullName: String, line: Int, file: String)( + val expression: Term, + out: Variable, + j: JUSTIFICATION, + val typ:A + ) extends SimpleFunctionDefinition[0](fullName, line, file)(lambda[Term, Term](Seq[Variable](), expression).asInstanceOf, out, j) { + val typingName = "typing_" + fullName + val typingJudgement = THM( label :: typ, typingName, line, file, InternalStatement)({ + have(expression :: typ) by TypeChecker.prove + thenHave(thesis) by lisa.automation.Substitution.ApplyRules(getShortDefinition(label).get) + }) + val typedLabel: TypedConstant[A] = TypedConstant(label.id, typ, typingJudgement) + + + } + object TypedSimpleConstantDefinition { + def apply[A <: Class](using om: OutputManager)(fullName: String, line: Int, file: String)(expression: Term, typ:A): TypedSimpleConstantDefinition[A] = { + val intName = "definition_" + fullName + val out = Variable(freshId(expression.allSchematicLabels.map(_.id), "y")) + val defThm = THM(ExistsOne(out, out === expression), intName, line, file, InternalStatement)({ + have(lisa.prooflib.SimpleDeducedSteps.simpleFunctionDefinition(lambda(Seq[Variable](), expression), out)) + }) + new TypedSimpleConstantDefinition(fullName, line, file)(expression, out, defThm, typ) + } + } + + + extension (d: definitionWithVars[0]) { + @nowarn + inline infix def -->[A<:Class]( + using om: OutputManager, name: sourcecode.FullName, line: sourcecode.Line, file: sourcecode.File)(term:Term, typ: A): TypedConstant[A] = + TypedSimpleConstantDefinition[A](name.value, line.value, file.value)(term, typ).typedLabel + } + + + extension (c: Constant) { + def typedWith[A <: Class](typ:A)(justif: JUSTIFICATION) : TypedConstant[A] = + if justif.statement.right.size != 1 || justif.statement.left.size != 0 || !K.isSame((c `is` typ).asFormula.underlying, justif.statement.right.head.underlying) then + throw new IllegalArgumentException(s"A proof of typing of $c must be of the form ${c :: typ}, but the given justification shows ${justif.statement}.") + else TypedConstant(c.id, typ, justif) + } + + + + + + + + + ///////////////////////// + ///// Type Checking ///// + ///////////////////////// + + object TypeChecker extends ProofTactic { + private val x = variable + + class TypingException(val msg: String) extends Exception(msg) + + def prove(using proof: SetTheoryLibrary.Proof)(bot:lisa.fol.FOL.Sequent): proof.ProofTacticJudgement = + val context = bot.left + var success: proof.ProofTacticJudgement = null + var typingError: proof.ProofTacticJudgement = null + bot.right.find(goal => + goal match + case (term `is` typ) => + val ptj = typecheck(using SetTheoryLibrary)(context.toSeq, term, Some(typ)) + if ptj.isValid then + success = ptj + true + else + typingError = ptj + false + case _ => false + ) + if success != null then success else if typingError != null then typingError else proof.InvalidProofTactic("The right hand side of the goal must be a typing judgement") + + private def fullFlat(context: Seq[Formula]): Seq[Formula] = context.flatMap{ + case AppliedConnector(And, cunj) => fullFlat(cunj) + case f => Seq(f) + } + + def typecheck(using lib: SetTheoryLibrary.type, proof: lib.Proof)(context: Seq[Formula], term:Term, typ: Option[Class]): proof.ProofTacticJudgement = + val typingAssumptions: Map[Term, Seq[Class]] = fullFlat(context).collect{ + case TypeAssignment(term, typ) => (term, typ) + }.groupBy(_._1).map((t, l) => (t, l.map(_._2))) + + val functionalTypingAssumptions: Map[(? |-> Term), Seq[FunctionalClass]] = context.collect{ + case FunctionalTypeAssignment(func, typ) => (func, typ) + }.groupBy(_._1).map((func, l) => (func, l.map(_._2))) + + TacticSubproof { + context.foreach(assume(_)) + try { + + def innerTypecheck(context2: Map[Term, Seq[Class]], term:Term, typ:Option[Class]): Class= { + val possibleTypes = typingAssumptions.getOrElse(term, Nil) + if typ == Some(any) then + have(term `is` any) by Restate.from(TypeLib.any.definition of (x := term)) + any + else if typ.isEmpty && possibleTypes.size >=1 then + have(term `is` possibleTypes.head) by Restate + possibleTypes.head + else if (typ.nonEmpty && possibleTypes.contains(typ.get)) then + have(term `is` typ.get) by Restate + typ.get + else term match + case tc: TypedConstant[?] => + if typ.isEmpty then + have(tc `is` tc.typ) by Restate.from(tc.justif) + tc.typ + else if K.isSame((tc `is` typ.get).asFormula.underlying, (tc `is` tc.typ).asFormula.underlying) then + have(tc `is` typ.get) by Restate.from(tc.justif) + typ.get + else throw TypingException("Constant " + tc + " expected to be of type " + typ + " but has type " + tc.typ + ".") + + case AppliedFunction(func, arg) => + val funcType = innerTypecheck(context2, func, None) + val funcProof = lastStep + val argType = innerTypecheck(context2, arg, None) + val argProof = lastStep + funcType match + case inType |=> outType => typ match + case None => + if K.isSame((arg `is` inType).asFormula.underlying, (arg `is` argType).asFormula.underlying) then + have(term `is` outType) by Tautology.from( + functionFromApplication of (f := func, a := arg, x := inType, y := outType), + funcProof, + argProof + ) + outType + else throw + TypingException("Function " + func + " found to have type " + funcType + ", but argument " + arg + " has type " + argType + " instead of expected " + inType + ".") + case Some(typ) if K.isSame((term `is` typ).asFormula.underlying, (term `is` outType).asFormula.underlying) => + if K.isSame((arg `is` inType).asFormula.underlying, (arg `is` argType).asFormula.underlying) then + have(term `is` outType) by Tautology.from( + functionFromApplication of (f := func, a := arg, x := inType, y := outType), + funcProof, + argProof + ) + typ + else + throw TypingException("Function " + func + " found to have type " + funcType + ", but argument " + arg + " has type " + argType + " instead of expected " + inType + ".") + + case _ => + throw TypingException("Function " + func + " expected to have function type ? |=> " + typ + ", but has type " + funcType + ". ") + case _ => + throw TypingException("Function " + func + " expected to have function type ? |=> " + typ + ", but has type " + funcType + ". Note that terms having multiple different types is only partialy supported.") + + case AppliedFunctional(label, args) => + val (argTypes, argTypesProofs) = args.map(arg => + try (innerTypecheck(context2, arg, None), lastStep) + catch + case e: TypingException => (any, any.definition of (x := arg)) //if no type could be constructed the normal way, we give it "any" + ).unzip + val labelTypes = label match + case label: TypedConstantFunctional[?] => + (label.typ, () => label.justif) +: + functionalTypingAssumptions.getOrElse(label, Nil).map(fc => (fc, () => (have( fc.formula(label.asInstanceOf) ) by Restate) )) + + case _ => functionalTypingAssumptions.getOrElse(label, Nil).map(fc => (fc, () => have( fc.formula(label.asInstanceOf) ) by Restate )) + functionalTypingAssumptions.get(label) + if labelTypes.isEmpty then + throw TypingException("Function " + label + " expected to have type (" + argTypes.mkString(", ") + ") |=> ? but is untyped.") + else + typ match + case None => + labelTypes.find((labelType, step) => + labelType.arity == args.size && + (args zip argTypes).zip(labelType.in.toSeq).forall((argAndTypes, inType) => + K.isSame((argAndTypes._1 `is` inType).asFormula.underlying, (argAndTypes._1 `is` argAndTypes._2).asFormula.underlying) // + ) + ) match + case None => + throw TypingException("Function " + label + " expected to have type (" + argTypes.mkString(", ") + ") |=> ? but is assigned " + labelTypes.mkString(" & ") + ". Note that terms having multiple different types is only partialy supported.") + case Some(labelType, step) => + val out: Class = labelType.out + + val in: Seq[Class] = labelType.in.toSeq + //val labelProp = labelType.formula(label.asInstanceOf) + val labelPropStatement = step() + val labInst = labelPropStatement.of(args*) + val subst = (labelType.args zip args).map((v, a) => (v := a)) + val newOut: Class = out match { + case t: Term => t.substitute(subst*) + case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst*) + } + have(term `is` newOut) by Tautology.from( + (argTypesProofs :+ labInst )* + ) + newOut + case Some(typValue) => + labelTypes.find((labelType, step) => + labelType.arity == args.size && + (args zip argTypes).zip(labelType.in.toSeq).forall((argAndTypes, inType) => + K.isSame((argAndTypes._1 `is` inType).asFormula.underlying, (argAndTypes._1 `is` argAndTypes._2).asFormula.underlying) + ) && { + val subst = (labelType.args zip args).map((v, a) => (v := a)) + val newOut: Class = labelType.out match { + case t: Term => t.substitute(subst*) + case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst*) + } + K.isSame((term `is` newOut).asFormula.underlying, (term `is` typValue).asFormula.underlying) + + } + ) match + case None => + throw TypingException("Function " + label + " expected to have type (" + argTypes.mkString(", ") + ") |=> " + typValue + " but is assigned " + labelTypes.mkString(" & ") + ". Note that terms having multiple different types is only partialy supported.") + case Some(labelType, step) => + val out: Class = labelType.out + val in: Seq[Class] = labelType.in.toSeq + //val labelProp = labelType.formula(label.asInstanceOf) + val labelPropStatement = step() + have(term `is` typValue) by Tautology.from( + (argTypesProofs :+ labelPropStatement.of(args*) )* + ) + typValue + + case v: Variable => + if possibleTypes.isEmpty then + throw TypingException("Variable " + v + " expected to be of type " + typ + " but is untyped.") + else throw TypingException("Variable " + v + " expected to be of type " + typ + " but is assigned " + possibleTypes.mkString(" & ") + ".") + + case c: Constant => + if possibleTypes.isEmpty then + throw TypingException("Constant " + c + " expected to be of type " + typ + " but is untyped.") + else throw TypingException("Constant " + c + " expected to be of type " + typ + " but is assigned " + possibleTypes.mkString(" & ") + ".") + + case _: AppliedFunctional => + throw Exception("Why is this not handled by the previous case? Scala reports an incomplete match") + + } + innerTypecheck(typingAssumptions, term, typ) + } + catch { + case e: TypingException => + return proof.InvalidProofTactic(e.msg) + } + } + + + } + + + + + + + + + + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala new file mode 100644 index 000000000..fa455cd39 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala @@ -0,0 +1,566 @@ +package lisa.maths.settheory.types.adt + +/** + * This object provides a DSL for defining algebraic data types (ADTs) and functions over ADT in Lisa. + * For usage examples, please refer to the documentation of the package or the reference manual. + */ +object ADTSyntax { + + import ADTDefinitions.* + import lisa.maths.settheory.SetTheory.{*, given} + + /** + * Builder for defining a constructor specification. + * + * @param param the parameters of the constructor + */ + case class ConstructorBuilder (private val param: Seq[ConstructorArgument]) { + + /** + * The number of arguments the constructor takes + */ + def size: Int = param.length + + /** + * Merges the parameters of two constructors. + * + * @param b the other constructor + */ + infix def ++(b: ConstructorBuilder): ConstructorBuilder = ConstructorBuilder(param ++ b.param.toSeq) + + /** + * Converts this constructor into an ADT with a single constructor. + */ + def toADTBuilder = ADTBuilder(Seq(this)) + + /** + * Combines two constructors into an ADT. + * + * @param b the other constructor + */ + infix def |(b: ConstructorBuilder): ADTBuilder = this | b.toADTBuilder + + /** + * Adds this constructor to an ADT. + * + * @param b the ADT to which the constructor is added + */ + infix def |(b: ADTBuilder): ADTBuilder = toADTBuilder | b + + /** + * Outputs the [[UntypedConstructor]] associated with this builder. + * + * @param name the name of the constructor + */ + def build(variables1: Seq[Variable], variables2: Seq[Variable]): SyntacticConstructor = SyntacticConstructor(param, variables1, variables2) + } + + /** + * Companion object for the [[ConstructorBuilder]] class. + */ + object ConstructorBuilder { + + /** + * Creates an empty [[ConstructorBuilder]]. + */ + def empty: ConstructorBuilder = ConstructorBuilder(Seq.empty) + } + + trait ConstructorConverter[T] { + + /** + * Converts a value into a [[ConstructorBuilder]]. + */ + def apply(t: T): ConstructorBuilder + } + + /** + * Converts a value into a [[ConstructorBuilder]]. + * + * @param any the value to convert + * @param c the converter that is used for the conversion + */ + private def any_to_const[T](any: T)(using c: ConstructorConverter[T]): ConstructorBuilder = c(any) + + given unit_to_const: ConstructorConverter[Unit] with { + + /** + * Converts a unit value into a constructor taking no arguments. + */ + override def apply(u: Unit): ConstructorBuilder = ConstructorBuilder.empty + } + + given empty_to_const: ConstructorConverter[EmptyTuple] with { + + /** + * Converts an empty tuple into a constructor taking no arguments. + */ + override def apply(t: EmptyTuple): ConstructorBuilder = ConstructorBuilder.empty + } + + given term_to_const[T <: Term]: ConstructorConverter[T] with { + + /** + * Converts a term into a constructor taking one non inductive argument. + */ + override def apply(t: T): ConstructorBuilder = ConstructorBuilder(Seq(GroundType(t))) + } + + given adt_to_const[N <: Arity]: ConstructorConverter[ADT[N]] with { + + /** + * Converts an ADT into a constructor taking one inductive argument. + */ + override def apply(a: ADT[N]): ConstructorBuilder = ConstructorBuilder(Seq(Self)) + } + + given adt_tuple_to_const[N <: Arity, T <: Tuple](using ConstructorConverter[T]): ConstructorConverter[ADT[N] *: T] with { + + /** + * Converts a tuple prepended with a type into a constructor taking an argument and whose other arguments are deduced from + * applying recursively the conversion to the tuple. + */ + override def apply(t: ADT[N] *: T): ConstructorBuilder = + any_to_const(t.head) ++ any_to_const(t.tail) + } + + + given term_tuple_to_const[H <: Term, T <: Tuple](using ConstructorConverter[T]): ConstructorConverter[H *: T] with { + + /** + * Converts a tuple prepended with a type into a constructor taking an argument and whose other arguments are deduced from + * applying recursively the conversion to the tuple. + */ + override def apply(t: H *: T): ConstructorBuilder = + any_to_const(t.head) ++ any_to_const(t.tail) + } + + extension [T1](left: T1)(using c1: ConstructorConverter[T1]) + /** + * Converts two values into constructors and combines them into an ADT. + * + * @param right the other value to convert + * @param c2 the implicit converter for the second value + */ + infix def |[T2](right: T2)(using c2: ConstructorConverter[T2]): ADTBuilder = any_to_const(left) | any_to_const(right) + + /** + * Builder for defining ADT specifications. + * + * @param constructors the builders for each constructor of the ADT. + */ + case class ADTBuilder (private val constructors: Seq[ConstructorBuilder]) { + + /** + * The number of constructors in the ADT. + */ + def size: Int = constructors.length + + /** + * Combines this ADT with another one. + * + * @param b the other ADT + */ + infix def |(b: ADTBuilder): ADTBuilder = ADTBuilder(constructors ++ b.constructors) + + /** + * Adds a constructor to this ADT. + * + * @param b the constructor to add + */ + infix def |(b: ConstructorBuilder): ADTBuilder = this | b.toADTBuilder + + /** + * Converts a value into a constructor and adds it to this ADT. + * + * @param t the value to convert + * @param c the implicit converter + */ + infix def |[T](t: T)(using c: ConstructorConverter[T]): ADTBuilder = this | any_to_const(t) + + /** + * Outputs the corresponding ADT and its constructors. + * + * @tparam N the number of type variables appearing in the specification of the ADT + * @param typeVariables the type variables of the ADT + * @param names the names of the constructors and of the ADT + */ + def build[N <: Arity](typeVariables: Variable ** N, names: Seq[String]): (ADT[N], constructors[N]) = + + val trimmedNames = (if size == 0 then names else names.tail).take(size + 1) + require( + trimmedNames.length == constructors.length + 1, + s"The number of new identifiers for constructors must match the given specification.\nNew identifiers: ${names.length - 1}, number of constructors: ${constructors.length}." + ) + + val typeVarsSet = typeVariables.toSeq.toSet + val syntacticCons = constructors.map(c => + c.build(Helpers.chooseVars("x", c.size, typeVarsSet), Helpers.chooseVars("y", c.size, typeVarsSet)) + ) + val syntacticADT = SyntacticADT[N](trimmedNames.head, syntacticCons, typeVariables) + val semanticCons = trimmedNames.tail.zip(syntacticCons).map(SemanticConstructor(_, _, syntacticADT)) + val semanticADT = SemanticADT[N](syntacticADT, semanticCons) + val cons = semanticCons.map(Constructor(_)) + (ADT[N](semanticADT, cons), new constructors[N](cons*)) + + } + + /** + * Companion object for the [[ADTBuilder]] class. + */ + object ADTBuilder { + + /** + * Creates an empty [[ADTBuilder]]. + */ + def empty: ADTBuilder = ADTBuilder(Seq.empty) + + /** + * Creates an empty [[ADTBuilder]]. + */ + def | = empty + } + + /** + * Builder for defining polymorphic ADT specifications. + * + * @tparam N the number of type variables of the ADT + * @param typeVariable the type variables of the ADT + * @param specification the builder for ADT + */ + case class PolymorphicADTBuilder[N <: Arity](typeVariables: Variable ** N, specification: ADTBuilder) { + + /** + * Outputs the corresponding ADT and its constructors. + * + * @param names the names of the constructors and of the ADT + */ + def build(names: Seq[String]) = specification.build(typeVariables, names) + + /** + * Adds a constructor to the ADT specification + * + * @param b the builder of the constructor + */ + def | (b: ConstructorBuilder): PolymorphicADTBuilder[N] = PolymorphicADTBuilder(typeVariables, specification | b) + + /** + * Adds a constructor to the ADT specification + * + * @param t the value to be converted into a constructor + */ + def |[T] (t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[N] = | (any_to_const(t)) + } + + // Syntactic sugar for polymorphic ADT Builders + + extension (u: Unit) + def --> (builder: ADTBuilder): PolymorphicADTBuilder[0] = PolymorphicADTBuilder[0](**(), builder) + def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[0] = --> (builder.toADTBuilder) + def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[0] = --> (any_to_const(t)) + + extension (v: Variable) + def --> (builder: ADTBuilder): PolymorphicADTBuilder[1] = PolymorphicADTBuilder[1](**(v), builder) + def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[1] = --> (builder.toADTBuilder) + def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[1] = --> (any_to_const(t)) + + extension (v: (Variable, Variable)) + def --> (builder: ADTBuilder): PolymorphicADTBuilder[2] = PolymorphicADTBuilder[2](**(v._1, v._2), builder) + def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[2] = --> (builder.toADTBuilder) + def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[2] = --> (any_to_const(t)) + + extension (v: (Variable, Variable, Variable)) + def --> (builder: ADTBuilder): PolymorphicADTBuilder[3] = PolymorphicADTBuilder[3](**(v._1, v._2, v._3), builder) + def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[3] = --> (builder.toADTBuilder) + def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[3] = --> (any_to_const(t)) + + extension (v: (Variable, Variable, Variable, Variable)) + def --> (builder: ADTBuilder): PolymorphicADTBuilder[4] = PolymorphicADTBuilder[4](**(v._1, v._2, v._3, v._4), builder) + def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[4] = --> (builder.toADTBuilder) + def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[4] = --> (any_to_const(t)) + + extension (v: (Variable, Variable, Variable, Variable, Variable)) + def --> (builder: ADTBuilder): PolymorphicADTBuilder[5] = PolymorphicADTBuilder[5](**(v._1, v._2, v._3, v._4, v._5), builder) + def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[5] = --> (builder.toADTBuilder) + def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[5] = --> (any_to_const(t)) + + + /** + * Lists all constructors of this ADT. + */ + case class constructors[N <: Arity](cons: Constructor[N]*) + + /** + * Companion object for the [[constructors]] class. + */ + object constructors { + def unapplySeq[N <: Arity](adt: ADT[N]): Seq[Constructor[N]] = adt.constructors + } + + /** + * Contains useful macros for ADT UI + */ + object Macro { + import scala.quoted._ + + /** + * Extract all the scala identifiers defined in the same line or after an expression. + * + * @param e the expression around which the names are extracted + */ + inline def extractNames[T](e: T): Seq[String] = ${extractNames('{e})} + + /** + * Macro implementing [[this.extractNames]]. + * + * @param e the quoted expression around which the names are extracted + */ + private def extractNames[T](using Quotes)(e: Expr[T]) : Expr[Seq[String]] = + + import quotes.reflect._ + + + val subscope = Symbol.spliceOwner.owner.owner.owner + val scope = subscope.owner + val tree = scope.tree + + case class traverser(s: Symbol) extends TreeTraverser { + var reachedADT: Boolean = false + var constructorNames: Seq[String] = Seq.empty[String] + + override def traverseTree(tree: Tree)(owner: Symbol): Unit = tree match + case v : ValDef => + if !reachedADT then + if v.symbol == s then + constructorNames = constructorNames :+ v.symbol.name + reachedADT = true + else + constructorNames = constructorNames :+ v.symbol.name + + super.traverseTree(tree)(owner) + case _ => super.traverseTree(tree)(owner) + } + + val trav = traverser(subscope) + trav.traverseTree(tree)(scope) + Expr(trav.constructorNames) + } + + /** + * Syntax to define Algebraic Data Types + */ + object define { + /** + * Extracts the constructors from an ADT. + * + * @param adt the ADT + * @return a tuple containing the ADT and its constructors + */ + private def extractConstructors[N <: Arity](adt: ADT[N]): (ADT[N], constructors[N]) = (adt, constructors(adt.constructors*)) + + /** + * Outputs a polymorphic ADT and constructors from a user specification + * Needs to be inline in order to fetch the name of the ADT and the constructor. + * + * @param builder the builder user for specifying the ADT + */ + inline def unapply[N <: Arity](builder: PolymorphicADTBuilder[N]): (ADT[N], constructors[N]) = builder.build(Macro.extractNames(builder)) + + /** + * Outputs a (non polymorphic) ADT and constructors from a user specification. + * Needs to be inline in order to fetch the name of the ADT and the constructor. + * + * @param builder the builder user for specifying the ADT + */ + inline def unapply(builder: ADTBuilder): (ADT[0], constructors[0]) = unapply[0](() --> builder) + + /** + * Returns an ADT containing only one constructor out of a user specification. + * Needs to be inline in order to fetch the name of the ADT and the constructor. + * + * @param builder the builder of the unique constructor of the ADT + */ + private inline def unapply(builder: ConstructorBuilder): (ADT[0], constructors[0]) = unapply(builder.toADTBuilder) + + /** + * Returns an ADT isomorphic to a given type. It has only one constructor taking as only argument an element of + * the provided type. + * Needs to be inline in order to fetch the name of the ADT and the constructor. + * + * @param t type given by the user + */ + inline def unapply(t: Term): (ADT[0], constructors[0]) = unapply(term_to_const(t)) + + /** + * Returns the unit type. This is an ADT containing only one value and hence having only one + * constructor (non-inductive and taking no arguments). + * Needs to be inline in order to fetch the name of the ADT and the constructor. + * + * @param u user specification indicating that they want to generate the unit type + */ + inline def unapply(u: Unit): (ADT[0], constructors[0]) = unapply(unit_to_const(u)) + + /** + * Returns a product type (also known as tuple). This is an ADT containing only one constructor. + * Generally its arguments are non inductive as the opposite would lead to the empty type. + * Needs to be inline in order to fetch the name of the ADT and the constructor. + * + * @param t user specification of the tuple + */ + inline def unapply[N <: Arity, T <: Tuple](t: (ADT[N] | Term) *: T)(using ConstructorConverter[T]): (ADT[0], constructors[0]) = + t.head match + case a: ADT[N] => unapply(adt_tuple_to_const(a *: t.tail)) + case term: Term => unapply(any_to_const(term *: t.tail)) + } + + /** + * Converts an ADT with no type variables into a term. + */ + given adt_to_term: Conversion[ADT[0], Term] = _.applyUnsafe(**()) + + /** + * Converts a function over an ADT with no type variables into a term (i.e a set function). + */ + given fun_to_term: Conversion[ADTFunction[0], Term] = _.applyUnsafe(**()) + + /** + * Converts a constructor with no type variables into a term (i.e a set function). + */ + given constructor_to_term: Conversion[Constructor[0], Term] = _.applyUnsafe(**()) + + /** + * Mutable data structure that registers the patterns that have been filled inside a pattern matching syntax. + * + * @tparam N the type variables of the ADT + * @param comp the complementary information stored in the builder + */ + class CaseBuilder[N <: Arity, T, R](val comp : R) { + + /** + * The underlying mutable map between patterns and the body of the corresponding cases. For each + * patterns stores the variables that have been used to represent its arguments. + * + */ + private val underlying = scala.collection.mutable.Map[Constructor[N], (Seq[Variable], T)]() + + /** + * Adds a case to the pattern matching + * + * @param cons the pattern / constructor + * @param value the value next to the variables that are used for the pattern's arguments + */ + def += (cons: Constructor[N], value: (Seq[Variable], T)) = underlying += (cons -> value) + + /** + * Checks if the cases of a pattern matching are valid. Specifically, it checks that: + * - All constructors are covered + * - There are no extra cases + * - The number of variables provided by the user matches the arity of the constructor + * + * @param adt the ADT over which the pattern matching is performed + * @return an error message if the pattern matching is invalid, None otherwise + */ + def isValid(adt: ADT[N]): Option[String] = + val constructors = adt.constructors.toSet + val casesConstructors = underlying.keySet.toSet + + val constructorsMinusCases = constructors -- casesConstructors + val casesMinusConstructors = casesConstructors -- constructors + + // STEP 1: Check that all constructors are covered + if !constructorsMinusCases.isEmpty then + Some(s"Case for ${constructorsMinusCases.head.name} is missing.") + // STEP 2: Check that there are no extra cases + else if !casesMinusConstructors.isEmpty then + Some(s"${casesMinusConstructors.head.name} is not a constructor of ${adt.name}.") + else + underlying.keys.foldLeft[Option[String]](None)((acc, c) => + val vars = underlying(c)._1.toSet + // STEP 3: Check that for each case the number of variables provided by the user matches the arity of the constructor + acc.orElse(Some(s"Case ${c.name}: ${vars.size} variables were provided whereas the arity of ${c.name} is ${c.arity}.").filter(_ => vars.size != c.underlying.arity)) + ) + + /** + * Outputs an immutable map out of the underlying mutable one + */ + def build: Map[Constructor[N], (Seq[Variable], T)] = underlying.toMap + } + + /** + * Case of a a pattern matching syntax + * + * @param cons the pattern / constructor + * @param vars variables that are used to represent the arguments of the constructor + */ + case class Case[N <: Arity](cons: Constructor[N], vars: Variable*) { + + /** + * Used in the context of an induction proof. Adds the subproof corresponding to this case into a builder. + * + * @see [[Induction]] + * + * @param proof the outer scope of the induction proof + * @param line the line at which this case is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param file the file in which this case is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param builder the builder of the induction proof + * @param subproof the proof of the case (possibly using the induction hypothesis) + */ + def subproof (using proof: Proof, line: sourcecode.Line, file: sourcecode.File, builder: CaseBuilder[N, proof.ProofStep, (Sequent, Seq[Term], Variable)])(subproof: proof.InnerProof ?=> Unit): Unit = + val (bot, args, adtVar) = builder.comp + val prop = bot.right.head + val consTerm = appSeq(cons.underlying.term(args))(vars) + val subst = adtVar -> consTerm + + val assumptions = + (wellTypedSet(cons.underlying.semanticSignature(vars).map(p => (p._1, p._2.substitute(cons.underlying.typeVariablesSeq.zip(args).map(SubstPair(_, _))*)))) + ++ + cons.underlying.syntacticSignature(vars).filter(_._2 == Self).map((v, _) => prop.substitute(adtVar -> v))) + + //val botWithAssumptions = bot.substitute(subst) ++ ((assumptions ++ proof.getAssumptions) |- ()) + val botWithAssumptions = bot.substitute(subst) ++ (assumptions |- ()) + + + + val iProof: proof.InnerProof = new proof.InnerProof(Some(botWithAssumptions)) + subproof(using iProof) + val proofStep = (new SUBPROOF(using proof)(None)(iProof)).judgement.validate(line, file).asInstanceOf[proof.ProofStep] + + def subproofWithExtraStep: proof.ProofTacticJudgement = TacticSubproof{ ip ?=> + val fullSeq = Tautology(using lisa.SetTheoryLibrary, ip)(proofStep)(botWithAssumptions) + if fullSeq.isValid then + fullSeq.validate(line, file) + else + return proof.InvalidProofTactic(s"Proof of case ${cons.name} is invalid.\nExpected: ${botWithAssumptions}.") + } + + builder += (cons, (vars, subproofWithExtraStep.validate(line, file))) + + /** + * Used in the context of a function definition. Adds the body of the case to a builder. + * + * @param body the body of this case + * @param builder the builder for the function definition + */ + def apply(body : Term)(using builder: CaseBuilder[N, Term, Unit]) = builder += (cons, (vars, body)) + } + + /** + * Defines a function over an ADT + * + * @param adt the domain of this function + * @param returnType the return type of this function + * @param name the name of this functions + * @param cases the definition of the function for each constructor + */ + def fun[N <: Arity](adt: ADT[N], returnType: Term)(using name: sourcecode.Name)(cases: CaseBuilder[N, Term, Unit] ?=> Unit): ADTFunction[N] = { + val builder = CaseBuilder[N, Term, Unit](()) + cases(using builder) + builder.isValid(adt) match + case None => + ADTFunction(SemanticFunction[N](name.value, adt.underlying, builder.build.map((k, v) => (k.underlying, v)), returnType), adt) + case Some(msg) => throw IllegalArgumentException(msg) + } + +} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala new file mode 100644 index 000000000..01b9d6ca4 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala @@ -0,0 +1,133 @@ +/** + * Defines set theoretic functions over Algebraic Data Types + */ + +package lisa.maths.settheory.types.adt + +import lisa.maths.settheory.SetTheory.{_, given} +import lisa.maths.settheory.functions.|=> +import lisa.maths.settheory.types.TypeSystem.:: +import lisa.maths.settheory.types.TypeSystem._ + +import ADTDefinitions.* +import Helpers.* +import Helpers.{/\, ===, \/} + +/** + * Set theoretic interpretation of a function over an ADT. + * + * @tparam N the number of type variables of the domain of this function + * @param name the name of this function + * @param adt the domain of this function + * @param cases the body of this function for each constructor + * @param returnType the codomain of this function + * @param line the line at which this function is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param file the file in which this function is defined. Usually fetched automatically by the compiler. + * Used for error reporting + */ +class SemanticFunction[N <: Arity](name: String, adt: SemanticADT[N], cases: Map[SemanticConstructor[N], (Seq[Variable], Term)], returnType: Term)(using line: sourcecode.Line, file: sourcecode.File) { + + /** + * Map binding each constructor to a theorem stating that the case is well typed. + */ + private val checkReturnType: Map[SemanticConstructor[N], THM] = + (for c <- cases.keys yield + val (vars, body) = cases(c) + c -> Lemma(wellTyped(c.semanticSignature(vars)) |- body :: returnType) { + have(thesis) by TypeChecker.prove + } + ).toMap + + /** + * Type variables appearing in this function's domain. + */ + val typeVariables: Variable ** N = adt.typeVariables + + /** + * Sequence of type variables appearing in this function's domain. + */ + val typeVariablesSeq: Seq[Variable] = adt.typeVariablesSeq + + /** + * Number of type variables appearing in this function. + */ + val typeArity: N = adt.typeArity + + /** + * Full name of this function. That is the name of the function prefixed by the name of the ADT. + */ + val fullName = s"$name" + // val fullName = s"${adt.name}/$name" + + val typ = adt.term |=> returnType + + /** + * Definition of this function. + * + * Formally it is the only function whose domain is the ADT and such that for each constructor c f * (c * x1 * ... * xn) = case(c, x1, ..., xn) + */ + private val untypedDefinition = (f :: typ) /\ simplify(/\(cases.map((c, caseDef) => + val (vars, body) = caseDef + forallSeq(vars, wellTypedFormula(c.semanticSignature(vars)) ==> (f * c.appliedTerm(vars) === body)) + ))) + + /** + * Lemma --- Uniqueness of this function. + */ + private val uniqueness = Axiom(existsOne(f, untypedDefinition)) + + /** + * Set theoretic definition of the constructor. + */ + private val classFunction = FunctionDefinition(fullName, line.value, file.value)(typeVariablesSeq, f, untypedDefinition, uniqueness).label + + /** + * Identifier of this function. + */ + val id: Identifier = classFunction.id + + /** + * Function where type variables are instantiated with schematic symbols. + */ + val term = classFunction.applySeq(typeVariablesSeq) + + /** + * Lemma --- The body of this function correpsonds to the cases provided by the user. + * + * `for each constructor c, ∀x1, ..., xn. f * (c * x1 * ... * xn) = case(c, x1, ..., xn)` + */ + val shortDefinition = cases.map((c, caseDef) => + val (vars, body) = caseDef + c -> Lemma(simplify(wellTypedFormula(c.semanticSignature(vars))) ==> (term * c.appliedTerm(vars) === body)) { + have(forall(f, (term === f) <=> untypedDefinition)) by Exact(classFunction.definition) + thenHave((term === term) <=> (term :: typ) /\ (/\(cases.map((c, caseDef) => { + val (vars, body) = caseDef + forallSeq(vars, wellTypedFormula(c.semanticSignature(vars)) ==> (term * c.appliedTerm(vars) === body)) + })))) by InstantiateForall(term) + thenHave(forallSeq(vars, wellTypedFormula(c.semanticSignature(vars)) ==> (term * c.appliedTerm(vars) === body))) by Weakening + vars.foldLeft(lastStep)((l, _) => + lastStep.statement.right.head match + case Forall(v, phi) => thenHave(phi) by InstantiateForall(v) + case _ => throw UnreachableException + ) + } + ) + + /** + * Lemma --- Introduction rule + * + * `f : ADT -> T` + * + * where `T` is the return type of this function + */ + val intro = Lemma(forallSeq(typeVariablesSeq, term :: typ)) { + have(forall(f, (term === f) <=> untypedDefinition)) by Exact(classFunction.definition) + thenHave((term === term) <=> (term :: typ) /\ (/\(cases.map((c, caseDef) => { + val (vars, body) = caseDef + forallSeq(vars, /\(wellTyped(c.semanticSignature(vars))) ==> (term * c.appliedTerm(vars) === body)) + })))) by InstantiateForall(term) + thenHave(term :: typ) by Weakening + thenHave(thesis) by QuantifiersIntro(typeVariablesSeq) + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala new file mode 100644 index 000000000..dfc601d29 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala @@ -0,0 +1,1032 @@ +package lisa.maths.settheory.types.adt + +/** + * Tactic that proves every goal of the form: + * + * ` ... |- ..., ∀x1, ..., xn. P(x), ...` + * + * ` ..., ∀x1, ..., xn . P(x), ... |- ...` + * + * ` ... |- ..., ∃x1, ..., xn. P(x), ...` + * + * ` ..., ∃x1, ..., xn . P(x), ... |- ...` + * + * given a proof of the sequents without quantification. + */ +object QuantifiersIntro extends lisa.prooflib.ProofTacticLib.ProofTactic { + + import lisa.prooflib.SimpleDeducedSteps.Restate + import lisa.prooflib.BasicStepTactic.* + import lisa.fol.FOL.* + + /** + * Executes the tactic on a specific goal. + * + * @param lib the library that is currently being used + * @param proof the ongoing proof in which the tactic is called + * @param vars the variables that needs to be quantified + * @param fact the proof of the sequent without quantification + * @param bot the statement to prove + */ + def apply(using lib: lisa.prooflib.Library, proof: lib.Proof)(vars: Seq[Variable])(fact: proof.Fact)(bot: Sequent): proof.ProofTacticJudgement = + TacticSubproof { sp ?=> + if vars.isEmpty then + lib.have(bot) by Restate.from(fact) + else + val diff: Sequent = bot -- fact.statement + + diff match + case Sequent(s, _) if s.size == 1 => + val diffRest = bot.left -- s + val f = s.head + val fWithoutQuant = (fact.statement.left -- diffRest).head + f match + case BinderFormula(Forall, _, _) => + vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => + val (accFact, accFormula) = acc + val newFormula = forall(v, accFormula) + (lib.have(diffRest + newFormula |- bot.right) by LeftForall(accFact), newFormula) + ) + case BinderFormula(Exists, _, _) => + vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => + val (accFact, accFormula) = acc + val newFormula = exists(v, accFormula) + (lib.have(diffRest + newFormula |- bot.right) by LeftExists(accFact), newFormula) + ) + case _ => return proof.InvalidProofTactic(s"The formula that changed is not quantified: $f.") + case Sequent(_, s) if s.size == 1 => + val diffRest = bot.right -- s + val f = s.head + val fWithoutQuant = (fact.statement.right -- diffRest).head + f match + case BinderFormula(Forall, _, _) => + vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => + val (accFact, accFormula) = acc + val newFormula = forall(v, accFormula) + (lib.have(bot.left |- diffRest + newFormula) by RightForall(accFact), newFormula) + ) + case BinderFormula(Exists, _, _) => + vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => + val (accFact, accFormula) = acc + val newFormula = exists(v, accFormula) + (lib.have(bot.left |- diffRest + newFormula) by RightExists(accFact), newFormula) + ) + case _ => return proof.InvalidProofTactic(s"The formula that changed is not quantified: $f.") + case Sequent(s1, s2) if s1.isEmpty && s2.isEmpty => lib.have(bot) by Restate.from(fact) + case _ => return proof.InvalidProofTactic("Two or more formulas in the sequent have changed.") + + + } +} + +/** + * General purpose helpers. + */ +private [adt] object Helpers { + + import lisa.fol.FOL.{*, given} + + /** + * Benchmarks a block of code. + * + * @param name the name of the benchmark + * @param f the block of code to benchmark + * @return the result of the block of code and prints how long it took to execute + */ + def benchmark[T](name: String)(f: => T): T = { + val before = System.nanoTime + + val res = f + + val totalTime = (System.nanoTime - before) / 1000000 + + println(s"$name time: $totalTime ms") + + res + } + + /** + * Exception thrown when code that should not be accessed is reached. + */ + object UnreachableException extends Exception("This code should not be accessed. If you see this message, please report it to the library maintainers.") + + // ********************* + // * FIRST ORDER LOGIC * + // ********************* + + val a = variable + val b = variable + val c = variable + val d = variable + + val f = variable + val g = variable + val h = variable + + val n = variable + val m = variable + + val p = formulaVariable + val p1 = formulaVariable + val p2 = formulaVariable + val p3 = formulaVariable + val p4 = formulaVariable + + val q1 = formulaVariable + val q2 = formulaVariable + + val r = variable + val s = variable + val t = variable + + val x = variable + val y = variable + val z = variable + + val Q = predicate[1] + val P = predicate[1] + val P2 = predicate[2] + val schemPred = predicate[1] + + /** + * Formula representing whether two sequences of terms are pairwise equal. + * + * @param s2 the sequence to compare with + */ + extension (s1: Seq[Term]) def ===(s2: Seq[Term]): Formula = /\(s1.zip(s2).map(_ === _)) + + /** + * Disjunction of a sequence of formulas. + * + * @param s the formulas to which the disjunction is applied + */ + def \/(s: Iterable[Formula]): Formula = + if s.isEmpty then False + else s.fold(False)(_ \/ _) + + /** + * Conjunction of a sequence of formulas. + * + * @param s the formulas to which the conjunction is applied + */ + def /\(s: Iterable[Formula]): Formula = + if s.isEmpty then True + else s.fold(True)(_ /\ _) + + /** + * Repeats existential quantification over a sequence of variables. + * + * @param vars the variables to quantify over + * @param f the formula to which the quantifiers are applied + * @return the quantified formula + */ + def existsSeq(vars: Seq[Variable], f: Formula): Formula = + vars.foldRight(f)(exists(_, _)) + + /** + * Repeats universal quantification over a sequence of variables. + * + * @param vars the variables to quantify over + * @param f the formula to which the quantifiers are applied + * @return the quantified formula + */ + def forallSeq(vars: Seq[Variable], f: Formula): Formula = + vars.foldRight(f)(forall(_, _)) + + /** + * Simplifies a formula by removing True and False constants. + * + * @param f the formula to simplify + */ + def simplify(f: Formula): Formula = + f match + case Or(False, phi) => simplify(phi) + case Or(phi, False) => simplify(phi) + case Or(phi, psi) => simplify(phi) \/ simplify(psi) + case And(True, phi) => simplify(phi) + case And(phi, True) => simplify(phi) + case And(phi, psi) => simplify(phi) /\ simplify(psi) + case Implies(True, phi) => simplify(phi) + case Implies(phi, psi) => Implies(simplify(phi), simplify(psi)) + case _ => f + + + /** + * Picks fresh variables starting with a given prefix . + * + * @param prefix the prefix of the fresh variables + * @param size the number of fresh variables to output + * @param assigned the variables that are already used + * @param counter the index to append to the prefix + * @param acc the variables that have already been generated by this method + * + */ + def chooseVars(prefix: String, size: Int, assigned: Set[Variable] = Set.empty, counter: Int = 0, acc: Seq[Variable] = Seq.empty): Seq[Variable] = + if size == 0 then + acc + else + val newVar = Variable(s"${prefix}${counter}") + if assigned.contains(newVar) then + chooseVars(prefix, size, assigned, counter + 1, acc) + else + chooseVars(prefix, size - 1, assigned, counter + 1, acc :+ newVar) + + +} + +/** + * Definitions and helper functions for ADT. + */ +private[adt] object ADTDefinitions { + + import lisa.maths.settheory.SetTheory.* + import lisa.maths.settheory.functions.* + import lisa.maths.settheory.types.TypeSystem.* + import Helpers.{/\} + + /** + * The specification of a constructor can either contain terms or a self reference, i.e. a reference to the ADT itself. + */ + trait ConstructorArgument { + /** + * Returns the term associated to a constructor argument, or in case it is a self reference, returns the term associated to the ADT. + * + * @param arg the constructor argument + * @param adt the term representing the ADT + */ + def getOrElse(adt: Term): Term = + this match { + case Self => adt + case GroundType(term) => term + } + + /** + * Substitutes the type variables of a constructor argument. + */ + def substitute(p: SubstPair*): ConstructorArgument = + this match + case Self => Self + case GroundType(t) => GroundType(t.substitute(p*)) + } + + /** + * A symbol for self reference + */ + case object Self extends ConstructorArgument + + /** + * Syntactic represenation of a term + * + * @param t the underlying term + */ + case class GroundType(t: Term) extends ConstructorArgument + + /** + * Shorthand for the union of the range of a function. + * + * @param f the function + */ + def unionRange(f: Term) = union(relationRange(f)) + + /** + * Shorthand for the range of a restricted function. + * + * @param f the function + * @param n the domain to which the function is restricted + */ + def restrRange(f: Term, n: Term) = relationRange(restrictedFunction(f, n)) + + /** + * Applies a sequence of arguments to a function. + * + * @param f the function + * @param args the arguments to apply + */ + def appSeq(f: Term)(args: Seq[Term]): Term = args.foldLeft(f)(_ * _) + + /** + * Converts an integer to the associated ordinal. + * + * @param n the integer to convert + */ + def toTerm(n: Int): Term = + require(n >= 0, "n must be a non-negative integer") + if n == 0 then emptySet + else successor(toTerm(n - 1)) + + /** + * Returns a sequence of formulas asserting that all terms of a sequence are well-typed. + * + * @param s the terms and their respective types + */ + def wellTyped(s: Seq[(Term, Term)]): Seq[Formula] = s.map(_ :: _) + + /** + * Returns a sequence of formulas asserting that all terms of a sequence are well-typed with respect to the + * specification of a constructor. + * + * @param s the terms and their respective type + * @param orElse the term to use in case of a self reference + */ + def wellTyped(s: Seq[(Term, ConstructorArgument)])(orElse: Term): Seq[Formula] = s.map((t, arg) => t :: arg.getOrElse(orElse)) + + /** + * Returns a set of formulas asserting that all terms of a sequence are well-typed. + * + * @param s the terms and their respective types + */ + def wellTypedSet(s: Seq[(Term, Term)]): Set[Formula] = wellTyped(s).toSet + + /** + * Returns a set of formulas asserting that all terms of a sequence are well-typed with respect to the + * specification of a constructor. + * + * @param s the terms and their respective type + * @param orElse the term to use in case of a self reference + */ + def wellTypedSet(s: Seq[(Term, ConstructorArgument)])(orElse: Term): Set[Formula] = wellTyped(s)(orElse).toSet + + /** + * Returns a formula asserting that all terms of a sequence are well-typed. + * + * @param s the terms and their respective types + */ + def wellTypedFormula(s: Seq[(Term, Term)]): Formula = /\ (wellTyped(s)) + + /** + * Returns a formula asserting that all terms of a sequence are well-typed with respect to the + * specification of a constructor. + * + * @param s the terms and their respective type + * @param orElse the term to use in case of a self reference + */ + def wellTypedFormula(s: Seq[(Term, ConstructorArgument)])(orElse: Term): Formula = /\ (wellTyped(s)(orElse)) + +} + + +/** + * List of external set theoretic theorems needed for proofs about ADT. + * Some of these theorems are not yet implemented in the library and + * will be added in the future. + */ +private [adt] object ADTHelperTheorems { + + import lisa.maths.settheory.SetTheory.{*, given} + import lisa.maths.settheory.functions.* + import lisa.maths.Quantifiers.{existentialEquivalenceDistribution, equalityInExistentialQuantifier, + existentialConjunctionWithClosedFormula, equalityTransitivity} + import ADTDefinitions.* + import Helpers.* + //import lisa.maths.Quantifiers.* + + // TODO: Remove + val pair = ConstantFunctionLabel("pair", 2) + addSymbol(pair) + + val pairExtensionality = Lemma((pair(a, b) === pair(c, d)) <=> ((a === c) /\ (b === d))) { + sorry + } + + // ********************* + // * FIRST ORDER LOGIC * + // ********************* + + + /** + * Lemma --- Alternative statement of transitivity of equality. + */ + val altEqualityTransitivity = Lemma((x === y, y === z) |- x === z) { + have(thesis) by Restate.from(equalityTransitivity) + } + + /** + * Lemma --- Transitivity of equivalence. + */ + val equivalenceRewriting = Lemma((p1 <=> p2, p2 <=> p3) |- p1 <=> p3) { + have(thesis) by Tautology + } + + /** + * Lemma --- Modus ponens for equivalence. + */ + val equivalenceApply = Lemma((p1 <=> p2, p1) |- p2) { + have(thesis) by Tautology + } + + /** + * Lemma --- Top level existential quantifiers can be swapped. + */ + val existentialSwap = Lemma(∃(x, ∃(y, P2(x, y))) <=> ∃(y, ∃(x, P2(x, y)))) { + have(thesis) by Tableau + } + + /** + * Lemma --- Modus ponens for reversed equivalence. + */ + val equivalenceRevApply = Lemma((p2 <=> p1, p1) |- p2) { + have(thesis) by Tautology + } + + /** + * Lemma --- If a statement is equivalent to the conjunction of two other statements, and one of them is true, then it can be removed from the equivalence. + */ + val equivalenceAnd = Lemma((p2, p1 <=> (p2 /\ p3)) |- p1 <=> p3) { + have(thesis) by Tautology + } + + /** + * Lemma --- If two formulas are equivalent then adding a disjunction on their right side preserves the equivalence. + */ + val rightAndEquivalence = Lemma(p1 <=> p2 |- (p1 /\ p) <=> (p2 /\ p)) { + have(thesis) by Tautology + } + + /** + * Lemma --- If two formulas are equivalent then adding an implication on their left side preserves the equivalence. + */ + val impliesEquivalence = Lemma((p1 <=> p2, p3 <=> p4) |- (p1 ==> p3) <=> (p2 ==> p4)) { + have(thesis) by Tautology + } + + /** + * Lemma --- If two formulas are equivalent then adding an implication on their left side preserves the equivalence. + */ + val leftImpliesEquivalenceWeak = Lemma(p1 <=> p2 |- (p ==> p1) <=> (p ==> p2)) { + have(thesis) by Tautology + } + + /** + * Lemma --- Implication distributes over equivalence. + */ + val leftImpliesEquivalenceStrong = Lemma(p ==> (p1 <=> p2) |- (p ==> p1) <=> (p ==> p2)) { + have(thesis) by Tautology + } + + /** + * Lemma --- If there exists a unique element satisfying a predicate, then all + * other elements satisfying the predicate are equal to it. + */ + val existsOneUniqueness = Lemma((∃!(x, P(x)), P(x), P(y)) |- x === y) { + sorry + } + + // ******************* + // * NATURAL NUMBERS * + // ******************* + + // Natural numbers + val N = Constant("N") + addSymbol(N) + + /** + * Lemma --- 0 is a natural number. + * + * `0 ∈ N` + */ + val zeroIsNat = Lemma(in(emptySet, N)) { + sorry + } + + /** + * Lemma --- The natural numbers are not empty. + * + * `N != ∅` + */ + val natNotEmpty = Lemma(!(N === emptySet)) { + have(thesis) by Cut(zeroIsNat, setWithElementNonEmpty of (y := emptySet, x := N)) + } + + /** + * Lemma --- There exists a natural number. + * + * `∃n ∈ N` + */ + val existsNat = Lemma(exists(n, in(n, N))) { + have(thesis) by RightExists(zeroIsNat) + } + + /** + * Lemma --- Successor is an injective function. + * + * `n = m <=> n + 1 = m + 1` + */ + val successorInjectivity = Lemma((n === m) <=> (successor(n) === successor(m))) { + sorry + } + + /** + * Lemma --- A term is a natural number if and only if its successor is a natural number. + * + * `n ∈ N <=> n + 1 ∈ N` + */ + val successorIsNat = Lemma(in(n, N) <=> in(successor(n), N)) { + sorry + } + + /** + * Lemma --- Any number is smaller than its successor + * + * `∀n ∈ N. n < n + 1` + */ + val inSuccessor = Lemma(in(n, successor(n))) { + val uniomAxiomForward = have(exists(y, in(y, unorderedPair(n, singleton(n))) /\ in(n, y)) |- in(n, union(unorderedPair(n, singleton(n))))) by Cut( + unionAxiom of (x := unorderedPair(n, singleton(n)), z := n), + equivalenceRevApply of (p1 := exists(y, in(y, unorderedPair(n, singleton(n))) /\ in(n, y)), p2 := in(n, union(unorderedPair(n, singleton(n))))) + ) + have(in(singleton(n), unorderedPair(n, singleton(n))) /\ in(n, singleton(n))) by RightAnd( + secondElemInPair of (x := n, y := singleton(n)), + singletonHasNoExtraElements of (x := n, y := n) + ) + thenHave(exists(y, in(y, unorderedPair(n, singleton(n))) /\ in(n, y))) by RightExists + have(in(n, union(unorderedPair(n, singleton(n))))) by Cut(lastStep, uniomAxiomForward) + thenHave(union(unorderedPair(n, singleton(n))) === successor(n) |- in(n, successor(n))) by RightSubstEq.withParametersSimple( + List((union(unorderedPair(n, singleton(n))), successor(n))), + lambda(s, in(n, s)) + ) + have(thesis) by Cut(successor.shortDefinition of (x := n), lastStep) + } + + /** + * Lemma --- 0 is not the successor of any natural number. + * + * `∀n ∈ N. n + 1 != 0` + */ + val zeroIsNotSucc = Lemma(!(successor(n) === emptySet)) { + have(thesis) by Cut(inSuccessor, setWithElementNonEmpty of (y := n, x := successor(n))) + } + + /** + * Lemma --- A number is smaller or equal than another number if and only if it is strictly smaller than its successor. + * + * `m <= n <=> m < n + 1` + */ + val natSubset = Lemma(in(n, N) |- subset(m, n) <=> in(m, successor(n))) { + sorry + } + + /** + * Lemma --- The intersection of a natural number with the set of natural numbers is the number itself. + * + * `n ∩ N = n` + */ + val intersectionNat = Lemma(in(n, N) |- setIntersection(n, N) === n) { + sorry + } + + /** + * Lemma --- If a number is smaller or equal than a natural number, then it is also a natural number. + * + * `m <= n, n ∈ N |- m ∈ N` + */ + val subsetIsNat = Lemma(subset(a, b) |- in(b, N) ==> in(a, N)) { + sorry + } + + /** + * Lemma --- Induction principle for natural numbers + * + * `P(0), ∀n ∈ N. P(n) => P(n + 1) |- ∀n ∈ N. P(n)` + */ + val natInduction = Lemma((P(emptySet), forall(m, in(m, N) ==> (P(m) ==> P(successor(m))))) |- forall(n, in(n, N) ==> P(n))) { + sorry + } + + /** + * Lemma --- Every number is smaller or equal than its successor. + * + * `n <= n + 1` + */ + val subsetSuccessor = Lemma(subset(n, successor(n))) { + have(setUnion(n, singleton(n)) === union(unorderedPair(n, singleton(n))) |- subset(n, union(unorderedPair(n, singleton(n))))) by RightSubstEq.withParametersSimple( + List((setUnion(n, singleton(n)), union(unorderedPair(n, singleton(n))))), + lambda(s, subset(n, s)) + )(unionSubsetFirst of (a := n, b := singleton(n))) + have(subset(n, union(unorderedPair(n, singleton(n))))) by Cut(setUnion.shortDefinition of (x := n, y := singleton(n)), lastStep) + thenHave(successor(n) === union(unorderedPair(n, singleton(n))) |- subset(n, successor(n))) by RightSubstEq.withParametersSimple( + List((successor(n), union(unorderedPair(n, singleton(n))))), + lambda(s, subset(n, s)) + ) + have(thesis) by Cut(successor.shortDefinition of (x := n), lastStep) + } + + // ************* + // * FUNCTIONS * + // ************* + + /** + * Lemma --- Range introduction and elimination rules. If en element is in the image of a function, then it has a preimage inside its domain. + * + * `functional(f) |- y ⊆ Im(f) <=> ∃x ∈ Dom(f). f(x) = y` + */ + val functionRangeMembership = Lemma(functional(f) |- in(y, relationRange(f)) <=> ∃(x, in(x, relationDomain(f)) /\ (app(f, x) === y))) { + sorry + } + + /** + * Lemma --- The restriction of a function is still a function. + * + * `functional(f) |- functional(f|x)` + */ + val functionalRestrictedFunction = Lemma(functional(f) |- functional(restrictedFunction(f, x))) { + sorry + } + + /** + * Lemma --- If an element is in the image of a restricted function, then it has a preimage inside its domain. + * + * `functional(f) |- y ⊆ Im(f) <=> ∃x ∈ d ∩ Dom(f). f|d(x) = y` + */ + val restrictedFunctionRangeMembership = Lemma(functional(f) |- in(y, relationRange(restrictedFunction(f, d))) <=> ∃(x, in(x, d ∩ relationDomain(f)) /\ (app(restrictedFunction(f, d), x) === y))) { + have(functional(f) |- in(y, relationRange(restrictedFunction(f, d))) <=> ∃(x, in(x, relationDomain(restrictedFunction(f, d))) /\ (app(restrictedFunction(f, d), x) === y))) by Cut( + functionalRestrictedFunction of (x := d), + functionRangeMembership of (f := restrictedFunction(f, d)) + ) + thenHave(functional(f) |- in(y, relationRange(restrictedFunction(f, d))) <=> ∃(x, in(x, d ∩ relationDomain(f)) /\ (app(restrictedFunction(f, d), x) === y))) by Substitution.ApplyRules( + restrictedFunctionDomain of (x := d) + ) + } + + /** + * Lemma --- Characterization of the union of the range of a function. + * + * `∪ Im(h) = {z | ∃n ∈ Dom(h). z ∈ h(n)}` + */ + val unionRangeMembership = Lemma(functional(h) |- in(z, unionRange(h)) <=> exists(n, in(n, relationDomain(h)) /\ in(z, app(h, n)))) { + val iffAfterAnd = have(functional(h) |- (y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y)) /\ z ∈ y) by Cut( + functionRangeMembership of (f := h), + rightAndEquivalence of (p1 := y ∈ relationRange(h), p2 := ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y)), p := z ∈ y) + ) + have(functional(h) |- (y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) by Apply(equivalenceRewriting).on( + iffAfterAnd, + existentialConjunctionWithClosedFormula of (P := lambda(m, m ∈ relationDomain(h) /\ (app(h, m) === y)), p := z ∈ y) + ) + + thenHave(functional(h) |- ∀(y, (y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y))) by RightForall + + val beforeExSwap = have(functional(h) |- ∃(y, y ∈ relationRange(h) /\ z ∈ y) <=> ∃(y, ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y))) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P := lambda(y, y ∈ relationRange(h) /\ z ∈ y), + Q := lambda(y, ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) + ) + ) + + have(∃(y, ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) <=> ∃(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) subproof { + + have(m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y <=> m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)) by Restate + thenHave(forall(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y <=> m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) by RightForall + have(∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y) <=> ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P := lambda(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y), + Q := lambda(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)) + ) + ) + thenHave(forall(m, ∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y) <=> ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) by RightForall + have(∃(m, ∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) <=> ∃(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P := lambda(y, ∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)), + Q := lambda(y, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) + ) + ) + have(thesis) by Apply(equivalenceRewriting).on(lastStep, existentialSwap of (P2 := lambda((y, m), m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y))) + } + + val introM = + have(functional(h) |- ∃(y, y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) by Apply(equivalenceRewriting).on(beforeExSwap, lastStep) + + have( + ∀(m, (∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) <=> (m ∈ relationDomain(h) /\ z ∈ app(h, m))) + ) by RightForall(equalityInExistentialQuantifier of (P := lambda(y, m ∈ relationDomain(h) /\ z ∈ y), y := app(h, m))) + + have( + ∃(m, (∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) <=> ∃(m, m ∈ relationDomain(h) /\ z ∈ app(h, m)) + ) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P := lambda(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))), + Q := lambda(m, m ∈ relationDomain(h) /\ z ∈ app(h, m)) + ) + ) + + have(functional(h) |- ∃(y, y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ z ∈ app(h, m))) by Apply(equivalenceRewriting).on( + introM, + lastStep + ) + + have(thesis) by Apply(equivalenceRewriting).on( + lastStep, + unionAxiom.asInstanceOf + ) + } + + // ************* + // * EMPTYNESS * + // ************* + + /** + * Lemma --- The union of the empty set is the empty set. + * + * `∪ ∅ = ∅` + */ + val unionEmpty = Lemma(union(emptySet) === emptySet) { + sorry + } + + /** + * Lemma --- Restricting the domain of a function to the empty set yields the empty set. + * + * `h|∅ = ∅` + */ + val restrictedFunctionEmptyDomain = Lemma(restrictedFunction(h, emptySet) === emptySet) { + sorry + } + + /** + * Lemma --- If the domain of a function is non empty, then the function is non empty as well. + * + * `Dom(h) != ∅ |- h != ∅` + */ + val nonEmptyDomain = Lemma(!(relationDomain(h) === emptySet) |- !(h === emptySet)) { + sorry + } + + /** + * Lemma --- A superset of a non empty set is non empty. + * + * `x ⊆ y, x != ∅ |- y != ∅` + */ + val subsetNotEmpty = Lemma((subset(x, y), !(x === emptySet)) |- !(y === emptySet)) { + val subst = have(y === emptySet |- y === emptySet) by Hypothesis + have((subset(x, emptySet), y === emptySet) |- (x === emptySet)) by Apply(equivalenceApply of (p1 := subset(x, emptySet))).on(emptySetIsItsOwnOnlySubset.asInstanceOf) + thenHave((subset(x, y), y === emptySet) |- (x === emptySet)) by Substitution.ApplyRules(subst) + } + + /** + * Theorem --- The range of the empty relation is empty. + * + * `range(∅) = ∅` + * + */ + val rangeEmpty = Theorem(relationRange(emptySet) === emptySet) { + import lisa.maths.settheory.SetTheory + + have(!in(SetTheory.pair(a, t), emptySet)) by Exact(emptySetAxiom) + thenHave(forall(a, !in(SetTheory.pair(a, t), emptySet))) by RightForall + val s0 = thenHave(!exists(a, in(SetTheory.pair(a, t), emptySet))) by Restate + + have(!in(t, emptySet)) by Exact(emptySetAxiom) + have(in(t, emptySet) <=> exists(a, in(SetTheory.pair(a, t), emptySet))) by Tautology.from(lastStep, s0) + val defRHS = thenHave(forall(t, in(t, emptySet) <=> exists(a, in(SetTheory.pair(a, t), emptySet)))) by RightForall + + have((relationRange(emptySet) === emptySet) <=> forall(t, in(t, emptySet) <=> exists(a, in(SetTheory.pair(a, t), emptySet)))) by InstantiateForall(emptySet)( + relationRange.definition of (r := emptySet, z := emptySet) + ) + have(relationRange(emptySet) === emptySet) by Tautology.from(defRHS, lastStep) + } + + + /** + * Lemma --- The range of the empty function is empty. + * + * `Im(∅) = ∅` + */ + val unionRangeEmpty = Lemma(unionRange(emptySet) === emptySet) { + have(unionRange(emptySet) === unionRange(emptySet)) by RightRefl + thenHave(unionRange(emptySet) === union(emptySet)) by Substitution.ApplyRules(rangeEmpty) + thenHave(thesis) by Substitution.ApplyRules(unionEmpty) + } + + /** + * Lemma --- If a function and a domain are non empty, then restricting this function to this + * domain yields a non empty set. + * + * `h != ∅, d != ∅ |- h|d != ∅` + */ + val restrictedFunctionNotEmpty = Lemma((!(h === emptySet), !(d === emptySet)) |- !(restrictedFunction(h, d) === emptySet)) { + sorry + } + + // **************** + // * MONOTONICITY * + // **************** + + /** + * Lemma --- Union is a monotonic operation with respect to set inclusion. + * + * `x ⊆ y |- ∪ x ⊆ ∪ y` + */ + val unionMonotonic = Lemma(subset(x, y) |- subset(union(x), union(y))) { + sorry + } + + /** + * Lemma --- Range is a monotonic operation with respect to set inclusion. + * + * `f ⊆ g |- Im(f) ⊆ Im(g)` + */ + val rangeMonotonic = Lemma(subset(f, g) |- subset(relationRange(f), relationRange(g))) { + sorry + } + + /** + * Lemma --- The union of the range is a monotonic operation with respect to set inclusion. + * + * `f ⊆ g |- ∪ Im(f) ⊆ ∪ Im(g)` + */ + val unionRangeMonotonic = Lemma(subset(f, g) |- subset(unionRange(f), unionRange(g))) { + have(thesis) by Apply(unionMonotonic).on(rangeMonotonic.asInstanceOf) + } + + /** + * Lemma --- If two implications are true then disjuncting on both sides is also a valid implication. + */ + val disjunctionsImplies = Lemma((p1 ==> p2, q1 ==> q2) |- (p1 \/ q1) ==> (p2 \/ q2)) { + + val right = have((p1 ==> p2, q1 ==> q2, p1) |- p2 \/ q2) by Restate + val left = have((p1 ==> p2, q1 ==> q2, q1) |- p2 \/ q2) by Restate + + have((p1 ==> p2, q1 ==> q2, p1 \/ q1) |- p2 \/ q2) by LeftOr(left, right) + } + + /** + * Lemma --- If a class function F (whose representation is P) is monotonic then with respect to set inclusion, then S -> F(S) ∪ S is also + * a monotonic function. + * + * `s ⊆ t, F(s) ⊆ F(t) |- F(s) ∪ s ⊆ F(t) ∪ t` + */ + val unionPreimageMonotonic = Lemma((subset(s, t), P(s) ==> P(t)) |- (P(s) \/ in(x, s)) ==> (P(t) \/ in(x, t))) { + have(subset(s, t) |- forall(z, in(z, s) ==> in(z, t))) by Cut( + subsetAxiom of (x := s, y := t), + equivalenceApply of (p1 := subset(s, t), p2 := forall(z, in(z, s) ==> in(z, t))) + ) + thenHave(subset(s, t) |- in(x, s) ==> in(x, t)) by InstantiateForall(x) + have(thesis) by Cut(lastStep, disjunctionsImplies of (p1 := in(x, s), p2 := in(x, t), q1 := P(s), q2 := P(t))) + } + + /** + * Lemma --- Resticting a function to a smaller domain yields a subset of the original function. + * + * `x ⊆ y |- f|x ⊆ f|y` + */ + val restrictedFunctionDomainMonotonic = Lemma(subset(x, y) |- subset(restrictedFunction(f, x), restrictedFunction(f, y))) { + sorry + } + + // ******************* + // * SPECIFIC LEMMAS * + // ******************* + + /** + * Lemma --- Characterization of the union of the range of a cumulative function restricted to the successor of a natural number. + * + * `cumulative(h) and Dom(h) = N |- ∪ Im(h|n + 1) = h(n)` + */ + val unionRangeCumulativeRestrictedFunction = + Lemma((functional(h), relationDomain(h) === N, in(n, N), ∀(m, subset(m, n) ==> subset(app(h, m), app(h, n)))) |- unionRange(restrictedFunction(h, successor(n))) === app(h, n)) { + + val domainSubset = have(in(n, N) |- setIntersection(successor(n), N) === successor(n)) by Apply(intersectionNat).on(equivalenceApply of (p1 := in(n, N)), successorIsNat.asInstanceOf) + + have(functional(h) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ (successor(n) ∩ relationDomain(h)) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y) by Cut( + restrictedFunctionRangeMembership of (f := h, d := successor(n)), + rightAndEquivalence of (p1 := y ∈ restrRange(h, successor(n)), p2 := ∃(m, m ∈ (successor(n) ∩ relationDomain(h)) /\ (app(restrictedFunction(h, successor(n)), m) === y)), p := z ∈ y) + ) + + thenHave( + (functional(h), relationDomain(h) === N) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ (successor(n) ∩ N) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y + ) by RightSubstEq.withParametersSimple( + List((relationDomain(h), N)), + lambda(s, (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ (successor(n) ∩ s) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y) + ) + + thenHave( + (functional(h), in(n, N), relationDomain(h) === N, successor(n) ∩ N === successor(n)) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( + m, + m ∈ (successor(n) ∩ N) /\ (app(restrictedFunction(h, successor(n)), m) === y) + ) /\ z ∈ y + ) by Weakening + + thenHave( + (functional(h), in(n, N), relationDomain(h) === N, successor(n) ∩ N === successor(n)) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( + m, + m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) + ) /\ z ∈ y + ) by RightSubstEq.withParametersSimple( + List((successor(n) ∩ N, successor(n))), + lambda(s, (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ s /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y) + ) + + have( + (functional(h), in(n, N), relationDomain(h) === N) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y + ) by Cut(domainSubset, lastStep) + + have( + (functional(h), in(n, N), relationDomain(h) === N) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y) + ) by Apply(equivalenceRewriting).on( + lastStep, + existentialConjunctionWithClosedFormula of (P := lambda(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y)), p := z ∈ y) + ) + + thenHave( + (functional(h), in(n, N), relationDomain(h) === N) |- ∀( + y, + (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y) + ) + ) by RightForall + + have( + (functional(h), in(n, N), relationDomain(h) === N) |- ∃(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( + y, + ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y) + ) + ) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P := lambda(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y), + Q := lambda(y, ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y)) + ) + ) + + val introM = + thenHave( + (functional(h), in(n, N), relationDomain(h) === N) |- ∃(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( + m, + ∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y)) + ) + ) by Tableau + + have((∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (m ∈ successor(n) /\ z ∈ app(restrictedFunction(h, successor(n)), m))) by Exact( + equalityInExistentialQuantifier of (P := lambda(y, m ∈ successor(n) /\ z ∈ y)) + ) + + thenHave(m ∈ successor(n) |- (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (m ∈ successor(n) /\ z ∈ app(h, m))) by Substitution.ApplyRules( + restrictedFunctionApplication + ) + thenHave((∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (m ∈ successor(n) /\ z ∈ app(h, m))) by Tableau + + thenHave(subset(m, n) <=> m ∈ successor(n) |- (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (subset(m, n) /\ z ∈ app(h, m))) by RightSubstIff + .withParametersSimple( + List((m ∈ successor(n), subset(m, n))), + lambda(p, (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (p /\ z ∈ app(h, m))) + ) + + have(in(n, N) |- (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (subset(m, n) /\ z ∈ app(h, m))) by Cut(natSubset, lastStep) + + thenHave( + in(n, N) |- ∀(m, (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (subset(m, n) /\ z ∈ app(h, m))) + ) by RightForall + + have( + in(n, N) |- ∃(m, (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y)))) <=> ∃(m, subset(m, n) /\ z ∈ app(h, m)) + ) by Cut( + lastStep, + existentialEquivalenceDistribution of ( + P := lambda(m, ∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))), + Q := lambda(m, subset(m, n) /\ z ∈ app(h, m)) + ) + ) + + have((functional(h), in(n, N), relationDomain(h) === N) |- ∃(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, subset(m, n) /\ z ∈ app(h, m))) by Apply(equivalenceRewriting).on( + introM, + lastStep + ) + + val unionIsExists = + have((functional(h), in(n, N), relationDomain(h) === N) |- z ∈ unionRange(restrictedFunction(h, successor(n))) <=> ∃(m, subset(m, n) /\ z ∈ app(h, m))) by Apply(equivalenceRewriting).on( + lastStep, + unionAxiom.asInstanceOf + ) + + val cumulativeAssumption = ∀(m, subset(m, n) ==> subset(app(h, m), app(h, n))) + + have(cumulativeAssumption |- ∃(m, subset(m, n) /\ z ∈ app(h, m)) <=> z ∈ app(h, n)) subproof { + val seq1 = have(z ∈ app(h, n) |- z ∈ app(h, n)) by Hypothesis + have(z ∈ app(h, n) |- subset(n, n) /\ z ∈ app(h, n)) by RightAnd(seq1, subsetReflexivity of (x := n)) + thenHave(z ∈ app(h, n) |- ∃(m, subset(m, n) /\ z ∈ app(h, m))) by RightExists + val backward = thenHave(cumulativeAssumption |- z ∈ app(h, n) ==> ∃(m, subset(m, n) /\ z ∈ app(h, m))) by Weakening + + have(cumulativeAssumption |- cumulativeAssumption) by Hypothesis + thenHave(cumulativeAssumption |- subset(m, n) ==> subset(app(h, m), app(h, n))) by InstantiateForall(m) + have((cumulativeAssumption, subset(m, n), z ∈ app(h, m)) |- forall(z, z ∈ app(h, m) ==> z ∈ app(h, n))) by Apply(equivalenceApply).on( + lastStep, + subsetAxiom + ) + thenHave((cumulativeAssumption, subset(m, n) /\ z ∈ app(h, m)) |- z ∈ app(h, n)) by InstantiateForall(z) + thenHave((cumulativeAssumption, ∃(m, subset(m, n) /\ z ∈ app(h, m))) |- z ∈ app(h, n)) by LeftExists + val forward = thenHave(cumulativeAssumption |- ∃(m, subset(m, n) /\ z ∈ app(h, m)) ==> z ∈ app(h, n)) by RightImplies + + have(thesis) by RightIff(forward, backward) + } + + have((functional(h), in(n, N), relationDomain(h) === N, cumulativeAssumption) |- (z ∈ unionRange(restrictedFunction(h, successor(n)))) <=> z ∈ app(h, n)) by Apply(equivalenceRewriting).on( + unionIsExists, + lastStep + ) + thenHave((functional(h), in(n, N), relationDomain(h) === N, cumulativeAssumption) |- ∀(z, z ∈ unionRange(restrictedFunction(h, successor(n))) <=> z ∈ app(h, n))) by RightForall + + have(thesis) by Apply(equivalenceApply).on(lastStep, extensionalityAxiom.asInstanceOf) + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala new file mode 100644 index 000000000..7a05b0b49 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala @@ -0,0 +1,174 @@ +/** + * Defines a set of tactics to reason on Algebraic Data Types + */ + +package lisa.maths.settheory.types.adt + +import lisa.maths.settheory.SetTheory.{*, given} +import ADTDefinitions.* +import Helpers.* + +/** + * Tactic performing a structural induction proof over an algebraic data type. + * + * ===Usage=== + * {{{ + * have(forall(x, x :: adt => P(x)) /*or*/ x :: adt |- P(x)) by Induction(x, adt) { + * Case(c1, x1, ..., xn) subproof { + * // proof of P(xi) /\ ... P(xj) => P(c1(x1, ..., xn)) + * } + * ... + * Case(cm, x1, ..., xk) subproof { + * // proof of P(xi) /\ ... P(xj) => P(c1(x1, ..., xn')) + * } + * } + * }}} + * + * x and adt are inferred from the context if not provided by the user. + * + * Supports only 1 formula on the right hand side of the sequent. + * @param expectedVar the variable on which the induction is performed + * @param expectedADT the algebraic data type on which the induction is performed + */ +class Induction[M <: Arity](expectedVar: Option[Variable], expectedADT: Option[ADT[M]]) extends lisa.prooflib.ProofTacticLib.ProofTactic { + + /** + * Given a proof of the claim for each case (possibly using the induction hypothesis), + * reassemble them to generate a proof of the claim of the form + * `∀x. x :: adt => P(x)` + * + * @param proof the proof in which the induction is performed + * @param cases the proofs of the claim for each case in addition to the variables used by the user + * @param inductionVariable the variable over which the induction is performed + * @param adt the algebraic data type to perform induction on + * @param prop the property to prove //TODO: Change to a lambda expression (Scala 3.4.2) + */ + private def proveForallPredicate[N <: Arity](using proof: lisa.SetTheoryLibrary.Proof)(cases: Map[Constructor[N], (Seq[Variable], proof.Fact)], inductionVariable: Variable, adt: ADT[N], typeVariablesSubst: Seq[Term], propFun: Term => Formula, context: Set[Formula]): proof.Fact = + + val prop = lambda[Term, Formula](x, propFun(x)) + val typeVariablesSubstPairs = adt.typeVariables.toSeq.zip(typeVariablesSubst).map(SubstPair(_, _)) + val instTerm = adt(typeVariablesSubst*) + + adt.constructors.foldLeft[proof.Fact](adt.induction.of((typeVariablesSubstPairs :+ (P := prop))*)) ( (acc, c) => + val inductiveCaseProof = cases(c)._1.zip(c.underlying.underlying.specification.map(_.substitute(typeVariablesSubstPairs*))).foldRight[proof.Fact](cases(c)._2) ( (el, acc2) => + val (v, ty) = el + val accRight: Formula = acc2.statement.right.head + ty match + case Self => + have((acc2.statement - accRight) by Weakening(acc2) + thenHave((lastStep.statement - (prop(v) ==> accRight)) by Weakening + thenHave(lastStep.statement.left |- forall(v, v :: instTerm ==> (prop(v) ==> accRight))) by RightForall + case GroundType(t)=> + thenHave((acc2.statement - accRight) by Weakening + thenHave(lastStep.statement.left |- forall(v, v :: t ==> accRight)) by RightForall + ) + acc.statement.right.head match + case Implies(trueInd, rest) => + // println(s"Case: ${c.fullName}") + // println(isSame(trueInd, inductiveCaseProof.statement.right.head)) + // println(inductiveCaseProof.statement) + // println(" + ") + // println(acc.statement) + // println(" = ") + // println((acc.statement.left ++ inductiveCaseProof.statement.left) |- rest) + have((acc.statement.left ++ inductiveCaseProof.statement.left) |- rest) by Sorry//Cut(inductiveCaseProof, acc) + case _ => throw UnreachableException + ) + thenHave(context |- forall(inductionVariable, inductionVariable :: instTerm ==> prop(inductionVariable))) by Tautology //Change + + + + /** + * Infers the variable, the ADT and the arguments of the ADT from a formula of the form `x :: ADT(T1, ..., Tn)`. + * + * @param f the formula to infer these elements from + */ + def inferArguments(f: Formula): Option[(Variable, ADT[?], Seq[Term])] = + def checkFoundArguments(foundVar: Variable, foundADT: ADT[?], args: Seq[Term]): Option[(Variable, ADT[?], Seq[Term])] = + (expectedVar, expectedADT) match + case (Some(v), _) if v != foundVar => None + case (_, Some(a)) if a != foundADT => None + case _ => Some((foundVar, foundADT, args)) + + f match + case TypeAssignment(Variable(id), ADT(foundADT, args)) => + checkFoundArguments(Variable(id), foundADT, args) + case AppliedPredicate(in, Seq[Term](Variable(id), ADT(foundADT, args))) => + checkFoundArguments(Variable(id), foundADT, args) + case _ => + None + + /** + * Infers the variable, the ADT and the arguments of the ADT from a set of formula + * containing one is of the form `x :: ADT(T1, ..., Tn)`. + * + * @param s the set of formula to infer these elements from + */ + def inferArguments(s: Set[Formula]): Option[(Variable, ADT[?], Seq[Term])] = + s.foldLeft[Option[(Variable, ADT[?], Seq[Term])]](None)((acc, prem) => + acc.orElse(inferArguments(prem)) + ) + + /** + * Infers the variable, the ADT and the arguments of the ADT from a sequent whose one of the premises + * is of the form `x :: ADT(T1, ..., Tn)`. + * + * @param seq the sequent to infer these elements from + */ + def inferArguments(seq: Sequent): Option[(Variable, ADT[?], Seq[Term], Option[Formula])] = + inferArguments(seq.left).map(p => (p._1, p._2, p._3, None)) + .orElse( + seq.right.head match + case Forall(x, Implies(assignment, prop)) => + inferArguments(assignment).filter(p => p._1 == x).map(p => (p._1, p._2, p._3, Some(prop))) + case _ => None + ) + + /** + * Given a proof of the claim for each case (possibly using the induction hypothesis), + * reassemble the subproofs to generate a proof of the claim for every element of the ADT. + * + * @tparam N the arity of the ADT + * @param proof the scope in which the induction is performed + * @param cases the cases to prove. A [[CaseBuilder]] is a mutable data structure that register every case that + * has been added to the tactic. + * @param bot the claim + */ + def apply[N <: Arity](using proof: lisa.SetTheoryLibrary.Proof)(cases: ADTSyntax.CaseBuilder[N, proof.ProofStep, (Sequent, Seq[Term], Variable)] ?=> Unit)(bot: Sequent): proof.ProofTacticJudgement = + inferArguments(bot) match + case Some((inferedVar, inferedADT, inferedArgs, inferedProp)) => + + val prop = inferedProp.getOrElse(bot.right.head) + val propFunction = (t: Term) => inferedProp.getOrElse(bot.right.head).substitute(inferedVar -> t) + val assignment = inferedVar :: inferedADT(inferedArgs*) + val context = (if inferedProp.isDefined then bot else bot -<< assignment).left + val builder = ADTSyntax.CaseBuilder[N, proof.ProofStep, (Sequent, Seq[Term], Variable)]((context |- prop, inferedArgs, inferedVar)) + cases(using builder) + + builder.isValid(inferedADT.asInstanceOf[ADT[N]]) match + case None => + TacticSubproof { sp ?=> + proveForallPredicate(using sp)(builder.build, inferedVar, inferedADT.asInstanceOf[ADT[N]], inferedArgs, propFunction, context) + if !inferedProp.isDefined then + lastStep.statement.right.head match + case Forall(_, phi) => + thenHave(context |- phi) by InstantiateForall(inferedVar) + case _ => throw UnreachableException + + thenHave(bot) by Tautology + } + case Some(msg) => proof.InvalidProofTactic(msg) + + case None => proof.InvalidProofTactic("No variable typed with the ADT found in the context.") + +} + +/** + * Companion object for the [[Induction]] tactic class. + */ +object Induction { + def apply()(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(None, None) + def apply[N <: Arity](adt: ADT[N])(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(None, Some(adt)) + def apply(v: Variable)(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(Some(v), None) + def apply[N <: Arity](v: Variable, adt: ADT[N])(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(Some(v), Some(adt)) +} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala new file mode 100644 index 000000000..4b1ed5713 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala @@ -0,0 +1,285 @@ +/** + * Gives a type theoretic interpretation to algebraic data types and functions over them. + */ + +package lisa.maths.settheory.types.adt + +import lisa.maths.settheory.SetTheory.{_, given} +import lisa.maths.settheory.types.TypeLib.any +import lisa.maths.settheory.types.TypeSystem.FunctionalClass +import lisa.maths.settheory.types.TypeSystem.TypedConstantFunctional + +/** + * Type theoretic interpretation of a constructor, that is a function whose type is + * + * `c :: ∀X1, ..., Xn. T1 -> ... -> Tn -> ADT + * + * @tparam N the number of type variables appearing in the definition of this constructor's ADT + * @param line the line at which this constructor is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param file the file in which this constructor is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param underlying the set theoretic underlying constructor + */ +class Constructor[N <: Arity] private[adt] (using line: sourcecode.Line, file: sourcecode.File)( + private[adt] val underlying: SemanticConstructor[N] +) extends TypedConstantFunctional[N]( + underlying.fullName, + underlying.typeArity, + FunctionalClass(Seq.fill(underlying.typeArity)(any), underlying.typeVariablesSeq, underlying.typ, underlying.typeArity), + underlying.intro + ) { + + /** + * Name of the constructor + * + * e.g `list/cons` or `list/nil` + */ + val name = underlying.fullName + + /** + * Theorem --- Introduction rule + * + * `c :: ∀X1, ..., Xn. T1 -> ... -> Tn -> ADT + * + * where `c` is this constructor, `ADT` the ADT it belongs to and `T1, ..., Tn` the domains of the constructor's arguments. + * X1, ..., Xn are the type variables of the ADT. + */ + val intro = + THM(underlying.intro.statement, s"${name} introduction rule", line.value, file.value, Theorem) { + have(underlying.intro) + } + + /** + * Theorem --- Injectivity + * + * ` c(x1, ..., xn) = c(y1, ..., yn) <=> x1 = y1 /\ ... /\ xn = yn` + */ + lazy val injectivity = + THM(underlying.injectivity.statement, s"${name} injectivity", line.value, file.value, Theorem) { + have(underlying.injectivity) + } + + /** + * Type variables appearing in the signature of this constructor + */ + val typeVariables: Variable ** N = underlying.typeVariables +} + +/** + * Type theoretic polymorphic inductive datatype. Comes with a structural induction schema, injection and pattern matching. + * + * @tparam N the number of type variables appearing in the definition of this ADT + * @param line the line at which this ADT is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param file the file in which this ADT is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param underlying + * @param constructors + */ +class ADT[N <: Arity] private[adt] (using line: sourcecode.Line, file: sourcecode.File)( + private[adt] val underlying: SemanticADT[N], + private[adt] val constructors: Seq[Constructor[N]] +) { + + /** + * Name of this ADT + */ + val name = underlying.name + + /** + * Identifier of this ADT. + */ + val id: Identifier = underlying.id + ADT.identifiersToADT.addOne(id -> this) + + /** + * Theorem --- Structural induction principle + * + * e.g. `P(nil) => (∀x :: T, l :: list(T). P(l) => P(cons(x, l)))) => ∀l :: list(T). P(l)` + */ + lazy val induction = + THM(underlying.induction.statement, s"${name} structural induction principle", line.value, file.value, Theorem) { + have(underlying.induction) + } + + /** + * Theorem --- Elimination rules (Pattern Matching) + * + * `x :: ADT |- ∃ x1, ..., xn. x = c1 * x1 * ... * xn \/ ... \/ ∃ x1, ..., xn'. x = cm * x1 * ... * xn' + * + * Every term of this ADT is an instance of one of its constructors. + * + * e.g. `∀l :: list(T). l = nil \/ ∃x, xs. l = cons(x, xs)` + */ + lazy val elim = + THM(underlying.elim.statement, s"${name} elimination rule", line.value, file.value, Theorem) { + have(underlying.elim) + } + + /** + * Theorem --- Injectivity + * + * ` c1 * x1 * ... * xn != c2 * y1 * ... * ym` + * + * Instances of different constructors are different. + * + * e.g. `cons(x, l) != nil` + * + * @param c1 the first constructor + * @param c2 the second constructor + */ + def injectivity(c1: Constructor[N], c2: Constructor[N]) = + val injectivityLemma = underlying.injectivity(c1.underlying, c2.underlying) + THM(injectivityLemma.statement, s"${c1.name}-${c2.name} injectivity", line.value, file.value, Theorem) { + have(injectivityLemma) + } + + /** + * Type variables appearing in the signature of this ADT + */ + val typeVariables: Variable ** N = underlying.typeVariables + + /** + * Instantiate the type variables of this ADT with given types. + * Checks the arity at runtime. + * + * @param args the types to instantiate the type variables with + */ + def applyUnsafe(args: Term ** N): Term = underlying.term(args.toSeq) + + /** + * Instantiate the type variables of this ADT with given types. + * Checks the arity at runtime. + * + * @param args the types to instantiate the type variables with + */ + def applySeq(args: Seq[Term]): Term = underlying.term(args) + + /** + * Instantiate the type variables of this ADT with given types. + * Checks the arity at runtime. + * + * TODO: wait Scala 3.4.2 to remove this method and extend Term ** N |-> Term instead + * + * @param args the types to instantiate the type variables with + */ + def apply(args: Term*): Term = underlying.term(args) +} + +private object ADT { + /** + * Global map from object identifiers to ADTs + */ + private val identifiersToADT: scala.collection.mutable.Map[Identifier, ADT[?]] = scala.collection.mutable.Map.empty + + /** + * Checks if a label is an ADT, and returns it if it is the case. + * + * @param l the label to check + */ + def unapply(l: Label[?]): Option[ADT[?]] = getADT(l.id) + + /** + * Checks if a term is an instance of an ADT and if it is the case, returns + * the appropriate instances of the type variables. + * + * @param term the term to check + */ + def unapply(obj: Term): Option[(ADT[?], Seq[Term])] = + obj match + case l: Label[?] => + val lwidened: Label[?] = l + unapply(lwidened).map((_, Seq.empty)) + case AppliedFunctional(l, args) => unapply(l).map((_, args)) + case _ => None + + /** + * Checks if a class is an instance of an ADT and if it is the case, returns + * the appropriate instances of the type variables. + * + * @param c the class to check + */ + def unapply(c: Class): Option[(ADT[?], Seq[Term])] = + c match + case t: Term => unapply(t) + case _ => None + + /** + * Returns the ADT associated with an identifier. + * + * @param id the identifier of the ADT + */ + def getADT(id: Identifier): Option[ADT[?]] = identifiersToADT.get(id) +} + +/** + * Type theoretic function over algebraic data types. Its definition is the direct sum of the definitions of its constructors. + * Comes with introduction and elimination rules. + * + * @constructor gives a type theoretic interpretation to a set theoretic function over an ADT + * @tparam N the number of type variables appearing in the definition of this function's domain + * @param line the line at which this ADT is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param file the file in which this ADT is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param underlying the underlying set theoretic function + * @param adt the domain of this function + */ +private class ADTFunction[N <: Arity](using line: sourcecode.Line, file: sourcecode.File)( + private val underlying: SemanticFunction[N], + private val adt: ADT[N] +) extends TypedConstantFunctional[N]( + underlying.fullName, + underlying.typeArity, + FunctionalClass(Seq.fill(underlying.typeArity)(any), underlying.typeVariablesSeq, underlying.typ, underlying.typeArity), + underlying.intro + ) { + + /** + * Name of the function + * + * e.g. list/length + */ + val name = underlying.fullName + + /** + * Theorem --- Elimination rules + * + * `f * (c * x1 * ... * xn) = case(c, x1, ..., xn)` + * + * That is, when this function is applied to a constructor, it returns the corresponding case. + */ + val elim: Map[Constructor[N], THM] = adt.constructors + .map(c => + ( + c, + THM(underlying.shortDefinition(c.underlying).statement, s"${name} elimination rule: ${c.name} case", line.value, file.value, Theorem) { + have(underlying.shortDefinition(c.underlying)) + } + ) + ) + .toMap + + /** + * Alias for [[this.elim]] + */ + val shortDefinition: Map[Constructor[N], THM] = elim + + /** + * Theorem --- Introduction rule + * + * `∀X1, ..., Xn. f(X1, ..., Xn) : ADT(X1, ..., Xn) -> T` + * + * where `f` is this function, `ADT` the ADT it takes argument and `T` its return type. + */ + val intro: THM = + THM(underlying.intro.statement, s"${name} introduction rule", line.value, file.value, Theorem) { + have(underlying.intro) + } + + /** + * Type variables in the signature of the function + */ + val typeVariables: Variable ** N = underlying.typeVariables +} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala new file mode 100644 index 000000000..70087f4c2 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala @@ -0,0 +1,1747 @@ +/** + * This file implements tactics to generate polymorphic set theoretic inductive algebraic data types (or ADT) and prove properties about them. + * An algebraic data type is the least set closed under introduction rules, also known as constructors. + * A constructor takes arguments as input that can either belong to other types (non inductive arguments) + * or to the ADT itself (inductive arguments). + * + * An example of algebraic data type is the type of singly linked lists: + * + * list ::= nil() | cons(head: T, tail: list) + */ + +package lisa.maths.settheory.types.adt + +import lisa.maths.settheory.SetTheory.{*, given} +import lisa.maths.settheory.functions.* +import Helpers.* +import Helpers.{/\, \/, ===} +import ADTDefinitions.* +import ADTHelperTheorems as ADTThm +import ADTThm.{N, pair, pairExtensionality} +import lisa.maths.settheory.functions.|=> +import lisa.maths.settheory.types.TypeSystem.{ :: } +import lisa.maths.Quantifiers.{universalEquivalenceDistribution} +import lisa.fol.FOL.Variable + +/** + * Helpers for constructors + */ +private object Constructors { + + /** + * Global counter used to uniquely identify constructors and thereby avoid structural subtyping. + */ + var tagCounter = 0 +} + +/** + * Syntactic set theoretical interpretation of a constructor for an algebraic data type. + * In set theory, a constructor is a tuple containing the arguments it has been applied to, in addition to a tag + * uniquely identifying it. + * + * E.g. `cons(1, nil())` is represented as `(tagcons, (1, ((tagnil, ∅), ∅)))` + * + * Constructors injectivity is proved within this class. + * + * @constructor creates a new constructor out of a user specification + * @param specification types that the constructor takes as arguments + * @param variables1 variables used to represent the arguments of the constructor + * @param variables2 alternative set of variables to avoid capture issues + */ +private class SyntacticConstructor( + val specification: Seq[ConstructorArgument], + val variables1: Seq[Variable], + val variables2: Seq[Variable], + ) { + + /** + * Unique identifier of this constructor + */ + val tag: Int = Constructors.tagCounter + Constructors.tagCounter = Constructors.tagCounter + 1 + + /** + * Term representation of the tag of this constructor + */ + val tagTerm: Term = toTerm(tag) + + /** + * Sequence of variables used to represent the arguments of the constructor + */ + val variables: Seq[Variable] = variables1 + + /** + * Number of arguments that this constructor takes + */ + val arity: Int = specification.length + + /** + * Sequence of variables of the constructor with their respective domains. + */ + val signature1: Seq[(Variable, ConstructorArgument)] = variables1.zip(specification) + + /** + * Alternative sequence of variables of the constructor with their respective domains. + */ + val signature2: Seq[(Variable, ConstructorArgument)] = variables2.zip(specification) + + /** + * Sequence of variables of the constructor with their respective domains. + */ + val signature: Seq[(Variable, ConstructorArgument)] = signature1 + + /** + * Internally, an instance of this constructor is represented as a list. + * The first element of this list is the tag of this constructor. + * The following elements are its arguments. We represent lists as chained + * pairs followed by the empty set. + * + * e.g. cons(1, nil()) --> (tagcons, (1, ((tagnil, ∅), ∅))) + * + * @param args the arguments of this instance of the constructor + */ + def term(args: Seq[Term]): Term = pair(tagTerm, subterm(args)) + + /** + * Internal representation of an instance of this constructor in which arguments are schematic variables. + */ + val term1: Term = term(variables1) + + /** + * Internal representation of an instance of this constructor in which arguments are an alternative set of schematic variables. + */ + val term2: Term = term(variables2) + + /** + * Internal representation of an instance of this constructor in which arguments are schematic variables. + */ + val term: Term = term1 + + /** + * Internal representation of an instance of this constructor without the tag + * + * @param args the arguments of this instance of the constructor + * + * @see [[this.term]] + */ + def subterm(args: Seq[Term]): Term = args.foldRight[Term](emptySet)(pair(_, _)) + + /** + * Internal representation of an instance of this constructor without the tag, in which arguments are schematic variables. + */ + val subterm1: Term = subterm(variables1) + + /** + * Internal representation of an instance of this constructor without the tag, in which arguments are an alternative set + * of schematic variables. + */ + val subterm2: Term = subterm(variables2) + + /** + * Internal representation of an instance of this constructor without the tag, in which arguments are schematic variables. + */ + val subterm: Term = subterm1 + + /** + * Theorem --- Injectivity of constructors. + * + * Two instances of this constructor are equal if and only if all of their arguments are pairwise equal + * + * e.g. cons(head1, tail1) === cons(head2, tail2) <=> head1 === head2 /\ tail1 === tail2 + */ + lazy val injectivity = + if arity == 0 then + Lemma(term1 === term2) { + have(thesis) by RightRefl + } + else + Lemma((term1 === term2) <=> (variables1 === variables2)) { + + // STEP 1: Get rid of the tag using pair extensionality + have((term1 === term2) <=> (subterm1 === subterm2)) by Restate.from(pairExtensionality of (a := tagTerm, b := subterm1, c := tagTerm, d := subterm2)) + + // STEP 2: Repeat pair extensionality until all variables have been pulled out of the term + variables1 + .zip(variables2) + .foldLeft(Seq.empty[Variable], variables1, Seq.empty[Variable], variables2, lastStep)((acc, v) => + + // pulledVars1 are the variables that have been pulled out of the left term + // remainingVars1 are the variables that are still in the left term + // pulledVars2 are the variables that have been pulled out of the right term + // remainingVars2 are the variables that are still in the right term + val (pulledVars1, remainingVars1, pulledVars2, remainingVars2, previousFact) = acc + + // v1 and v2 are the variables that are being pulled out + val (v1, v2) = v + + val updatedPulledVars1 = pulledVars1 :+ v1 + val updatedPulledVars2 = pulledVars2 :+ v2 + val updatedRemainingVars1 = remainingVars1.tail + val updatedRemainingVars2 = remainingVars2.tail + + val subsubterm1 = subterm(updatedRemainingVars1) + val subsubterm2 = subterm(updatedRemainingVars2) + + have( + (pair(v1, subsubterm1) === pair(v2, subsubterm2)) <=> + ((v1 === v2) /\ (subsubterm1 === subsubterm2)) + ) by Restate.from(pairExtensionality of (a := v1, b := subsubterm1, c := v2, d := subsubterm2)) + have( + ((pulledVars1 === pulledVars2) /\ (pair(v1, subsubterm1) === pair(v2, subsubterm2))) <=> + ((pulledVars1 === pulledVars2) /\ (v1 === v2) /\ (subsubterm1 === subsubterm2)) + ) by Cut( + lastStep, + ADTThm.rightAndEquivalence of (p := pulledVars1 === pulledVars2, p1 := pair(v1, subsubterm1) === pair(v2, subsubterm2), p2 := (v1 === v2) /\ (subsubterm1 === subsubterm2)) + ) + val newFact = have( + (term1 === term2) <=> + ((updatedPulledVars1 === updatedPulledVars2) /\ (subsubterm1 === subsubterm2)) + ) by Apply(ADTThm.equivalenceRewriting).on(lastStep, previousFact) + + (updatedPulledVars1, updatedRemainingVars1, updatedPulledVars2, updatedRemainingVars2, newFact) + ) + } + +} + +/** + * Syntactic set theoretical interpretation of an algebraic data type. That is the least set closed under [[SyntacticConstructor]]. + * + * E.g. list is the smallest set containing nil and closed under the syntactic operation cons. + * + * Injectivity between different constructors, introduction rules and structural induction are proved within this class. + * + * @constructor creates a new algebraic data type out of a user specification. + * @param line the line at which the ADT is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param file the file in which the ADT is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param name the name of the ADT + * @param constructors constructors of the ADT + * @param typeVariables type variables used in the definition of this ADT + */ +private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: sourcecode.File)( + val name: String, + val constructors: Seq[SyntacticConstructor], + val typeVariables: Variable ** N, + ) { + + /** + * Sequence of type variables used in the definition of this ADT + */ + val typeVariablesSeq: Seq[Variable] = typeVariables.toSeq + + /** + * Number of type variables used in the definition of this ADT + */ + val typeArity: N = typeVariablesSeq.length.asInstanceOf[N] + + // *************** + // * INJECTIVITY * + // *************** + + /** + * Theorem --- Injectivity of constructors. + * + * Two instances of different construcors are always different. + * + * e.g. Nil != Cons(head, tail) + */ + def injectivity(c1: SyntacticConstructor, c2: SyntacticConstructor) = + require(c1.tag != c2.tag, "The given constructors must be different.") + + Lemma(!(c1.term1 === c2.term2)) { + + // STEP 0: Caching + val tagTerm1: Term = c1.tagTerm + val tagTerm2: Term = c2.tagTerm + + // STEP 1: Prove that the tags are different + val diffTag = have(!(tagTerm1 === tagTerm2)) subproof { + + // STEP 1.1: Order the tags + val minTag: Int = Math.min(c1.tag, c2.tag) + val maxTag: Int = Math.max(c1.tag, c2.tag) + + val start = have(tagTerm1 === tagTerm2 |- toTerm(maxTag) === toTerm(minTag)) by Restate + + // STEP 1.2: Apply successor injectivity to both tags until one becomes 0 + (1 to minTag).foldLeft(start)((fact, i) => + val midMaxTag = toTerm(maxTag - i) + val midMinTag = toTerm(minTag - i) + have(successor(midMaxTag) === successor(midMinTag) |- midMaxTag === midMinTag) by Cut( + ADTThm.successorInjectivity of (n := midMaxTag, m := midMinTag), + ADTThm.equivalenceApply of (p1 := successor(midMaxTag) === successor(midMinTag), p2 := midMaxTag === midMinTag) + ) + have(tagTerm1 === tagTerm2 |- midMaxTag === midMinTag) by Cut(fact, lastStep) + ) + + val chainInjectivity = thenHave(!(toTerm(maxTag - minTag) === emptySet) |- !(tagTerm1 === tagTerm2)) by Restate + + // STEP 1.3: Conclude using the fact that 0 is not the successor of any number + have(!(toTerm(maxTag - minTag) === emptySet)) by Exact(ADTThm.zeroIsNotSucc) + have(thesis) by Cut(lastStep, chainInjectivity) + } + + // STEP 2: Prove that the terms are different if the tags are different + have(c1.term1 === c2.term2 |- (tagTerm1 === tagTerm2) /\ (c1.subterm1 === c2.subterm2)) by Apply(ADTThm.equivalenceRevApply).on( + pairExtensionality of (a := tagTerm1, b := c1.subterm1, c := tagTerm2, d := c2.subterm2) + ) + thenHave(!(tagTerm1 === tagTerm2) |- !(c1.term1 === c2.term2)) by Weakening + + // STEP 3: Conclude + have(!(c1.term1 === c2.term2)) by Cut(diffTag, lastStep) + } + + // ************************* + // * INTRODUCTION FUNCTION * + // ************************* + + /** + * Formula describing whether the variables of a constructor belongs to their respective domain or s when they are self-referencing. + * + * @param c The considered constructor + * @param s The set of elements in which self-referencing variables of the constructor are. + */ + private def constructorVarsInDomain(c: SyntacticConstructor, s: Term): Formula = wellTypedFormula(c.signature)(s) + + /** + * Formula describing whether an element x is an instance of a specific constructor. + * + * @param c The constructor we want to check if x is an instance of + * @param x The element we want to check if it is an instance of c + * @param s The set of elements in which self-referencing arguments of the constructor are. + */ + private def isConstructor(c: SyntacticConstructor, x: Term, s: Term): Formula = + existsSeq(c.variables2, wellTypedFormula(c.signature2)(s) /\ (x === c.term2)) + + /** + * Formula describing whether an element x is an instance of one of this ADT's constructors. + * + * @param x The element we want to check if it is an instance of some constructor. + * @param s The set of elements in which self-referencing arguments of the constructor are. + */ + private def isConstructor(x: Term, s: Term): Formula = \/(constructors.map(c => isConstructor(c, x, s))) + + /** + * The introduction (class) function applies this ADT's constructors to the argument to given to it. + * It then adds to elements of the resulting set to the one given in argument. For example, if all arguments of the + * constructors were self-referencing we would have: + * + * introductionFunction(s) = {y | y = c(x1, ..., xn) for some c ∈ constructors and x1, ..., xn ∈ s} ∪ s + * + * In order to avoid introducing a new symbol in the theory, we describe this function with a predicate. + * + * @param s the argument of this function, i.e. set of elements on which the constructors are applied + * @param y the element we want to check if it is in the image of s under the introduction function. + * + * @return a formula describing whether y ∈ introductionFunction(s) + * + * @note The existence of the image of the introduction function is guaranteed by the union and replacement axioms. Moreover, it is not necessary to compute the union with s. It however simplifies further proofs. See [[this.heightSuccessorStrong]] for a proof of the equivalence of the two definitions. + */ + private def isInIntroductionFunctionImage(s: Term)(y: Term): Formula = isConstructor(y, s) \/ in(y, s) + + + /** + * Lemma --- The introduction function is monotonic with respect to set inclusion. + * + * `s ⊆ t |- introductionFunction(s) ⊆ introductionFunction(t)` + */ + private val introductionFunctionMononotic = Lemma(subset(s, t) |- isInIntroductionFunctionImage(s)(x) ==> isInIntroductionFunctionImage(t)(x)) { + // In the rest of the proof we assume that s ⊆ t + + // STEP 0: Caching predicates that are often used + val subsetST = subset(s, t) + val isConstructorXS = isConstructor(x, s) + val isConstructorXT = isConstructor(x, t) + + // STEP 1: Prove x ∈ s implies x ∈ t + have(subsetST |- forall(z, in(z, s) ==> in(z, t))) by Apply(ADTThm.equivalenceApply of (p1 := subsetST)).on(subsetAxiom.asInstanceOf) + val subsetElimination = thenHave(subsetST |- in(z, s) ==> in(z, t)) by InstantiateForall(z) + + // STEP 2: For each constructor, prove that if x is an instance of that constructor with self referencing arguments in s + // then it is also an instance of some constructor with self referencing arguments in t + val isConstructorXSImpliesT = + for c <- constructors yield + // STEP 2.0: Caching predicates that are often used + // TODO change identifier + val labelEq = x === c.term2 + val isConstructorCXS = isConstructor(c, x, s) + val isConstructorCXT = isConstructor(c, x, t) + val varsWellTypedS = wellTypedFormula(c.signature2)(s) + val varsWellTypedT = wellTypedFormula(c.signature2)(t) + + if c.arity == 0 then have((subsetST, isConstructorCXS) |- isConstructorXT) by Restate + else + // STEP 2.1: Prove that we can expand the domain of the (quantified) variables of the constructor + val andSeq = + for (v, ty) <- c.signature2 yield have((subsetST, varsWellTypedS) |- in(v, ty.getOrElse(t))) by Weakening(subsetElimination of (z := v)) + val expandingDomain = have((subsetST, varsWellTypedS) |- varsWellTypedT) by RightAnd(andSeq*) + val weakeningLabelEq = have(labelEq |- labelEq) by Hypothesis + have((subsetST, varsWellTypedS, labelEq) |- varsWellTypedT /\ labelEq) by RightAnd(expandingDomain, weakeningLabelEq) + + // STEP 2.2: Prove that x stays an instance of this constructor if we expand the domain of the variables + thenHave((subsetST, varsWellTypedS, labelEq) |- isConstructorCXT) by QuantifiersIntro(c.variables2) + thenHave((subsetST, varsWellTypedS /\ labelEq) |- isConstructorCXT) by LeftAnd + thenHave((subsetST, isConstructorCXS) |- isConstructorCXT) by QuantifiersIntro(c.variables2) + + // STEP 2.3: Weaken the conclusion to some constructor instead of a specific one + thenHave((subsetST, isConstructorCXS) |- isConstructorXT) by Weakening + + // STEP 3: Prove that this holds for any constructor + // ? Steps 2 and 3 can be merged and optimized through the repeated use of an external theorem like [[ADTHelperTheorems.unionPreimageMonotonic]] + if constructors.isEmpty then have((subsetST, isConstructorXS) |- isConstructorXT) by Restate + else have((subsetST, isConstructorXS) |- isConstructorXT) by LeftOr(isConstructorXSImpliesT*) + + // STEP 4: Prove the thesis by showing that making the union with the function argument does not change the monotonicity + thenHave(subsetST |- isConstructorXS ==> isConstructorXT) by RightImplies + have(thesis) by Cut(lastStep, ADTThm.unionPreimageMonotonic of (P := lambda(s, isConstructorXS))) + } + + + /** + * Lemma --- Every constructor is in the image of the introduction function. + * + * `For every c ∈ constructors, xi ∈ s, ..., xj ∈ s |- c(x1, ..., xn) ∈ introductionFunction(s)` + */ + private val constructorIsInIntroductionFunction = constructors + .map(c => + // Caching + val constructorVarsInDomainCS = constructorVarsInDomain(c, s) + + c -> Lemma(constructorVarsInDomainCS |- isInIntroductionFunctionImage(s)(c.term)) { + + have(constructorVarsInDomainCS |- constructorVarsInDomainCS /\ (c.term === c.term)) by Restate + + // Replace each variable on the LHS of the equality by a quantified variable and then introduce an existential quantifier + (c.variables2).foldRight((c.variables1, List[Variable]()))((v, acc) => + + // At each step remove a variable and add a quantified one + val oldVariables = acc._1.init + val newVariables = v :: acc._2 + val vars = oldVariables ++ newVariables + + thenHave(constructorVarsInDomainCS |- existsSeq(newVariables, wellTypedFormula(vars.zip(c.specification))(s) /\ (c.term(vars) === c.term))) by RightExists + + (oldVariables, newVariables) + ) + + thenHave(constructorVarsInDomainCS |- isInIntroductionFunctionImage(s)(c.term)) by Weakening + } + ) + .toMap + + // ********************************** + // * EXTENDED INTRODUCTION FUNCTION * + // ********************************** + + /** + * The extended introduction (class) function takes a function f as an argument instead of set. + * - If f is not empty, it calls the introduction function on the union of the ranges of the function. Since f will + * always be cumulative by assumption, this is equivalent as passing as argument the broadest set among the ranges of f. + * - If the function is empty, it returns the empty set. + * + * This class function is in a suited format to be used within the transfinite recursion theorem, which will be called to + * construct the height function. + * + * @see [[this.heightFunctionUniqueness]] + * + * @param f the function given as argument to the extended introduction function + * @param x the element we want to check if it is in the image of f under the extended introduction function + * @return a formula describing whether x ∈ extendedIntroductionFunction(f) + */ + private def isInExtendedIntroductionFunctionImage(f: Term)(x: Term): Formula = !(f === emptySet) /\ isInIntroductionFunctionImage(unionRange(f))(x) + + /** + * Lemma --- The extended introduction function is monotonic with respect to set inclusion. + * + * `f ⊆ g |- extendedIntroductionFunction(f) ⊆ extendedIntroductionFunction(g)` + */ + private val extendedIntroductionFunctionMonotonic = Lemma(subset(f, g) |- isInExtendedIntroductionFunctionImage(f)(x) ==> isInExtendedIntroductionFunctionImage(g)(x)) { + + // STEP 0: Caching + val introFunUnionRangeF = isInIntroductionFunctionImage(unionRange(f))(x) + val introFunUnionRangeG = isInIntroductionFunctionImage(unionRange(g))(x) + + // STEP 1: Instantiate monotonicity of the introduction function for the union of the ranges of f and g + have(subset(f, g) |- introFunUnionRangeF ==> introFunUnionRangeG) by Cut( + ADTThm.unionRangeMonotonic, + introductionFunctionMononotic of (s := unionRange(f), t := unionRange(g)) + ) + val left = thenHave((subset(f, g), introFunUnionRangeF) |- introFunUnionRangeG) by Restate + + // STEP 2: Conclude by applying the conjuction on both sides + have((subset(f, g), !(f === emptySet), introFunUnionRangeF) |- isInExtendedIntroductionFunctionImage(g)(x)) by RightAnd(left, ADTThm.subsetNotEmpty of (x := f, y := g)) + } + + // ******************* + // * HEIGHT FUNCTION * + // ******************* + + /** + * The height function assigns to each natural number the set of elements of the ADT of that height or below. + * The set of terms with height 0 is empty. Non inductive constructors have height one. + * The height of an instance of an inductive constructor is the maximum height of its arguments plus one. + * The height function is guaranteed to exists and is unique. + * + * @see [[this.heightFunctionUniqueness]] + * + * @param g the function we want to know if it is the height function + * + * @return a formula that is true if and only if g is the height function + */ + private def isTheHeightFunction(h: Term): Formula = + functional(h) /\ (relationDomain(h) === N) /\ forall(n, in(n, N) ==> forall(x, in(x, app(h, n)) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x))) + + // Caching + private val fIsTheHeightFunction: Formula = isTheHeightFunction(f) + private val hIsTheHeightFunction: Formula = isTheHeightFunction(h) + + /** + * Lemma --- There exists a unique height function for this ADT. + * + * `∃!h. h = height` + * + * TODO: Prove this using transfinite recursion + */ + private val heightFunUniqueness = Axiom(existsOne(h, hIsTheHeightFunction)) + + /** + * Lemma --- The height function exists. + * + * `∃h. h = height` + */ + private val heightFunctionExistence = Lemma(exists(h, hIsTheHeightFunction)) { + have(thesis) by Apply(lisa.maths.Quantifiers.existsOneImpliesExists of (P := lambda(h, hIsTheHeightFunction))).on(heightFunUniqueness.asInstanceOf) + } + + /** + * Lemma --- If two functions are the height function then they are the same. + * + * `f = height /\ h = height => f = h` + */ + private val heightFunctionUniqueness2 = Lemma((fIsTheHeightFunction, hIsTheHeightFunction) |- f === h) { + have(thesis) by Cut(heightFunUniqueness, ADTThm.existsOneUniqueness of (P := lambda(h, hIsTheHeightFunction), x := f, y := h)) + } + + /** + * Lemma --- The height function is not empty. + * + * `height ≠ ∅` + */ + private val heightFunctionNonEmpty = Lemma(hIsTheHeightFunction |- !(h === emptySet)) { + // The proof goes by contradiction. If the height function is empty then its domain is empty as well. + // This would imply that the set of natural numbers is empty, which is a contradiction. + have(N === emptySet |- ()) by Restate.from(ADTThm.natNotEmpty) + thenHave((relationDomain(h) === emptySet, relationDomain(h) === N, relationDomain(h) === relationDomain(h)) |- ()) by LeftSubstEq.withParametersSimple( + List((relationDomain(h), emptySet), (relationDomain(h), N)), + lambda((x, y), y === x) + ) + thenHave((relationDomain(h) === N, relationDomain(h) === relationDomain(h)) |- !(relationDomain(h) === emptySet)) by RightNot + have(thesis) by Apply(ADTThm.nonEmptyDomain).on(lastStep) + } + + /** + * Lemma --- The set of elements of height n or below is the image of the extended introduction function under the height + * function restricted to n (consequence of transfinite recursion). + * + * `height(n) = extendedIntroductionFunction(height | n)` + */ + private val heightApplication = Lemma((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, n)) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x)) { + + // Caching + val extendedIntroFunRestrictedFunM = isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x) + val heightFunApplicationDef = forall(n, in(n, N) ==> forall(x, in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM)) + + // Nothing fancy, just instantiations and restates + have(heightFunApplicationDef |- heightFunApplicationDef) by Hypothesis + thenHave(heightFunApplicationDef |- in(n, N) ==> forall(x, in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM)) by InstantiateForall(n) + thenHave((heightFunApplicationDef, in(n, N)) |- forall(x, in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM)) by Restate + thenHave((heightFunApplicationDef, in(n, N)) |- in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM) by InstantiateForall(x) + thenHave(thesis) by Weakening + } + + /** + * Lemma --- The height function is monotonic + * + * `n <= m => height(n) ⊆ height(m)` + * + * TODO: Try to pull out + */ + private val heightMonotonic = Lemma((hIsTheHeightFunction, in(n, N), subset(m, n)) |- subset(app(h, m), app(h, n))) { + + // STEP 0: Caching + val extendedIntroFunRestrictedFunM = isInExtendedIntroductionFunctionImage(restrictedFunction(h, m))(x) + + // STEP 1: Unfold the definition of height(m) + have((hIsTheHeightFunction, in(n, N), subset(m, n)) |- in(x, app(h, m)) <=> extendedIntroFunRestrictedFunM) by Apply(heightApplication).on(ADTThm.subsetIsNat.asInstanceOf) + val unfoldHeightApplicationM = have((hIsTheHeightFunction, in(n, N), subset(m, n), in(x, app(h, m))) |- extendedIntroFunRestrictedFunM) by Cut( + lastStep, + ADTThm.equivalenceRevApply of (p1 := in(x, app(h, m)), p2 := extendedIntroFunRestrictedFunM) + ) + + // STEP 2: Use the monotonicity of the extended introduction function + have(subset(m, n) |- extendedIntroFunRestrictedFunM ==> isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x)) by Cut( + ADTThm.restrictedFunctionDomainMonotonic of (x := m, y := n, f := h), + extendedIntroductionFunctionMonotonic of (f := restrictedFunction(h, m), g := restrictedFunction(h, n)) + ) + have((hIsTheHeightFunction, in(n, N), subset(m, n), extendedIntroFunRestrictedFunM) |- in(x, app(h, n))) by Apply(ADTThm.equivalenceRevApply).on(lastStep, heightApplication.asInstanceOf) + + // STEP 3: Fold the definition of subset + have((hIsTheHeightFunction, in(n, N), subset(m, n), in(x, app(h, m))) |- in(x, app(h, n))) by Cut(unfoldHeightApplicationM, lastStep) + thenHave((hIsTheHeightFunction, in(n, N), subset(m, n)) |- in(x, app(h, m)) ==> in(x, app(h, n))) by RightImplies + thenHave((hIsTheHeightFunction, in(n, N), subset(m, n)) |- forall(x, in(x, app(h, m)) ==> in(x, app(h, n)))) by RightForall + have(thesis) by Apply(ADTThm.equivalenceRevApply).on(lastStep, subsetAxiom.asInstanceOf) + } + + /** + * Lemma --- There is no element of height 0 in the ADT. + * + * `!∃x ∈ adt. height(x) = 0` + */ + private val heightZero = Lemma(hIsTheHeightFunction |- !in(x, app(h, emptySet))) { + + // This is due to the fact that the extended introduction function is the empty set when the function is empty + // (which happens when the height is set to 0). + have(hIsTheHeightFunction |- in(x, app(h, emptySet)) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, emptySet))(x)) by Cut(ADTThm.zeroIsNat, heightApplication of (n := emptySet)) + thenHave((restrictedFunction(h, emptySet) === emptySet, hIsTheHeightFunction) |- !in(x, app(h, emptySet))) by + RightSubstEq.withParametersSimple( + List((restrictedFunction(h, emptySet), emptySet)), + lambda(s, in(x, app(h, emptySet)) <=> isInExtendedIntroductionFunctionImage(s)(x)) + ) + have(thesis) by Cut(ADTThm.restrictedFunctionEmptyDomain, lastStep) + } + + /** + * Lemma --- The set of elements of height n + 1 is the set of elements of height n to which the introduction function is applied. + * + * `height(n + 1) = introductionFunction(height(n))` + */ + private val heightSuccessorWeak = Lemma((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> isInIntroductionFunctionImage(app(h, n))(x)) { + + // STEP 1: Prove that the restriction of height to n + 1 is not empty + val restrHeightNotEmpty: Formula = !(restrictedFunction(h, successor(n)) === emptySet) + have(!(h === emptySet) |- restrHeightNotEmpty) by Cut(ADTThm.zeroIsNotSucc, ADTThm.restrictedFunctionNotEmpty of (d := successor(n))) + val restrHeightNotEmptyLemma = have(hIsTheHeightFunction |- restrHeightNotEmpty) by Cut(heightFunctionNonEmpty, lastStep) + + // STEP 2: Use the fact that if the function is cumulative then ∪ range(height | n + 1) = height(n) to conclude the proof + have((hIsTheHeightFunction, in(n, N)) |- subset(m, n) ==> subset(app(h, m), app(h, n))) by RightImplies(heightMonotonic) + thenHave((hIsTheHeightFunction, in(n, N)) |- forall(m, subset(m, n) ==> subset(app(h, m), app(h, n)))) by RightForall + val unionRangeRestr = have((hIsTheHeightFunction, in(n, N)) |- unionRange(restrictedFunction(h, successor(n))) === app(h, n)) by Apply(ADTThm.unionRangeCumulativeRestrictedFunction).on(lastStep) + + have((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, successor(n)))(x)) by Apply(heightApplication).on( + ADTThm.equivalenceApply of (p1 := in(n, N)), + ADTThm.successorIsNat.asInstanceOf + ) + + thenHave( + (hIsTheHeightFunction, in(n, N), unionRange(restrictedFunction(h, successor(n))) === app(h, n)) |- + in(x, app(h, successor(n))) <=> restrHeightNotEmpty /\ isInIntroductionFunctionImage(app(h, n))(x) + ) by + RightSubstEq.withParametersSimple( + List((unionRange(restrictedFunction(h, successor(n))), app(h, n))), + lambda(s, in(x, app(h, successor(n))) <=> (restrHeightNotEmpty /\ isInIntroductionFunctionImage(s)(x))) + ) + + have((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> restrHeightNotEmpty /\ isInIntroductionFunctionImage(app(h, n))(x)) by Cut(unionRangeRestr, lastStep) + + have((hIsTheHeightFunction, in(n, N), restrHeightNotEmpty) |- in(x, app(h, successor(n))) <=> isInIntroductionFunctionImage(app(h, n))(x)) by Apply(ADTThm.equivalenceAnd of (p2 := restrHeightNotEmpty)) + .on(lastStep) + + have(thesis) by Cut(restrHeightNotEmptyLemma, lastStep) + } + + // ******** + // * TERM * + // ******** + + /** + * Formula describing this ADT's term, i.e. the set of all its instances. + * It equal to the union of all the terms that have a height. + * + * `adt = ∪ height(n) = {x | ∃n ∈ N. x ∈ height(n)}` + * + * @param adt the set chracterizing this ADT + */ + private def termDefinition(adt: Term): Formula = forall(t, in(t, adt) <=> forall(h, hIsTheHeightFunction ==> in(t, unionRange(h)))) + + /** + * Lemma --- There exists a unique set satisfying the definition of this ADT + * + * `∃!z. z = ADT + */ + private val termExistence = Lemma(existsOne(z, termDefinition(z))) { + + // STEP 0: Caching + val termDefinitionRight = forall(h, hIsTheHeightFunction ==> in(t, unionRange(h))) + val inUnionRangeF = in(t, unionRange(f)) + + // STEP 1: Prove that there exists a term satisfying the definition of this ADT. + // Specifically, this term is the union of all the terms with a height. + have(exists(z, termDefinition(z))) subproof { + + // STEP 1.1: Prove the forward implication of the definition, using the uniqueness of the height function + have(inUnionRangeF |- inUnionRangeF) by Hypothesis + thenHave((f === h, inUnionRangeF) |- in(t, unionRange(h))) by RightSubstEq.withParametersSimple( + List((f, h)), + lambda(f, inUnionRangeF) + ) + have((fIsTheHeightFunction, hIsTheHeightFunction, inUnionRangeF) |- in(t, unionRange(h))) by Cut(heightFunctionUniqueness2, lastStep) + thenHave((fIsTheHeightFunction, inUnionRangeF) |- hIsTheHeightFunction ==> in(t, unionRange(h))) by RightImplies + thenHave((fIsTheHeightFunction, inUnionRangeF) |- termDefinitionRight) by RightForall + val forward = thenHave(fIsTheHeightFunction |- inUnionRangeF ==> termDefinitionRight) by RightImplies + + // STEP 1.2: Prove the backward implication of the definition + have(termDefinitionRight |- termDefinitionRight) by Hypothesis + thenHave(termDefinitionRight |- fIsTheHeightFunction ==> inUnionRangeF) by InstantiateForall(f) + val backward = thenHave(fIsTheHeightFunction |- termDefinitionRight ==> inUnionRangeF) by Restate + + // STEP 1.3: Use the existence of the height function to prove the existence of this ADT + have(fIsTheHeightFunction |- inUnionRangeF <=> termDefinitionRight) by RightIff(forward, backward) + thenHave(fIsTheHeightFunction |- forall(t, inUnionRangeF <=> termDefinitionRight)) by RightForall + + thenHave(fIsTheHeightFunction |- exists(z, forall(t, in(t, z) <=> termDefinitionRight))) by RightExists + thenHave(exists(f, fIsTheHeightFunction) |- exists(z, forall(t, in(t, z) <=> termDefinitionRight))) by LeftExists + have(thesis) by Cut(heightFunctionExistence, lastStep) + } + + // STEP 2: Conclude using the extension by definition + + have(thesis) by Cut(lastStep, uniqueByExtension of (schemPred := lambda(t, termDefinitionRight))) + } + + /** + * Class function defining the ADT. Takes as parameters the type variables of the ADT and return the set of all its instances. + */ + val polymorphicTerm = FunctionDefinition[N](name, line.value, file.value)(typeVariablesSeq, z, termDefinition(z), termExistence).label + + /** + * The set of all instances of the ADT where the type variables are not instantiated (i.e. are kept variable). + */ + val term = polymorphicTerm.applySeq(typeVariablesSeq) + + /** + * Definition of this ADT's term. + */ + private val termDefinition: Formula = termDefinition(term) + + /** + * Lemma --- This ADT satisfies its definition. + * + * `adt = ∪ height(n)` + */ + private val termSatisfiesDefinition = Lemma(termDefinition) { + have(thesis) by InstantiateForall(term)(polymorphicTerm.definition) + } + + // ************************* + // * TYPING / INTRODUCTION * + // ************************* + + /** + * Lemma --- Every element of this ADT has a height. Conversely, if an element has a height, it is in this ADT. + * + * ` x ∈ ADT <=> ∃n ∈ N. x ∈ height(n)` + * + * TODO: Split into two lemmas + */ + private val termHasHeight = Lemma(hIsTheHeightFunction |- in(x, term) <=> ∃(n, in(n, N) /\ in(x, app(h, n)))) { + + // STEP 0 : Instantiate the definition of this ADT and recover the forward and backward implications + val termDefinition = have(in(x, term) <=> forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) by InstantiateForall(x)(termSatisfiesDefinition) + val termDefinitionForward = have(in(x, term) |- forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) by Cut( + termDefinition, + ADTThm.equivalenceApply of (p1 := in(x, term), p2 := forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) + ) + val termDefinitionBackward = have(forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))) |- in(x, term)) by Cut( + termDefinition, + ADTThm.equivalenceRevApply of (p2 := in(x, term), p1 := forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) + ) + + // STEP 1 : Prove that an element is in this ADT if and only if it is in one of the images of the height function. + have(hIsTheHeightFunction |- in(x, term) <=> in(x, unionRange(h))) subproof { + + // STEP 1.1 : Forward implication + have(forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))) |- forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) by Hypothesis + thenHave(forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))) |- hIsTheHeightFunction ==> in(x, unionRange(h))) by InstantiateForall(h) + thenHave((forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))), hIsTheHeightFunction) |- in(x, unionRange(h))) by Restate + + val forward = have(hIsTheHeightFunction |- in(x, term) ==> in(x, unionRange(h))) by Apply(lastStep).on(termDefinitionForward) + + // STEP 1.2 : Backward implication, follows from uniqueness of the height function + have(in(x, unionRange(h)) |- in(x, unionRange(h))) by Hypothesis + thenHave((f === h, in(x, unionRange(h))) |- in(x, unionRange(f))) by RightSubstEq.withParametersSimple(List((f, h)), lambda(h, in(x, unionRange(h)))) + have((fIsTheHeightFunction, hIsTheHeightFunction, in(x, unionRange(h))) |- in(x, unionRange(f))) by Cut(heightFunctionUniqueness2, lastStep) + thenHave((hIsTheHeightFunction, in(x, unionRange(h))) |- fIsTheHeightFunction ==> in(x, unionRange(f))) by RightImplies + thenHave((hIsTheHeightFunction, in(x, unionRange(h))) |- forall(f, fIsTheHeightFunction ==> in(x, unionRange(f)))) by RightForall + have((hIsTheHeightFunction, in(x, unionRange(h))) |- in(x, term)) by Cut(lastStep, termDefinitionBackward) + val backward = thenHave(hIsTheHeightFunction |- in(x, unionRange(h)) ==> in(x, term)) by RightImplies + + have(thesis) by RightIff(forward, backward) + } + + // STEP 2: Conclude by instantiating the union range membership lemma + have(hIsTheHeightFunction |- in(x, term) <=> ∃(n, in(n, relationDomain(h)) /\ in(x, app(h, n)))) by Apply(ADTThm.equivalenceRewriting).on(ADTThm.unionRangeMembership.asInstanceOf, lastStep) + + thenHave((hIsTheHeightFunction, relationDomain(h) === N) |- in(x, term) <=> ∃(n, in(n, N) /\ in(x, app(h, n)))) by RightSubstEq.withParametersSimple( + List((relationDomain(h), N)), + lambda(z, in(x, term) <=> ∃(n, in(n, z) /\ in(x, app(h, n)))) + ) + } + + /** + * Lemma --- Every element of this ADT has a height. Conversely, if an element has a height, it is in this ADT. + * + * ` xi, ..., xj ∈ ADT <=> ∃n ∈ N. xi, ..., xj ∈ height(n)` + * + * TODO: Work this out + * TODO: Split into two lemmas + */ + private val termsHaveHeight = constructors + .map(c => + c -> Lemma(hIsTheHeightFunction |- (constructorVarsInDomain(c, term) <=> ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n))))) { + + if c.variables.isEmpty then have(thesis) by Weakening(ADTThm.existsNat) + else + + // STEP 1: Backward implication + + val backward = have(hIsTheHeightFunction |- ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) ==> constructorVarsInDomain(c, term)) subproof { + val andSeq = for (v, ty) <- c.signature yield ty match + case Self => + val termHasHeightBackward = have((hIsTheHeightFunction, exists(n, in(n, N) /\ in(v, app(h, n)))) |- in(v, term)) by Cut( + termHasHeight of (x := v), + ADTThm.equivalenceRevApply of (p1 := ∃(n, in(n, N) /\ in(v, app(h, n))), p2 := in(v, term)) + ) + + have((in(n, N) /\ in(v, app(h, n))) |- in(n, N) /\ in(v, app(h, n))) by Restate + thenHave((in(n, N) /\ in(v, app(h, n))) |- exists(n, in(n, N) /\ in(v, app(h, n)))) by RightExists + have((hIsTheHeightFunction, in(n, N) /\ in(v, app(h, n))) |- in(v, term)) by Cut(lastStep, termHasHeightBackward) + thenHave((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- in(v, term)) by Weakening + case GroundType(t) => + have((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- in(v, t)) by Restate + + have((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- constructorVarsInDomain(c, term)) by RightAnd(andSeq*) + thenHave((hIsTheHeightFunction, exists(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) |- constructorVarsInDomain(c, term)) by LeftExists + } + + // STEP 2: Forward implication + + val forward = have(hIsTheHeightFunction |- constructorVarsInDomain(c, term) ==> ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) subproof { + val nSeq: Seq[Variable] = (0 until c.variables.size).map(i => Variable(s"n$i")) + val max = if c.arity == 0 then emptySet else nSeq.reduce[Term](setUnion(_, _)) + + val maxInN = have(/\(nSeq.map(n => in(n, N))) |- in(max, N)) by Sorry + + val andSeq = for ((v, ty), ni) <- c.signature.zip(nSeq) yield + val niInMax = have(subset(ni, max)) by Sorry + + ty match + case Self => + have((hIsTheHeightFunction, in(max, N), subset(ni, max)) |- subset(app(h, ni), app(h, max))) by Restate.from(heightMonotonic of (m := ni, n := max)) + have((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N)))) |- subset(app(h, ni), app(h, max))) by Sorry // Apply(lastStep).on(Seq(maxInN, niInMax), excluding = nSeq) + have((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N)))) |- forall(z, in(z, app(h, ni)) ==> in(z, app(h, max)))) by Apply(ADTThm.equivalenceApply) + .on(Seq(lastStep, subsetAxiom), excluding = nSeq) + thenHave((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N)))) |- in(v, app(h, ni)) ==> in(v, app(h, max))) by InstantiateForall(v) + thenHave((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N))), in(v, app(h, ni))) |- in(v, app(h, max))) by Restate + case GroundType(t) => + have((/\(nSeq.map(n => in(n, N))), hIsTheHeightFunction, in(v, t)) |- in(v, t)) by Restate + + have((/\(nSeq.map(n => in(n, N))), hIsTheHeightFunction, in(v, ty.getOrElse(app(h, ni)))) |- in(max, N) /\ in(v, ty.getOrElse(app(h, max)))) by RightAnd(maxInN, lastStep) + thenHave(nSeq.map(n => in(n, N) /\ in(v, ty.getOrElse(app(h, n)))).toSet + hIsTheHeightFunction |- in(max, N) /\ in(v, ty.getOrElse(app(h, max)))) by Weakening + thenHave(nSeq.map(n => in(n, N) /\ in(v, ty.getOrElse(app(h, n)))).toSet + hIsTheHeightFunction |- ∃(n, in(n, N) /\ in(v, ty.getOrElse(app(h, n))))) by RightExists + + sorry + } + + // STEP 3: Conclude + have(thesis) by RightIff(forward, backward) + } + ) + .toMap + + /** + * Lemma --- If all inductive arguments of a constructor have height below n then the instance of + * this constructor has height below n + 1. + * + * ` xi, ..., xj ∈ height(n) |- c(x1, ..., xn) ∈ height(n + 1)` + */ + private val heightConstructor = constructors + .map(c => + c -> Lemma((hIsTheHeightFunction, in(n, N), constructorVarsInDomain(c, app(h, n))) |- in(c.term, app(h, successor(n)))) { + + // Caching + val constructorInIntroFunHeight = isInIntroductionFunctionImage(app(h, n))(c.term) + + // Chaining the lemma on the elements of height n + 1 and the one on constructors being in the image of the introduction function + have((hIsTheHeightFunction, in(n, N), constructorInIntroFunHeight) |- in(c.term, app(h, successor(n)))) by Cut( + heightSuccessorWeak of (x := c.term), + ADTThm.equivalenceRevApply of (p1 := constructorInIntroFunHeight, p2 := in(c.term, app(h, successor(n)))) + ) + have((hIsTheHeightFunction, in(n, N), constructorVarsInDomain(c, app(h, n))) |- in(c.term, app(h, successor(n)))) by Cut(constructorIsInIntroductionFunction(c) of (s := app(h, n)), lastStep) + } + ) + .toMap + + /** + * Lemma --- If all inductive arguments of a constructor are in this ADT, and the non inductive ones in their respective domain, + * then the instance of this constructor is in this ADT as well. Also known as introduction rules. + * + * ` xi, ..., xj ∈ ADT |- c(x1, ..., xn) ∈ ADT` + */ + val intro = constructors + .map(c => { + c -> + Lemma(simplify(constructorVarsInDomain(c, term)) |- simplify(in(c.term, term))) { + // STEP 0: Instantiate the forward direction of termsHaveHeight. + val termsHaveHeightForward = have((hIsTheHeightFunction, constructorVarsInDomain(c, term)) |- ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) by Cut( + termsHaveHeight(c), + ADTThm.equivalenceApply of (p1 := constructorVarsInDomain(c, term), p2 := exists(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) + ) + + // STEP 1: Prove that if an instance of a constructor has height n + 1 then it is in this ADT. + val left = have(in(n, N) |- in(successor(n), N)) by Cut(ADTThm.successorIsNat, ADTThm.equivalenceApply of (p1 := in(n, N), p2 := in(successor(n), N))) + val right = have(in(c.term, app(h, successor(n))) |- in(c.term, app(h, successor(n)))) by Hypothesis + have((in(n, N), in(c.term, app(h, successor(n)))) |- in(successor(n), N) /\ in(c.term, app(h, successor(n)))) by RightAnd(left, right) + thenHave((in(n, N), in(c.term, app(h, successor(n)))) |- exists(m, in(m, N) /\ in(c.term, app(h, m)))) by RightExists + have((hIsTheHeightFunction, in(n, N), in(c.term, app(h, successor(n)))) |- in(c.term, term)) by Apply(ADTThm.equivalenceRevApply).on(lastStep, termHasHeight.asInstanceOf) + + // STEP 2: Prove that if the inductive arguments of the constructor have height then the instance of the constructor is in the ADT. + have((hIsTheHeightFunction, in(n, N), constructorVarsInDomain(c, app(h, n))) |- in(c.term, term)) by Cut(heightConstructor(c), lastStep) + + // STEP 3: Prove that if the inductive arguments of the constructor are in the ADT then they have a height and therefore + // the instance of the constructor is in the ADT. + thenHave((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- in(c.term, term)) by LeftAnd + thenHave((hIsTheHeightFunction, exists(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) |- in(c.term, term)) by LeftExists + have((hIsTheHeightFunction, constructorVarsInDomain(c, term)) |- in(c.term, term)) by Cut(termsHaveHeightForward, lastStep) + + // STEP 4: Remove lingering assumptions + thenHave((exists(h, hIsTheHeightFunction), constructorVarsInDomain(c, term)) |- in(c.term, term)) by LeftExists + have(constructorVarsInDomain(c, term) |- in(c.term, term)) by Cut(heightFunctionExistence, lastStep) + } + }) + .toMap + + // ************************ + // * STRUCTURAL INDUCTION * + // ************************ + + /** + * Lemma --- An element has height n + 1 if and only if it is the instance of some constructor with inductive arguments of height n. + * + * ` x ∈ height(n + 1) <=> x = c(x1, ..., xn) for some c and xi, ..., xj ∈ height(n)` + */ + private lazy val heightSuccessorStrong = Lemma((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> isConstructor(x, app(h, n))) { + val forward = have((hIsTheHeightFunction, in(n, N)) |- isInIntroductionFunctionImage(app(h, n))(x) ==> isConstructor(x, app(h, n))) subproof { + + def inductionFormula(n: Term): Formula = isInIntroductionFunctionImage(app(h, n))(x) ==> isConstructor(x, app(h, n)) + val inductionFormulaN: Formula = inductionFormula(n) + val inductionFormulaSuccN: Formula = inductionFormula(successor(n)) + + // STEP 1.1 : Base case + val isContructorXHEmptySet = isConstructor(x, app(h, emptySet)) + val baseCaseLeft = have(isContructorXHEmptySet |- isContructorXHEmptySet) by Hypothesis + val baseCaseRight = have((hIsTheHeightFunction, in(x, app(h, emptySet))) |- ()) by Restate.from(heightZero) + have((hIsTheHeightFunction, isInIntroductionFunctionImage(app(h, emptySet))(x)) |- isContructorXHEmptySet) by LeftOr(baseCaseLeft, baseCaseRight) + thenHave(hIsTheHeightFunction |- isInIntroductionFunctionImage(app(h, emptySet))(x) ==> isContructorXHEmptySet) by RightImplies + val inductiveCaseRemaining = have((hIsTheHeightFunction, forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormulaSuccN))) |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut( + lastStep, + ADTThm.natInduction of (P := lambda(n, inductionFormulaN)) + ) + + // STEP 1.2: Unfolding the definition of subset + have(subset(app(h, n), app(h, successor(n))) |- forall(z, in(z, app(h, n)) ==> in(z, app(h, successor(n))))) by Cut( + subsetAxiom of (x := app(h, n), y := app(h, successor(n))), + ADTThm.equivalenceApply of (p1 := subset(app(h, n), app(h, successor(n))), p2 := forall(z, in(z, app(h, n)) ==> in(z, app(h, successor(n))))) + ) + val subsetElimination = thenHave(subset(app(h, n), app(h, successor(n))) |- in(y, app(h, n)) ==> in(y, app(h, successor(n)))) by InstantiateForall(y) + + // STEP 1.3 : Use monotonicity to prove that y ∈ height(n) => y ∈ height(n + 1) + have(in(n, N) |- in(successor(n), N)) by Cut(ADTThm.successorIsNat, ADTThm.equivalenceApply of (p1 := in(n, N), p2 := in(successor(n), N))) + have((hIsTheHeightFunction, in(n, N), subset(n, successor(n))) |- subset(app(h, n), app(h, successor(n)))) by Cut(lastStep, heightMonotonic of (n := successor(n), m := n)) + have((hIsTheHeightFunction, in(n, N)) |- subset(app(h, n), app(h, successor(n)))) by Cut(ADTThm.subsetSuccessor, lastStep) + val liftHeight = have((hIsTheHeightFunction, in(n, N)) |- in(y, app(h, n)) ==> in(y, app(h, successor(n)))) by Cut(lastStep, subsetElimination) + + // STEP 1.4 : Generalize the above result to show that if for some c, x = c(x1, ..., xn) with xi, ..., xj ∈ height(n) + // then for some c', x = c'(x1, ..., xn) with xi, ..., xj ∈ height(n + 1). + + // Caching + val isConstructorXHN = isConstructor(x, app(h, n)) + val isConstructorXHSuccN = isConstructor(x, app(h, successor(n))) + val liftConstructorHeight = + if constructors.size == 0 then have((hIsTheHeightFunction, in(n, N), isConstructorXHN) |- isConstructorXHSuccN) by Restate + else + val liftConstructorHeightOrSequence = + for c <- constructors yield + + // Caching + val isConstructorCXHN = isConstructor(c, x, app(h, n)) + val isConstructorCXHSuccN = isConstructor(c, x, app(h, successor(n))) + val constructorVarsInHN = constructorVarsInDomain(c, app(h, n)) + val constructorVarsInHSuccN = constructorVarsInDomain(c, app(h, successor(n))) + + if c.arity == 0 then have((hIsTheHeightFunction, in(n, N), isConstructorCXHN) |- isConstructorCXHSuccN) by Restate + else + val liftHeightAndSequence = + for (v, ty) <- c.signature + yield have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- in(v, ty.getOrElse(app(h, successor(n))))) by Weakening(liftHeight of (y := v)) + + val left = have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInHSuccN) by RightAnd(liftHeightAndSequence*) + val right = have(x === c.term |- x === c.term) by Hypothesis + + have((hIsTheHeightFunction, in(n, N), constructorVarsInHN, (x === c.term)) |- constructorVarsInHSuccN /\ (x === c.term )) by RightAnd( + left, + right + ) + thenHave((hIsTheHeightFunction, in(n, N), constructorVarsInHN /\ (x === c.term)) |- constructorVarsInHSuccN /\ (x === c.term)) by LeftAnd + thenHave((hIsTheHeightFunction, in(n, N), constructorVarsInHN /\ (x === c.term)) |- isConstructorCXHSuccN) by QuantifiersIntro(c.variables) + thenHave((hIsTheHeightFunction, in(n, N), isConstructorCXHN) |- isConstructorCXHSuccN) by QuantifiersIntro(c.variables) + + thenHave((hIsTheHeightFunction, in(n, N), isConstructorCXHN) |- isConstructorXHSuccN) by Weakening + + have((hIsTheHeightFunction, in(n, N), isConstructorXHN) |- isConstructorXHSuccN) by LeftOr(liftConstructorHeightOrSequence*) + + // STEP 1.5: Show that x ∈ introductionFunction(height(n + 1)) => for some c, x = c(x1, ..., xn) + // with xi, ..., xj ∈ height(n + 1). + val heightSuccessorWeakForward = have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n)))) |- isInIntroductionFunctionImage(app(h, n))(x)) by Cut( + heightSuccessorWeak, + ADTThm.equivalenceApply of (p1 := in(x, app(h, successor(n))), p2 := isInIntroductionFunctionImage(app(h, n))(x)) + ) + have((inductionFormulaN, isInIntroductionFunctionImage(app(h, n))(x)) |- isConstructorXHN) by Restate + have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n))), inductionFormulaN) |- isConstructorXHN) by Cut(heightSuccessorWeakForward, lastStep) + val right = have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n))), inductionFormulaN) |- isConstructorXHSuccN) by Cut(lastStep, liftConstructorHeight) + val left = have(isConstructorXHSuccN |- isConstructorXHSuccN) by Hypothesis + have((hIsTheHeightFunction, in(n, N), inductionFormulaN, isInIntroductionFunctionImage(app(h, successor(n)))(x)) |- isConstructorXHSuccN) by LeftOr(left, right) + + // STEP 1.6: Conclude + thenHave((hIsTheHeightFunction, in(n, N), inductionFormulaN) |- inductionFormulaSuccN) by RightImplies + thenHave((hIsTheHeightFunction, in(n, N)) |- inductionFormulaN ==> inductionFormulaSuccN) by RightImplies + thenHave(hIsTheHeightFunction |- in(n, N) ==> (inductionFormulaN ==> inductionFormulaSuccN)) by RightImplies + thenHave(hIsTheHeightFunction |- forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormulaSuccN))) by RightForall + have(hIsTheHeightFunction |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut(lastStep, inductiveCaseRemaining) + thenHave(hIsTheHeightFunction |- in(n, N) ==> inductionFormulaN) by InstantiateForall(n) + } + + // STEP 2: Prove the backward implication + val backward = have((hIsTheHeightFunction, in(n, N)) |- isConstructor(x, app(h, n)) ==> isInIntroductionFunctionImage(app(h, n))(x)) by Restate + + // STEP 3: Conclude + have((hIsTheHeightFunction, in(n, N)) |- isInIntroductionFunctionImage(app(h, n))(x) <=> isConstructor(x, app(h, n))) by RightIff(forward, backward) + have(thesis) by Apply(ADTThm.equivalenceRewriting).on(lastStep, heightSuccessorWeak.asInstanceOf) + } + + /** + * Generates the structural inductive case for a given constructor. + * + * @param c the constructor + */ + lazy val inductiveCase: Map[SyntacticConstructor, Formula] = constructors + .map(c => + c -> c.signature.foldRight[Formula](P(c.term))((el, fc) => + val (v, ty) = el + ty match + case Self => forall(v, in(v, term) ==> (P(v) ==> fc)) + case GroundType(t) => forall(v, in(v, t) ==> fc) + ) + ) + .toMap + + /** + * Lemma --- Structural induction principle for this ADT. + * + * `base cases => inductive cases => ∀x ∈ ADT. P(x)` + */ + lazy val induction = Lemma(constructors.foldRight[Formula](forall(x, in(x, term) ==> P(x)))((c, f) => inductiveCase(c) ==> f)) { + + // List of cases to prove for structural induction to hold + val structuralInductionPreconditions: Formula = /\(constructors.map(inductiveCase)) + + // We want to prove the claim by induction on the height of n, i.e. prove that for any + // n in N, P holds. + def inductionFormula(n: Term): Formula = forall(x, in(x, app(h, n)) ==> P(x)) + val inductionFormulaN: Formula = inductionFormula(n) + + // STEP 1: Prove the base case + have(hIsTheHeightFunction |- in(x, app(h, emptySet)) ==> P(x)) by Weakening(heightZero) + val zeroCase = thenHave(hIsTheHeightFunction |- inductionFormula(emptySet)) by RightForall + + val inductiveCaseRemaining = have((hIsTheHeightFunction, forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormula(successor(n))))) |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut( + zeroCase, + ADTThm.natInduction of (P := lambda(n, inductionFormulaN)) + ) + + // STEP 2: Prove the inductive case + val succCase = have((hIsTheHeightFunction, structuralInductionPreconditions) |- forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormula(successor(n))))) subproof { + + // STEP 2.1 : Prove that if the x = c(x1, ..., xn) for some c and xi, ..., xj ∈ height(n) then P(x) holds. + val isConstructorImpliesP = have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(x, app(h, n))) |- P(x)) subproof { + + if constructors.isEmpty then have(thesis) by Restate + else + val orSeq = + (for c <- constructors yield + + // Caching + val constructorPrecondition = inductiveCase(c) + val constructorVarsInHN = constructorVarsInDomain(c, app(h, n)) + val constructorVarsInHNEx = ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) + val constructorVarsInTerm = constructorVarsInDomain(c, term) + + // STEP 2.1.1: Prove that if xi, ..., xj ∈ height(n) then xi, ..., xj ∈ ADT. + val constructorQuantVarsInHNToTerm = have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInTerm) subproof { + have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- in(n, N) /\ constructorVarsInHN) by Restate + val consVarL = thenHave((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInHNEx) by RightExists + have((constructorVarsInTerm <=> constructorVarsInHNEx, constructorVarsInHNEx) |- constructorVarsInTerm) by Restate.from( + ADTThm.equivalenceRevApply of (p1 := constructorVarsInTerm, p2 := constructorVarsInHNEx) + ) + have((hIsTheHeightFunction, constructorVarsInHNEx) |- constructorVarsInTerm) by Cut( + termsHaveHeight(c), + lastStep + ) + have(thesis) by Cut(consVarL, lastStep) + } + + + // STEP 2.1.2: Prove that if xi, ..., xj ∈ height(n) then P(c(x1, ..., xn)). + have((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- constructorPrecondition) by Restate + + c.signature + .foldLeft(lastStep)((fact, el) => + val (v, ty) = el + + fact.statement.right.head match + case Forall(_, factCclWithoutForall) => + thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- factCclWithoutForall) by InstantiateForall(v) + + factCclWithoutForall match + case Implies(membership, subformula) => + ty match + case Self => + subformula match + case Implies(hypothesis, subSubFormula) => + val proofSubSubFormula = thenHave( + (hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInTerm, constructorVarsInHN, P(v)) |- subSubFormula + ) by Weakening + + have(inductionFormulaN |- inductionFormulaN) by Hypothesis + thenHave(inductionFormulaN |- in(v, app(h, n)) ==> P(v)) by InstantiateForall(v) + thenHave((inductionFormulaN, constructorVarsInHN) |- P(v)) by Weakening + + have((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInTerm, constructorVarsInHN) |- subSubFormula) by Cut( + lastStep, + proofSubSubFormula + ) + have((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- subSubFormula) by Cut( + constructorQuantVarsInHNToTerm, + lastStep + ) + + case _ => throw UnreachableException + + case GroundType(t) => + thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- subformula) by Restate + case _ => throw UnreachableException + case _ => throw UnreachableException + ) + + thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- P(c.term)) by Restate + + // STEP 2.1.3: Prove that if xi, ..., xj ∈ height(n) then P(x). + thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN, x === c.term) |- P(x)) by RightSubstEq + .withParametersSimple(List((x, c.term)), lambda(x, P(x))) + + thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN /\ (x === c.term)) |- P(x)) by LeftAnd + + thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, isConstructor(c, x, app(h, n))) |- P(x)) by QuantifiersIntro(c.variables) + thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(c, x, app(h, n))) |- P(x)) by Weakening + ).toSeq + + + have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(x, app(h, n))) |- P(x)) by LeftOr(orSeq*) + } + + // STEP 2.2: Prove that if x ∈ height(n + 1) then P(x) holds. + have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n)))) |- isConstructor(x, app(h, n))) by Cut( + heightSuccessorStrong, + ADTThm.equivalenceApply of (p1 := in(x, app(h, successor(n))), p2 := isConstructor(x, app(h, n))) + ) + have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, in(x, app(h, successor(n)))) |- P(x)) by Cut(lastStep, isConstructorImpliesP) + + // STEP 2.3: Conclude + thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN) |- in(x, app(h, successor(n))) ==> P(x)) by RightImplies + + thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN) |- inductionFormula(successor(n))) by RightForall + thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N)) |- inductionFormulaN ==> inductionFormula(successor(n))) by RightImplies + thenHave((hIsTheHeightFunction, structuralInductionPreconditions) |- in(n, N) ==> (inductionFormulaN ==> inductionFormula(successor(n)))) by RightImplies + thenHave(thesis) by RightForall + } + + // STEP 3: Conclude + + have((hIsTheHeightFunction, structuralInductionPreconditions) |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut(lastStep, inductiveCaseRemaining) + thenHave((hIsTheHeightFunction, structuralInductionPreconditions) |- in(n, N) ==> inductionFormulaN) by InstantiateForall(n) + thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N)) |- inductionFormulaN) by Restate + thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N)) |- in(x, app(h, n)) ==> P(x)) by InstantiateForall(x) + thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N) /\ in(x, app(h, n))) |- P(x)) by Restate + val exImpliesP = thenHave((hIsTheHeightFunction, structuralInductionPreconditions, exists(n, in(n, N) /\ in(x, app(h, n)))) |- P(x)) by LeftExists + have((hIsTheHeightFunction, in(x, term)) |- exists(n, in(n, N) /\ in(x, app(h, n)))) by Cut(termHasHeight, ADTThm.equivalenceApply of (p1 := in(x, term), p2 := exists(n, in(n, N) /\ in(x, app(h, n))))) + + have((hIsTheHeightFunction, structuralInductionPreconditions, in(x, term)) |- P(x)) by Cut(lastStep, exImpliesP) + thenHave((exists(h, hIsTheHeightFunction), structuralInductionPreconditions, in(x, term)) |- P(x)) by LeftExists + have((structuralInductionPreconditions, in(x, term)) |- P(x)) by Cut(heightFunctionExistence, lastStep) + thenHave(structuralInductionPreconditions |- in(x, term) ==> P(x)) by RightImplies + thenHave(structuralInductionPreconditions |- forall(x, in(x, term) ==> P(x))) by RightForall + } + +} + +/** + * Semantic set theoretical interpretation of a constructor for an algebraic data type. + * That is a function from the arguments' domains to the set of instances of the algebraic data type. + * + * `c : T1 -> ... -> Tn -> ADT` + * + * Since polymorphism is supported, this function is parametrized by the type variables appearing inside + * the specification of the ADT. In this sense, a constructor is a class function whose parameters are + * type variables and whose body is the set theoretic function detailed above. With polymorphism, the signature + * thus becomes: + * + * `c(X1, ..., Xn) : T1(X1, ..., Xn) -> ... -> Tn(X1, ..., Xn) -> ADT(X1, ..., Xn)` + * + * Injectivity and introduction rule are proven within this class. + * + * @constructor generates a class function for this constructor + * @param line the line at which this constructor is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param file the file in which this constructor is defined. Usually fetched automatically by the compiler. + * Used for error reporting + * @param name the name of this constructor + * @param underlying the syntactic constructor + * @param adt the algebraic data type to which this constructor belongs + */ +private class SemanticConstructor[N <: Arity](using line: sourcecode.Line, file: sourcecode.File)( + val name: String, + val underlying: SyntacticConstructor, + val adt: SyntacticADT[N], +) { + /** + * Full name of this constructor, i.e. concatenation of the ADT name and this constructor name. + */ + val fullName: String = s"${adt.name}/${name}" + + /** + * Type variables that may appear in the signature of this constructor. + */ + val typeVariables: Variable ** N = adt.typeVariables + + /** + * Sequence of type variables that may appear in the signature of this constructor. + */ + val typeVariablesSeq: Seq[Variable] = adt.typeVariablesSeq + + /** + * Number of type variables in the signature of this constructor. + */ + val typeArity: N = adt.typeArity + + /** + * Variables used for constructor arguments. + */ + val variables: Seq[Variable] = underlying.variables + + /** + * Variables used for constructor arguments. + */ + val variables1: Seq[Variable] = underlying.variables1 + + /** + * Alternative set of variables used for constructor arguments. + */ + val variables2: Seq[Variable] = underlying.variables2 + + /** + * Set of variables for this constructor with their respective domain or a + * special symbol in case the domain is the ADT. + * + * @param vars variables + */ + def syntacticSignature(vars: Seq[Variable]): Seq[(Variable, ConstructorArgument)] = + vars.zip(underlying.specification) + + /** + * Variables of this constructor with their respective domain or a special symbol in case the domain is the ADT. + */ + val syntacticSignature: Seq[(Variable, ConstructorArgument)] = underlying.signature + + /** + * Constructor arguments with their respective domains. + * + * @param vars this constructor arguments + */ + def semanticSignature(vars: Seq[Variable]): Seq[(Variable, Term)] = vars.zip(underlying.specification.map(_.getOrElse(adt.term))) + + /** + * Variables of this constructor with their respective domains. + */ + val semanticSignature: Seq[(Variable, Term)] = semanticSignature(variables) + + /** + * Variables of this constructor with their respective domains. + */ + val semanticSignature1: Seq[(Variable, Term)] = semanticSignature + + /** + * Alternative set of variables of this constructor with their respective domain. + */ + val semanticSignature2: Seq[(Variable, Term)] = semanticSignature(variables2) + + /** + * Type of this constructor. + */ + val typ: Term = semanticSignature.unzip._2.foldRight[Term](adt.term)((a, b) => a |=> b) + + /** + * Arity of this constructor. + */ + val arity: Int = variables.size + + /** + * Internal representation of this constructor (i.e. as a tuple). + */ + val structuralTerm: Term = underlying.term + /** + * Internal representation of this constructor (i.e. as a tuple). + */ + val structuralTerm1: Term = underlying.term1 + /** + * Internal representation of this constructor (i.e. as a tuple) with an alternative set of variables. + */ + val structuralTerm2: Term = underlying.term2 + + /** + * Definition of this constructor. + * + * Formally it is the only function whose codomain is the ADT such that for all variables x1 :: S1, ...,xn :: Sn + * c * x1 * ... * xn = (tagc, (x1, (..., (xn, ∅)...)) + */ + private val untypedDefinition = (c :: typ) /\ forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appSeq(c)(variables) === structuralTerm)) + + /** + * Lemma --- Uniqueness of this constructor. + * + * ` ∃!c. c ∈ T1 -> ... -> Tn -> ADT /\ ∀x1, ..., xn. c * x1 * ...* xn = (tagc, (x1, (..., (xn, ∅)...))` + */ + private val uniqueness = Axiom(existsOne(c, untypedDefinition)) + + /** + * Class function representing this constructor + */ + private val classFunction = FunctionDefinition[N](fullName, line.value, file.value)(typeVariablesSeq, c, untypedDefinition, uniqueness).label + + /** + * Identifier of this constructor. + */ + val id: Identifier = classFunction.id + + /** + * This constructor in which type variables are instantiated. + * + * @param args the instances of this constructor's type variables + */ + def term(args: Seq[Term]): Term = classFunction.applySeq(args) + + /** + * Constructor where type variables are instantiated with schematic variables. + */ + private val term: Term = term(typeVariablesSeq) + + /** + * Constructor where type variables are instantiated with schematic variables and arguments instantiated. + * + * @param args the instances of this constructor arguments + */ + def appliedTerm(args: Seq[Term]): Term = appSeq(term)(args) + + /** + * Constructor where type variables and arguments are instantiated with schematic variables. + */ + val appliedTerm: Term = appliedTerm(variables) + + /** + * Constructor where type variables and arguments are instantiated with schematic variables. + */ + val appliedTerm1: Term = appliedTerm + + /** + * Constructor where type variables and arguments are instantiated with schematic variables. + * Arguments variables are however drawn from an alternative set of variables. + */ + val appliedTerm2: Term = appliedTerm(variables2) + + /** + * Lemma --- This constructor is equal to its internal representation. + * + * `∀x1, ..., xn. c * x1 * ... * xn = (tagc, (x1, (..., (xn, ∅)...))` + */ + val shortDefinition = Lemma(forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appliedTerm === structuralTerm))) { + have(forall(c, (term === c) <=> untypedDefinition)) by Exact(classFunction.definition) + thenHave((term === term) <=> ((term :: typ) /\ forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appliedTerm === structuralTerm)))) by InstantiateForall(term) + thenHave(thesis) by Weakening + } + + /** + * Lemma --- Introduction rule for this constructor. + * + * `∀A1, ..., Am. c(X1, ..., Xm) ∈ T1(X1, ..., Xm) -> ... -> Tn(X1, ..., Xm) -> ADT(X1, ..., Xm)` + * + * where Ai are the type variables of the ADT and Ti are domains of this constructor arguments. + * + * e.g. `∀T. nil(T) ∈ list(T)` and `∀T. cons(T) ∈ T -> list(T) -> list(T)` + */ + val intro = Lemma(forallSeq(typeVariablesSeq, term :: typ)) { + have(forall(c, (term === c) <=> untypedDefinition)) by Exact(classFunction.definition) + thenHave((term === term) <=> ((term :: typ) /\ forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appliedTerm === structuralTerm)))) by InstantiateForall(term) + thenHave(term :: typ) by Weakening + thenHave(thesis) by QuantifiersIntro(typeVariablesSeq) + } + + /** + * Theorem --- Injectivity of constructors. + * + * Two instances of this constructor are equal if and only if all of their arguments are pairwise equal + * + * e.g. Cons(head1, tail1) === Cons(head2, tail2) <=> head1 === head2 /\ tail1 === tail2 + */ + lazy val injectivity = + val vars1WellTyped: Set[Formula] = wellTypedSet(semanticSignature1) + val vars2WellTyped: Set[Formula] = wellTypedSet(semanticSignature2) + + if arity == 0 then + Lemma(appliedTerm1 === appliedTerm2) { + have(thesis) by RightRefl + } + else + Lemma(vars1WellTyped ++ vars2WellTyped |- simplify((appliedTerm1 === appliedTerm2) <=> (variables1 === variables2))) { + + have(forallSeq(variables1, wellTypedFormula(semanticSignature1) ==> (appliedTerm1 === structuralTerm1))) by Restate.from(shortDefinition) + + variables1.foldLeft(lastStep)((fact, v) => + fact.statement.right.head match + case Forall(_, phi) => thenHave(phi) by InstantiateForall(v) + case _ => throw UnreachableException + ) + val tappTerm1Def = thenHave(vars1WellTyped |- appliedTerm1 === structuralTerm1) by Restate + + // println(forallSeq(variables1, wellTypedFormula(semanticSignature1) ==> (appliedTerm1 === structuralTerm1))) + // println(forallSeq(variables2, wellTypedFormula(semanticSignature2) ==> (appliedTerm2 === structuralTerm))) + have(forallSeq(variables2, wellTypedFormula(semanticSignature2) ==> (appliedTerm2 === structuralTerm2))) by Restate.from(shortDefinition) + + variables2.foldLeft(lastStep)((fact, v) => + fact.statement.right.head match + case Forall(_, phi) => thenHave(phi) by InstantiateForall(v) + case _ => throw UnreachableException + ) + val tappTerm2Def = thenHave(vars2WellTyped |- appliedTerm2 === structuralTerm2) by Restate + + + val s0 = have(vars2WellTyped + (appliedTerm1 === appliedTerm2) |- appliedTerm1 === structuralTerm2) by Cut(tappTerm2Def, + ADTThm.altEqualityTransitivity of (x := appliedTerm1, y := appliedTerm2, z := structuralTerm2)) + have(vars1WellTyped + (appliedTerm1 === structuralTerm2) |- structuralTerm1 === structuralTerm2) by Cut(tappTerm1Def, + ADTThm.altEqualityTransitivity of (x := structuralTerm1, y := appliedTerm1, z := structuralTerm2)) + have((vars1WellTyped ++ vars2WellTyped) + (appliedTerm1 === appliedTerm2) |- structuralTerm1 === structuralTerm2) by Cut(s0, lastStep) + val forward = thenHave(vars1WellTyped ++ vars2WellTyped |- (appliedTerm1 === appliedTerm2) ==> (structuralTerm1 === structuralTerm2)) by RightImplies + + val s1 = have(vars1WellTyped + (structuralTerm1 === structuralTerm2) |- appliedTerm1 === structuralTerm2) by Cut(tappTerm1Def, + ADTThm.altEqualityTransitivity of (x := appliedTerm1, y := structuralTerm1, z := structuralTerm2)) + have(vars2WellTyped + (appliedTerm1 === structuralTerm2) |- appliedTerm1 === appliedTerm2) by Cut(tappTerm2Def, + ADTThm.altEqualityTransitivity of (x := appliedTerm1, y := structuralTerm2, z := appliedTerm2)) + have((vars1WellTyped ++ vars2WellTyped) + (structuralTerm1 === structuralTerm2) |- appliedTerm1 === appliedTerm2) by Cut(s1, lastStep) + val backward = thenHave(vars1WellTyped ++ vars2WellTyped |- (structuralTerm1 === structuralTerm2) ==> (appliedTerm1 === appliedTerm2)) by RightImplies + + val definitionUnfolding = have(vars1WellTyped ++ vars2WellTyped |- (appliedTerm1 === appliedTerm2) <=> (structuralTerm1 === structuralTerm2)) by RightIff(forward, backward) + have((appliedTerm1 === appliedTerm2) <=> (structuralTerm1 === structuralTerm2) |- (appliedTerm1 === appliedTerm2) <=> /\(variables1.zip(variables2).map(_ === _))) by Sorry + Cut( + underlying.injectivity, + ADTThm.equivalenceRewriting of (p1 := (appliedTerm1 === appliedTerm2), p2 := (structuralTerm1 === structuralTerm2), p3 := /\(variables1.zip(variables2).map(_ === _))) + ) + have(thesis) by Cut(definitionUnfolding, lastStep) + } + + + /** + * Case generated by this constructor when performing a proof by induction + */ + lazy val inductiveCase: Formula = + syntacticSignature.foldRight[Formula](P(appliedTerm1)) + ((el, fc) => + val (v, typ) = el + typ match + case Self => forall(v, v :: adt.term ==> (P(v) ==> fc)) + case GroundType(t) => forall(v, v :: t ==> fc) + ) +} + +/** + * Semantic set theoretical interpretation of an algebraic data type. That is the least set closed under [[SemanticConstructor]]. + * + * E.g. list is the smallest set containing nil and closed under the cons function. + * + * Injectivity between different constructors, structural induction and elimination rule are proved within this class. + * + * @constructor generates a semantic interpretation for this ADT out of a syntactic one + * @param underlying the syntactic representation of this ADT + * @param constructors constructors of this ADT + */ + private class SemanticADT[N <: Arity]( + val underlying: SyntacticADT[N], + val constructors: Seq[SemanticConstructor[N]] + ) { + + /** + * Name of this ADT. + */ + val name: String = underlying.name + + /** + * Identifier of this ADT. + */ + val id: Identifier = underlying.polymorphicTerm.id + + /** + * Type variables of this ADT. + */ + val typeVariables: Variable ** N = underlying.typeVariables + + /** + * Sequence of type variables of this ADT. + */ + val typeVariablesSeq: Seq[Variable] = underlying.typeVariablesSeq + + /** + * Number of type variables in this ADT. + */ + val typeArity: N = underlying.typeArity + + /** + * Term representing this ADT where type variables are instantiated with given arguments. + * + * @param args the instances of this ADT type variables + */ + def term(args: Seq[Term]) = underlying.polymorphicTerm.applySeq(args) + + /** + * Term representing this ADT where type variables are instantiated with schematic variables. + */ + val term: Term = underlying.term + + /** + * Theorem --- Injectivity of constructors. + * + * Two instances of different construcors are always different. + * + * e.g. Nil != Cons(head, tail) + */ + def injectivity(c1: SemanticConstructor[N], c2: SemanticConstructor[N]) = + + val vars1WellTyped: Set[Formula] = wellTypedSet(c1.semanticSignature1) + val vars2WellTyped: Set[Formula] = wellTypedSet(c2.semanticSignature2) + + Lemma(vars1WellTyped ++ vars2WellTyped |- !(c1.appliedTerm1 === c2.appliedTerm2)) { + + val defUnfolding = have((vars1WellTyped ++ vars2WellTyped) + (c1.appliedTerm1 === c2.appliedTerm2) |- c1.structuralTerm1 === c2.structuralTerm2) subproof { + have(forallSeq(c1.variables1, wellTypedFormula(c1.semanticSignature1) ==> (c1.appliedTerm1 === c1.structuralTerm1))) by Restate.from(c1.shortDefinition) + + c1.variables1.foldLeft(lastStep)((fact, v) => + fact.statement.right.head match + case Forall(_, phi) => thenHave(phi.substitute(v := x)) by InstantiateForall(x) + case _ => throw UnreachableException + ) + val tappTerm1Def = thenHave(vars1WellTyped |- c1.structuralTerm1 === c1.appliedTerm1) by Restate + + have(forallSeq(c2.variables2, wellTypedFormula(c2.semanticSignature2) ==> (c2.appliedTerm2 === c2.structuralTerm2))) by Restate.from(c2.shortDefinition) + + c2.variables2.foldLeft(lastStep)((fact, v) => + fact.statement.right.head match + case Forall(_, phi) => thenHave(phi) by InstantiateForall(v) + case _ => throw UnreachableException + ) + val tappTerm2Def = thenHave(vars2WellTyped |- c2.appliedTerm2 === c2.structuralTerm2) by Restate + + val s0 = have(vars2WellTyped + (c1.appliedTerm1 === c2.appliedTerm2) |- c1.appliedTerm1 === c2.structuralTerm2) by Cut( + tappTerm2Def, + ADTThm.altEqualityTransitivity of (x := c1.appliedTerm1, y := c2.appliedTerm2, z := c2.structuralTerm2) + ) + have(vars1WellTyped + (c1.appliedTerm1 === c2.structuralTerm2) |- c1.structuralTerm1 === c2.structuralTerm2) by Cut( + tappTerm1Def, + ADTThm.altEqualityTransitivity of (x := c1.structuralTerm1, y := c1.appliedTerm1, z := c2.structuralTerm2) + ) + have(thesis) by Cut(s0, lastStep) + } + + have(!(c1.structuralTerm1 === c2.structuralTerm2)) by Restate.from(underlying.injectivity(c1.underlying, c2.underlying)) + thenHave(c1.structuralTerm1 === c2.structuralTerm2 |- ()) by Restate + + have((vars1WellTyped ++ vars2WellTyped) + (c1.appliedTerm1 === c2.appliedTerm2) |- ()) by Cut(defUnfolding, lastStep) + } + + /** + * Theorem --- Structural induction principle for this ADT. + * + * `base cases => inductive cases => ∀x ∈ ADT. P(x)` + */ + lazy val induction = Lemma(constructors.foldRight[Formula](forall(x, x :: term ==> P(x)))((c, f) => c.inductiveCase ==> f)) { sp ?=> + constructors.foldRight[(Formula, Formula, sp.Fact)] { + val prop = forall(x, x :: term ==> P(x)) + (prop, prop, have(prop <=> prop) by Restate) + }((c, acc) => + val (oldBefore, oldAfter, fact) = acc + val newBefore = underlying.inductiveCase(c.underlying) ==> oldBefore + val newAfter = c.inductiveCase ==> oldAfter + + have(underlying.inductiveCase(c.underlying) <=> c.inductiveCase) subproof { + val wellTypedVars: Seq[Formula] = wellTyped(c.semanticSignature) + val wellTypedVarsSet = wellTypedVars.toSet + + + have(forallSeq(c.variables, wellTypedFormula(c.semanticSignature) ==> (c.appliedTerm === c.structuralTerm))) by Restate.from(c.shortDefinition) + if c.arity > 0 then + c.variables1.foldLeft(lastStep)((l, _) => + lastStep.statement.right.head match + case Forall(v, phi) => thenHave(phi) by InstantiateForall(v) + case _ => throw UnreachableException + ) + + val eq = thenHave(wellTypedVarsSet |- c.appliedTerm === c.structuralTerm) by Restate + have(P(c.appliedTerm) <=> P(c.appliedTerm)) by Restate + thenHave(c.structuralTerm === c.appliedTerm |- P(c.structuralTerm) <=> P(c.appliedTerm)) by RightSubstEq.withParametersSimple( + List((c.structuralTerm, c.appliedTerm)), + lambda(s, P(c.structuralTerm) <=> P(s)) + ) + have(wellTypedVarsSet |- P(c.structuralTerm) <=> P(c.appliedTerm)) by Cut(eq, lastStep) + + c.syntacticSignature + .foldRight[(Formula, Formula, Seq[Formula])]((P(c.structuralTerm), P(c.appliedTerm), wellTypedVars))((el, fc) => + val (v, ty) = el + val (fc1, fc2, wellTypedVars) = fc + ty match + case Self => + val wellTypedV: Formula = v :: term + have(wellTypedVars |- (P(v) ==> fc1) <=> (P(v) ==> fc2)) by Cut(lastStep, ADTThm.leftImpliesEquivalenceWeak of (p := P(v), p1 := fc1, p2 := fc2)) + thenHave(wellTypedVars.init |- wellTypedV ==> ((P(v) ==> fc1) <=> (P(v) ==> fc2))) by RightImplies + have(wellTypedVars.init |- (wellTypedV ==> (P(v) ==> fc1)) <=> (wellTypedV ==> (P(v) ==> fc2))) by Cut( + lastStep, + ADTThm.leftImpliesEquivalenceStrong of (p := wellTypedV, p1 := P(v) ==> fc1, p2 := P(v) ==> fc2) + ) + thenHave(wellTypedVars.init |- forall(v, (wellTypedV ==> (P(v) ==> fc1)) <=> (wellTypedV ==> (P(v) ==> fc2)))) by RightForall + have(wellTypedVars.init |- forall(v, (wellTypedV ==> (P(v) ==> fc1))) <=> forall(v, (wellTypedV ==> (P(v) ==> fc2)))) by Cut( + lastStep, + universalEquivalenceDistribution of (P := lambda(v, wellTypedV ==> (P(v) ==> fc1)), Q := lambda(v, wellTypedV ==> (P(v) ==> fc2))) + ) + (forall(v, wellTypedV ==> (P(v) ==> fc1)), forall(v, wellTypedV ==> (P(v) ==> fc2)), wellTypedVars.init) + case GroundType(t) => + thenHave(wellTypedVars.init |- v :: t ==> (fc1 <=> fc2)) by RightImplies + have(wellTypedVars.init |- (in(v, t) ==> fc1) <=> (v :: t ==> fc2)) by Cut(lastStep, ADTThm.leftImpliesEquivalenceStrong of (p := in(v, t), p1 := fc1, p2 := fc2)) + thenHave(wellTypedVars.init |- forall(v, (in(v, t) ==> fc1) <=> (v :: t ==> fc2))) by RightForall + have(wellTypedVars.init |- forall(v, (in(v, t) ==> fc1)) <=> forall(v, (v :: t ==> fc2))) by Cut( + lastStep, + universalEquivalenceDistribution of (P := lambda(v, in(v, t) ==> fc1), Q := lambda(v, v :: t ==> fc2)) + ) + (forall(v, (in(v, t) ==> fc1)), forall(v, (v :: t ==> fc2)), wellTypedVars.init) + ) + } + (newBefore, newAfter, have(newBefore <=> newAfter) by Apply(ADTThm.impliesEquivalence).on(lastStep, fact)) + ) + have(underlying.induction.statement.right.head |- thesis.right.head) by Cut( + lastStep, + ADTThm.equivalenceApply of ( + p1 := underlying.induction.statement.right.head, p2 := thesis.right.head + ) + ) + have(thesis) by Cut(underlying.induction, lastStep) + } + + /** + * Returns a map binding each constructor to formula describing whether x is an instance of it. + */ + private lazy val isConstructorMap: Map[SemanticConstructor[N], Formula] = + constructors.map(c => c -> existsSeq(c.variables, wellTypedFormula(c.semanticSignature) /\ (x === c.appliedTerm))).toMap + + /** + * Returns a formula describing whether x is an instance of one of this ADT's constructors. + */ + private lazy val isConstructor = + \/(constructors.map(c => isConstructorMap(c))) + + /** + * Theorem --- Pattern matching principle (also known as elimination rule) for this ADT. + * + * `x ∈ ADT |- x = c * x1 * ... * xn for some constructor c and xi, ..., xj ∈ ADT` + */ + lazy val elim = Lemma(x :: term |- simplify(isConstructor)) { + + // Induction preconditions with P(z) = z != x + val inductionPreconditionIneq = constructors.map(c => c -> c.inductiveCase.substitute((P -> lambda(z, !(x === z))))).toMap + val inductionPreconditionsIneq = /\(inductionPreconditionIneq.map(_._2)) + + // Weakening of the negation of the induction preconditions + val weakNegInductionPreconditionIneq: Map[SemanticConstructor[N], Formula] = constructors + .map(c => + c -> + c.semanticSignature + .foldRight[Formula](x === c.appliedTerm)((el, fc) => + val (v, t) = el + exists(v, (v :: t) /\ fc) + ) + ) + .toMap + + // STEP 1: Prove that if the induction preconditions with P(z) = z != x do not hold then x is the instance of some constructor + val strengtheningOfInductionPreconditions = have(!inductionPreconditionsIneq |- isConstructor) subproof { + if constructors.isEmpty then have(thesis) by Restate + else + + // STEP 1.1: Prove the claim for each constructor + val negInductionPreconditionsOrSequence = + for c <- constructors yield + + // STEP 1.1.1: Prove the strengthening of the negations of the induction preconditions + val conditionStrenghtening = have(!inductionPreconditionIneq(c) |- weakNegInductionPreconditionIneq(c)) subproof { + have(x === c.appliedTerm |- x === c.appliedTerm) by Hypothesis + + c.syntacticSignature + .foldRight(lastStep)((el, fact) => + val (v, ty) = el + val left = fact.statement.left.head + val right = fact.statement.right.head + + ty match + case Self => + thenHave(!(x === v) /\ left |- right) by Weakening + case _ => () + + val weakr = thenHave(in(v, ty.getOrElse(term)) /\ left |- right) by Weakening + val weakl = have(in(v, ty.getOrElse(term)) /\ left |- in(v, ty.getOrElse(term))) by Restate + + have((v :: ty.getOrElse(term)) /\ left |- (v :: ty.getOrElse(term)) /\ right) by RightAnd(weakl, weakr) + thenHave((v :: ty.getOrElse(term)) /\ left |- exists(v, (v :: ty.getOrElse(term)) /\ right)) by RightExists + thenHave(exists(v, (v :: ty.getOrElse(term)) /\ left) |- exists(v, (v :: ty.getOrElse(term)) /\ right)) by LeftExists + ) + + } + + // STEP 1.1.2: Conclude + // TODO: Change to a more efficient way of proving this + have(weakNegInductionPreconditionIneq(c) |- isConstructorMap(c)) by Tableau + have(!inductionPreconditionIneq(c) |- isConstructorMap(c)) by Cut(conditionStrenghtening, lastStep) + thenHave(!inductionPreconditionIneq(c) |- isConstructor) by Weakening + + have(thesis) by LeftOr(negInductionPreconditionsOrSequence*) + } + + // STEP 2: Conclude + have(inductionPreconditionsIneq |- forall(z, z :: term ==> !(x === z))) by Restate.from(induction of (P := lambda(z, !(x === z)))) + thenHave(inductionPreconditionsIneq |- x :: term ==> !(x === x)) by InstantiateForall(x) + val ind = thenHave(x :: term |- !inductionPreconditionsIneq) by Restate + have(x :: term |- isConstructor) by Cut(lastStep, strengtheningOfInductionPreconditions) + } + } diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala new file mode 100644 index 000000000..7af2af24c --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala @@ -0,0 +1,200 @@ +package lisa.maths.settheory.types + +/** + * Provides definitional mechanisms for Algebraic Data Types (ADTs) and tactics for reasoning about them. + * + * ===Usage=== + * + * In order to use ADTs, you need to import + * {{{ + * lisa.maths.settheory.types.adt.{*, given} + * }}} + * + * ====Defining an ADT==== + * + * ADTs can be defined using the following syntax: + * {{{ + * val define(adtName: ADT[N], constructors(c1, ..., cn)) = (X1, .., Xk) --> (T1, ..., Tn) | ... | (S1, ..., Sm) + * }}} + * + * where: + * - `adtName` is the identifier of the ADT + * - `N` is the number of poylmorphic variables + * - `c1 ... cn` are the identifiers of the different constructors of the ADT, + * - `X1, ..., Xk` are the polymorphic variables + * - `T1, ..., Tn, S1, ..., Sm` are the types defining the signature of each constructor. + * Note that `adtName` and `c1 ... cn` are scala identifiers, respectively referring to the ADT and the constructors. + * In addition, you cannot define two ADT with the same name, even in different scopes, as they introduce a new global definition. + * Type variables must be declarated as variables before being used. + * Here are some examples of ADT definitions: + * {{{ + * + * //variables declaration + * val A = variable + * + * // Definition of booleans with two constructors tru and fals. Each of them take no argument so their signature is () + * val define(bool: ADT, constructors(tru, fals)) = () | () + * + * // Definition of options with two constructors none and some. Options are polymorphic over a type variable A. + * // Their second constructor, some, has signature A has it takes only a value of type A. + * val define(option: ADT, constructors(none, some)) = A -> () | A + * + * // Recursive definition of lists with two constructors nil and cons. Lists are polymorphic over a type variable A. + * // The constructor cons takes two arguments of respectively type A and list(A) and append an element to + * // a sublist. + * val define(list: ADT, constructors(nil, cons)) = A -> () | (A, list) + * + * //Definition of natural numbers. + * val define(nat: ADT, constructors(zero, succ)) = () | nat + * }}} + * + * ====Using an ADT and its theorem==== + * + * Each constructor of an ADT comes with an introduction rule and an injectivity theorem. + * The introduction rule gives the type of the constructor and is of the form + * + * `∀ X1, ..., Xk. c(X1, ..., Xk) :: T1 -> ... Tn -> ADT(X1, ..., Xk)` + * + * The injection theorem stipulates that two instances of the same constructor are equal if and only if all their arguments are equal. + * + * For examples for lists we have + * + * nil introduction: `∀ A. nil(A) :: list(A)` + * + * cons introduction: `∀ A. cons(A) :: A -> list(A) -> list(A)` + * + * nil injectivity: `nil(A) = nil(A)` + * + * cons injectivity: `cons(A) * x0 * x1 = cons(A) * y0 * y1 <=> x0 = x1 /\ y0 = y1` + * + * Each ADT comes with an structural induction schema, an elimination rule (or pattern matching principle) and an injectivity theorem. + * + * Structural induction states that if a statement is true for each constructor assuming that the proposition is true for the constructor's + * arguments, then it is true for every element of the ADT. + * + * e.g. list induction: ` P(nil(A)) => (∀ x0 :: A. ∀ x1 :: list(A). P(x1) => P(cons * x0 * x1)) => ∀ x :: list(A). P(x)` + * + * Elimination rule is a direct consequence of induction and states that each instance of an ADT is an instance of one of its constructors + * + * e.g. list elimination: ` x :: list(A) => x = nil(A) \/ ∃ x0, x1. x = cons(A) * x0 * x1` + * + * Finally injectivity tells us that instances of different constructors are different + * + * e.g. nil/cons injectivity: `nil(A) != cons(A) * x0 * x1` + * + * ====Type Checking==== + * + * We provide a tactic to automatically type check instances of algebraic data types. + * It can be called using `TypeChecker.prove`. For examples instances of lists can be + * typechecked as follow. + * + * {{{ + * have(nil(A) :: list(A)) by TypeChecker.prove + * have((x :: A, l :: list(A)) |- cons * x * l :: list(A)) by TypeChecker.prove + * }}} + * + * ====Induction Tactic==== + * + * We provide a tactic for performing proofs by induction. + * The tactic can prove goals of the form + * + * ` ∀ x :: adt. P(x)` + * + * To work the user needs to provide a proof of P(c * x0 * ... * xn) for each constructor. + * The syntax of the tactic is of the following form: + * {{{ + * have(∀ x :: adt. P(x)) by Induction(adt) { + * Case(c1, v1, ..., vn) { + * // proof of (v1 :: T1, ..., vn :: Tn, P(vi), ..., P(vj)) |- P(c1 * v0 * ... * vn) + * } + * ... + * Case(ck, v1, ..., vm) { + * // proof of (v1 :: S1, ..., vm :: Sm, P(vi), ..., P(vj)) |- P(ck * v0 * ... * vn) + * } + * } + * }}} + * + * For lists it would look like: + * {{{ + * have(∀ x :: list(A). P(x)) by Induction(list) { + * Case(nil) { + * // ... + * have(P(nil)) by //Tactic to conclude the subcase + * } + * Case(cons, x, l) { + * // ... + * have((x :: A, l :: list(A), P(l)) |- P(cons * x * l)) by //Tactic to conclude the subcase + * } + * } + * }}} + * + * ====Defining functions==== + * + * Functions over ADT can also be defined via the following syntax: + * + * {{{ + * val myFunction = fun(adt, returnType) { + * Case(c1, v1, ..., vn) { + * body1 + * } + * ... + * Case(ck, v1, ..., vm) { + * bodyk + * } + * } + * }}} + * + * The body of each case is automatically typechecked at runtime. + * For example let's define the predecessor function over natural numbers + * + * {{{ + * val pred = fun(nat, nat) { + * Case(zero) { + * zero + * } + * Case(succ, n) { + * n + * } + * } + * }}} + * + * Recursive functions are not yet supported. + * + * ====Using functions==== + * + * Functions come with an introduction and elimination rules. + * Their introduction rule is of the form + * + * `∀ X1, ..., Xk. f(X1, ..., Xk) :: ADT(X1, ..., Xk) -> T` + * + * where `T` is the return type of the function + * + * On the other hand, we have an elimination rule for each constructor, giving the result of the function when applied to some + * constructor + * + * ` f * (ck * v1 * ... * vn) = body(ck) ` + * + * For example, for the `pred` function defined above the introduction rule would be + * + * `pred :: nat -> nat` + * + * and its elimination rules + * + * ` pred * zero = zero` + * + * ` pred * (succ * n) = n` + * + * Functions are also fully compatible with the typechecking tactic. We can for instance write: + * + * {{{ + * have(n :: nat |- pred * succ * n :: nat) + * }}} + * + * + */ +package object adt { + export ADTSyntax.{ |, define, constructors, adt_to_term, fun_to_term, constructor_to_term, Case, fun, -->} + export ADTSyntax.ADTBuilder.| + export lisa.maths.settheory.types.TypeSystem.* +} + diff --git a/lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala b/lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala new file mode 100644 index 000000000..34bc77eee --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala @@ -0,0 +1,913 @@ +package lisa.automation +import lisa.fol.FOL.{*, given} +import lisa.automation.Congruence.* +import lisa.automation.Congruence +import org.scalatest.funsuite.AnyFunSuite + + +class CongruenceTest extends AnyFunSuite with lisa.TestMain { + + + given lib: lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary + + val a = variable + val b = variable + val c = variable + val d = variable + val e = variable + val f = variable + val g = variable + val h = variable + val i = variable + val j = variable + val k = variable + val l = variable + val m = variable + val n = variable + val o = variable + + val x = variable + + val F = function[1] + + val one = variable + val two = variable + val * = SchematicFunctionLabel("*", 2) + val << = SchematicFunctionLabel("<<", 2) + val / = SchematicFunctionLabel("/", 2) + + + val af = formulaVariable + val bf = formulaVariable + val cf = formulaVariable + val df = formulaVariable + val ef = formulaVariable + val ff = formulaVariable + val gf = formulaVariable + val hf = formulaVariable + val if_ = formulaVariable + val jf = formulaVariable + val kf = formulaVariable + val lf = formulaVariable + val mf = formulaVariable + val nf = formulaVariable + val of = formulaVariable + + val xf = formulaVariable + + val Ff = SchematicConnectorLabel("Ff", 1) + val Fp = SchematicPredicateLabel("Fp", 1) + + val onef = formulaVariable + val twof = formulaVariable + val `*f` = SchematicConnectorLabel("*f", 2) + val `< cf, ef <=> ff, if_ <=> kf, mf <=> nf, af <=> bf, + of <=> mf, if_ <=> mf, gf <=> hf, lf <=> kf, bf <=> cf, ff <=> ef, of <=> if_, gf <=> ef, if_ <=> jf) + + val test1 = Theorem(base |- bf <=> cf) { + egraph.proveInnerFormula(bf, cf, base |- ()) + } + + val test2 = Theorem(base |- ff <=> hf) { + egraph.proveInnerFormula(ff, hf, base |- ()) + } + + val test3 = Theorem(base |- if_ <=> of) { + egraph.proveInnerFormula(if_, of, base |- ()) + } + + val test4 = Theorem(base |- of <=> if_) { + egraph.proveInnerFormula(of, if_, base |- ()) + } + + } + + test("4 formulas with congruence test with proofs") { + val egraph = new EGraphTerms() + egraph.add(Ff(af)) + egraph.add(Ff(bf)) + egraph.merge(af, bf) + val test5 = Theorem(af <=> bf |- Ff(af) <=> Ff(bf)) { + egraph.proveInnerFormula(Ff(af), Ff(bf), (af <=> bf) |- ()) + } + } + + test("divide-mult-shift by 2 in formulas egraph test with proofs") { + val egraph = new EGraphTerms() + egraph.add(onef) + egraph.add(twof) + egraph.add(af) + val ax2 = egraph.add(`*f`(af, twof)) + val ax2_d2 = egraph.add(`/f`(`*f`(af, twof), twof)) + val `2d2` = egraph.add(`/f`(twof, twof)) + val ax_2d2 = egraph.add(`*f`(af, `/f`(twof, twof))) + val ax1 = egraph.add(`*f`(af, onef)) + val as1 = egraph.add(`< as1, ax2_d2 <=> ax_2d2, `2d2` <=> onef, ax1 <=> af) + + val one_2d2 = Theorem(base |- onef <=> `2d2`) { + egraph.proveInnerFormula(onef, `2d2`, base |- ()) + } + + val ax2_as1 = Theorem(base |- ax2 <=> as1) { + egraph.proveInnerFormula(ax2, as1, base |- ()) + } + + val ax2_d2_ax_2d2 = Theorem(base |- ax2_d2 <=> ax_2d2) { + egraph.proveInnerFormula(ax2_d2, ax_2d2, base |- ()) + } + + val ax_2d2_ax1 = Theorem(base |- ax_2d2 <=> ax1) { + egraph.proveInnerFormula(ax_2d2, ax1, base |- ()) + } + + val ax_2d2_a = Theorem(base |- ax_2d2 <=> af) { + egraph.proveInnerFormula(ax_2d2, af, base |- ()) + } + + } + + test("long chain of formulas congruence eGraph with proofs") { + val egraph = new EGraphTerms() + egraph.add(xf) + val fx = egraph.add(Ff(xf)) + val ffx = egraph.add(Ff(fx)) + val fffx = egraph.add(Ff(ffx)) + val ffffx = egraph.add(Ff(fffx)) + val fffffx = egraph.add(Ff(ffffx)) + val ffffffx = egraph.add(Ff(fffffx)) + val fffffffx = egraph.add(Ff(ffffffx)) + val ffffffffx = egraph.add(Ff(fffffffx)) + + egraph.merge(ffffffffx, xf) + egraph.merge(fffffx, xf) + + val base = List(ffffffffx <=> xf, fffffx <=> xf) + + val test2 = Theorem(base |- fffx <=> xf) { + egraph.proveInnerFormula(fffx, xf, base |- ()) + } + val test3 = Theorem(base |- ffx <=> xf) { + egraph.proveInnerFormula(ffx, xf, base |- ()) + } + val test4 = Theorem(base |- fx <=> xf) { + egraph.proveInnerFormula(fx, xf, base |- ()) + } + } + + + test("2 terms 6 predicates with congruence egraph test with proofs") { + val egraph = new EGraphTerms() + egraph.add(Ff(Ff(Fp(a)))) + egraph.add(Ff(Ff(Fp(b)))) + egraph.merge(a, b) + + val test5 = Theorem((a === b) |- Fp(a) <=> Fp(b)) { + egraph.proveInnerFormula(Fp(a), Fp(b), (a === b) |- ()) + } + + val test6 = Theorem((a === b) |- Ff(Fp(a)) <=> Ff(Fp(b))) { + egraph.proveInnerFormula(Ff(Fp(a)), Ff(Fp(b)), (a === b) |- ()) + } + + val test7 = Theorem((a === b) |- Ff(Ff(Fp(a))) <=> Ff(Ff(Fp(b))) ) { + egraph.proveInnerFormula(Ff(Ff(Fp(a))), Ff(Ff(Fp(b))), (a === b) |- ()) + } + + } + + test("6 terms 6 predicates with congruence egraph test with proofs") { + val egraph = new EGraphTerms() + egraph.add(Ff(Ff(Fp(F(F(a)))))) + egraph.add(Ff(Ff(Fp(F(F(b)))))) + egraph.merge(a, b) + + val test5 = Theorem((a === b) |- (F(a) === F(b))) { + egraph.proveInnerTerm(F(a), F(b), (a === b) |- ()) + } + + val test6 = Theorem((a === b) |- Fp(F(F(a))) <=> Fp(F(F(b))) ) { + egraph.proveInnerFormula(Fp(F(F(a))), Fp(F(F(b))), (a === b) |- ()) + } + + val test7 = Theorem((a === b) |- Ff(Ff(Fp(F(F(a))))) <=> Ff(Ff(Fp(F(F(b))))) ) { + egraph.proveInnerFormula(Ff(Ff(Fp(F(F(a))))), Ff(Ff(Fp(F(F(b))))), (a === b) |- ()) + } + + egraph.merge(Fp(F(F(b))), Ff(Fp(F(F(a))))) + + val test8 = Theorem(((a === b), Fp(F(F(b))) <=> Ff(Fp(F(F(a)))) ) |- Ff(Ff(Fp(F(F(a))))) <=> Ff(Ff(Fp(F(F(b))))) ) { + egraph.proveInnerFormula(Ff(Ff(Fp(F(F(a))))), Ff(Ff(Fp(F(F(b))))), (a === b, Fp(F(F(b))) <=> Ff(Fp(F(F(a)))) ) |- ()) + } + + } + + test("Full congruence tactic tests") { + println("Full congruence tactic tests\n") + + val base1 = List(a === c, e === f, i === k, m === n, a === b, o === m, i === m, g === h, l === k, b === c, f === e, o === i, g === e, i === j) + + val test1 = Theorem(base1 |- (b === c)) { + have(thesis) by Congruence + } + + val test2 = Theorem(base1 |- (f === h)) { + have(thesis) by Congruence + } + + val test3 = Theorem(base1 |- (i === o)) { + have(thesis) by Congruence + } + + + val ax2 = `*`(a, two) + val ax2_d2 = `/`(`*`(a, two), two) + val `2d2` = `/`(two, two) + val ax_2d2 = `*`(a, `/`(two, two)) + val ax1 = `*`(a, one) + val as1 = `<<`(a, one) + + val base2 = List[Formula](ax2 === as1, ax2_d2 === ax_2d2, `2d2` === one, ax1 === a) + + + val one_2d2 = Theorem(base2 |- (one === `2d2`)) { + have(thesis) by Congruence + } + + val ax2_as1 = Theorem(base2 |- (ax2 === as1)) { + have(thesis) by Congruence + } + + val ax2_d2_ax_2d2 = Theorem(base2 |- (ax2_d2 === ax_2d2)) { + have(thesis) by Congruence + } + + val ax_2d2_ax1 = Theorem(base2 |- (ax_2d2 === ax1)) { + have(thesis) by Congruence + } + + val ax_2d2_a = Theorem(base2 |- (ax_2d2 === a)) { + have(thesis) by Congruence + } + + val ax_2d2_a_2 = Theorem(base2 |- (Fp(ax_2d2) <=> Fp(a))) { + have(thesis) by Congruence + } + + val ax_2d2_a_1 = Theorem((Fp(a) :: base2) |- Fp(ax_2d2)) { + have(thesis) by Congruence + } + + val ax_2d2_a_3 = Theorem((base2 :+ Fp(ax_2d2) :+ !Fp(a)) |- () ) { + have(thesis) by Congruence + } + + val test5 = Theorem(a===b |- F(a) === F(b)) { + have(thesis) by Congruence + } + + val test6 = Theorem(a === b |- F(a) === F(b)) { + have(thesis) by Congruence + } + + val test7 = Theorem((Ff(Ff(Ff(Ff(Ff(Ff(Ff(xf))))))) <=> xf, Ff(Ff(Ff(Ff(Ff(xf))))) <=> xf) |- Ff(Ff(Ff(xf))) <=> xf) { + have(thesis) by Congruence + } + + val test8 = Theorem((Ff(Ff(Ff(Ff(Ff(Ff(Ff(xf))))))) <=> xf, Ff(Ff(Ff(Ff(Ff(xf))))) <=> xf) |- Ff(xf) <=> xf) { + have(thesis) by Congruence + } + + val test9 = Theorem((a === b) |- (Fp(F(F(a))), !Fp(F(F(b)))) ) { + have(thesis) by Congruence + } + + val test10 = Theorem((a === b) |- Fp(F(F(a))) <=> Fp(F(F(b))) ) { + have(thesis) by Congruence + } + + + val test11 = Theorem((a === b) |- Ff(Ff(Fp(F(F(a))))) <=> Ff(Ff(Fp(F(F(b))))) ) { + have(thesis) by Congruence + } + + val test12 = Theorem(((a === b), Fp(F(F(b))) <=> Ff(Fp(F(F(a)))), Ff(Ff(Fp(F(F(a))))) ) |- Ff(Ff(Fp(F(F(b))))) ) { + have(thesis) by Congruence + } + + + } + + +} \ No newline at end of file diff --git a/lisa-topology/src/test/scala/lisa/automation/TableauTest.scala b/lisa-topology/src/test/scala/lisa/automation/TableauTest.scala new file mode 100644 index 000000000..69a8d2507 --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/automation/TableauTest.scala @@ -0,0 +1,158 @@ +package lisa.test.automation + +import lisa.SetTheoryLibrary.{_, given} +import lisa.automation.Substitution.* +import lisa.automation.Tableau.* +import lisa.fol.FOL.* +import lisa.prooflib.Exports.* +import lisa.utils.K.SCProofChecker.checkSCProof +import lisa.utils.parsing.FOLPrinter.prettyFormula +import lisa.utils.parsing.FOLPrinter.prettySCProof +import lisa.utils.parsing.FOLPrinter.prettyTerm +import org.scalatest.funsuite.AnyFunSuite + +class TableauTest extends AnyFunSuite { + import TableauTest.* + + test(s"Propositional Positive cases (${posi.size})") { + assert(posi.forall(_._3), posi.map((i, f, b, proof, judg) => s"$i $b" + (if !b then s" $f" else "")).mkString("\n")) + if posi.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then + fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) + } + + test(s"First Order Quantifier Free Positive cases (${posqf.size})") { + assert(posqf.forall(_._3), posqf.map((i, f, b, proof, judg) => (s"$i $b" + (if !b then s" $f" else ""))).mkString("\n")) + if posqf.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then + fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) + } + + test(s"First Order Easy Positive cases (${poseasy.size})") { + assert(poseasy.forall(_._3), poseasy.map((i, f, b, proof, judg) => (s"$i $b" + (if !b then s" $f" else ""))).mkString("\n")) + if poseasy.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then + fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) + } + + test(s"First Order Hard Positive cases (${poshard.size})") { + assert(poshard.forall(_._3), poshard.map((i, f, b, proof, judg) => (s"$i $b" + (if !b then s" $f" else ""))).mkString("\n")) + if poshard.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then + fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) + } + +} +object TableauTest { + + val u = variable + val w = variable + val x = variable + val y = variable + val z = variable + + val a = formulaVariable + val b = formulaVariable + val c = formulaVariable + val d = formulaVariable + val e = formulaVariable + + val f = function[1] + val g = function[1] + val h = function[2] + + val D = predicate[1] + val E = predicate[2] + val P = predicate[1] + val Q = predicate[1] + val R = predicate[2] + + var doprint: Boolean = false + def printif(s: Any) = if doprint then println(s) else () + + val posi = List( + a <=> a, + a \/ !a, + ((a ==> b) /\ (b ==> c)) ==> (a ==> c), + (a <=> b) <=> ((a /\ b) \/ (!a /\ !b)), + ((a ==> c) /\ (b ==> c)) <=> ((a \/ b) ==> c), + ((a ==> b) /\ (c ==> d)) ==> ((a \/ c) ==> (b \/ d)) + ).zipWithIndex.map(f => + val res = solve(() |- f._1) + (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) + ) + + // Quantifier Free + + val posqf = List( + posi.map(fo => fo._2.substitute(a := P(h(x, y)), b := P(x), c := R(x, h(x, y)))), + posi.map(fo => fo._2.substitute(a := P(h(x, y)), b := P(h(x, y)), c := R(x, h(x, f(x))))), + posi.map(fo => fo._2.substitute(a := R(y, y), b := P(h(y, y)), c := R(h(x, y), h(z, y)))) + ).flatten.zipWithIndex.map(f => + val res = solve(() |- f._1) + (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) + ) + + // First Order Easy + + val poseasy = List( + posi.map(fo => fo._2.substitute(a := forall(x, P(x)), b := forall(x, P(y)), c := exists(x, P(x)))), + posi.map(fo => fo._2.substitute(a := forall(x, P(x) /\ Q(f(x))), b := forall(x, P(x) \/ R(y, x)), c := exists(x, Q(x) ==> R(x, y)))), + posi.map(fo => fo._2.substitute(a := exists(y, forall(x, P(x) /\ Q(f(y)))), b := forall(y, exists(x, P(x) \/ R(y, x))), c := forall(y, exists(x, Q(x) ==> R(x, y))))) + ).flatten.zipWithIndex.map(f => + val res = solve(() |- f._1) + (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) + ) + + // First Order Hard, from https://isabelle.in.tum.de/library/FOL/FOL-ex/Quantifiers_Cla.html + + val a1 = forall(x, forall(y, forall(z, ((E(x, y) /\ E(y, z)) ==> E(x, z))))) + val a2 = forall(x, forall(y, (E(x, y) ==> E(f(x), f(y))))) + val a3 = forall(x, E(f(g(x)), g(f(x)))) + val biga = forall( + x, + forall( + y, + forall( + z, + ((E(x, y) /\ E(y, z)) ==> E(x, z)) /\ + (E(x, y) ==> E(f(x), f(y))) /\ + E(f(g(x)), g(f(x))) + ) + ) + ) + + val poshard = List( + forall(x, P(x) ==> Q(x)) ==> (forall(x, P(x)) ==> forall(x, Q(x))), + forall(x, forall(y, R(x, y))) ==> forall(y, forall(x, R(x, y))), + forall(x, forall(y, R(x, y))) ==> forall(y, forall(x, R(y, x))), + exists(x, exists(y, R(x, y))) ==> exists(y, exists(x, R(x, y))), + exists(x, exists(y, R(x, y))) ==> exists(y, exists(x, R(y, x))), + (forall(x, P(x)) \/ forall(x, Q(x))) ==> forall(x, P(x) \/ Q(x)), + forall(x, a ==> Q(x)) <=> (a ==> forall(x, Q(x))), + forall(x, P(x) ==> a) <=> (exists(x, P(x)) ==> a), + exists(x, P(x) \/ Q(x)) <=> (exists(x, P(x)) \/ exists(x, Q(x))), + exists(x, P(x) /\ Q(x)) ==> (exists(x, P(x)) /\ exists(x, Q(x))), + exists(y, forall(x, R(x, y))) ==> forall(x, exists(y, R(x, y))), + forall(x, Q(x)) ==> exists(x, Q(x)), + (forall(x, P(x) ==> Q(x)) /\ exists(x, P(x))) ==> exists(x, Q(x)), + ((a ==> exists(x, Q(x))) /\ a) ==> exists(x, Q(x)), + forall(x, P(x) ==> Q(f(x))) /\ forall(x, Q(x) ==> R(g(x), x)) ==> (P(y) ==> R(g(f(y)), f(y))), + forall(x, forall(y, P(x) ==> Q(y))) ==> (exists(x, P(x)) ==> forall(y, Q(y))), + (exists(x, P(x)) ==> forall(y, Q(y))) ==> forall(x, forall(y, P(x) ==> Q(y))), + forall(x, forall(y, P(x) ==> Q(y))) <=> (exists(x, P(x)) ==> forall(y, Q(y))), + exists(x, exists(y, P(x) /\ R(x, y))) ==> (exists(x, P(x) /\ exists(y, R(x, y)))), + (exists(x, P(x) /\ exists(y, R(x, y)))) ==> exists(x, exists(y, P(x) /\ R(x, y))), + exists(x, exists(y, P(x) /\ R(x, y))) <=> (exists(x, P(x) /\ exists(y, R(x, y)))), + exists(y, forall(x, P(x) ==> R(x, y))) ==> (forall(x, P(x)) ==> exists(y, R(x, y))), + forall(x, P(x)) ==> P(y), + !forall(x, D(x) /\ !D(f(x))), + !forall(x, (D(x) /\ !D(f(x))) \/ (D(x) /\ !D(x))), + forall(x, forall(y, (E(x, y) ==> E(f(x), f(y))) /\ E(f(g(x)), g(f(x))))) ==> E(f(f(g(u))), f(g(f(u)))), + !(forall(x, !((E(f(x), x)))) /\ forall(x, forall(y, !(E(x, y)) /\ E(f(x), g(x))))), + a1 /\ a2 /\ a3 ==> E(f(f(g(u))), f(g(f(u)))), + a1 /\ a2 /\ a3 ==> E(f(g(f(u))), g(f(f(u)))), + biga ==> E(f(f(g(u))), f(g(f(u)))), + biga ==> E(f(g(f(u))), g(f(f(u)))) + ).zipWithIndex.map(f => + val res = solve(() |- f._1) + (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) + ) + +} diff --git a/lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala b/lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala new file mode 100644 index 000000000..2f01882f7 --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala @@ -0,0 +1,344 @@ +package lisa.examples.peano_example + +import lisa.kernel.fol.FOL.* +import lisa.kernel.proof.RunningTheory +import lisa.kernel.proof.SCProof +import lisa.kernel.proof.SequentCalculus.* +import lisa.prooflib.Library +import lisa.prooflib.OutputManager +import lisa.utils.KernelHelpers.{_, given} +import lisa.utils.Printer + +object Peano { /* + export PeanoArithmeticsLibrary.{_, given} + + /////////////////////////// OUTPUT CONTROL ////////////////////////// + given om:OutputManager = new OutputManager { + override val output: String => Unit = println + override val finishOutput: Throwable => Nothing = e => throw e + } + + def main(args: Array[String]): Unit = {} + ///////////////////////////////////////////////////////////////////// + + def instantiateForallImport(proofImport: Sequent, t: Peano.Term): SCProof = { + require(proofImport.right.size == 1) + val formula = proofImport.right.head + require(formula.isInstanceOf[BinderFormula] && formula.asInstanceOf[BinderFormula].label == Forall) + val forall = formula.asInstanceOf[BinderFormula] + instantiateForall(SCProof(IndexedSeq(), IndexedSeq(proofImport))) + val tempVar = VariableLabel(freshId(formula.freeVariables.map(_.id), "x")) + val instantiated = instantiateBinder(forall, t) + val p1 = SC.Hypothesis(instantiated |- instantiated, instantiated) + val p2 = SC.LeftForall(formula |- instantiated, 0, instantiateBinder(forall, tempVar), tempVar, t) + val p3 = SC.Cut(() |- instantiated, -1, 1, formula) + SCProof(IndexedSeq(p1, p2, p3), IndexedSeq(proofImport)) + } + + def applyInduction(baseProof: SC.SCSubproof, inductionStepProof: SC.SCSubproof, inductionInstance: SCProofStep): IndexedSeq[SCProofStep] = { + require(baseProof.bot.right.size == 1, s"baseProof should prove exactly one formula, got ${FOLPrinter.prettySequent(baseProof.bot)}") + require(inductionStepProof.bot.right.size == 1, s"inductionStepProof should prove exactly one formula, got ${FOLPrinter.prettySequent(inductionStepProof.bot)}") + require( + inductionInstance.bot.left.isEmpty && inductionInstance.bot.right.size == 1, + s"induction instance step should have nothing on the left and exactly one formula on the right, got ${FOLPrinter.prettySequent(inductionInstance.bot)}" + ) + 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 ${FOLPrinter.prettyFormula(inductionInstance.bot.right.head)}") + }): @unchecked + val baseFormula = baseProof.bot.right.head + val stepFormula = inductionStepProof.bot.right.head + require( + isSame(baseFormula /\ stepFormula, premise), + "induction instance premise should be of the form base /\\ step, got " + + s"premise: ${FOLPrinter.prettyFormula(premise)}, base: ${FOLPrinter.prettyFormula(baseFormula)}, step: ${FOLPrinter.prettyFormula(stepFormula)}" + ) + + val lhs: Set[Formula] = baseProof.bot.left ++ inductionStepProof.bot.left + val base0 = baseProof + val step1 = inductionStepProof + val instance2 = inductionInstance + val inductionPremise3 = SC.RightAnd(lhs |- baseFormula /\ stepFormula, Seq(0, 1), Seq(baseFormula, stepFormula)) + val hypConclusion4 = hypothesis(conclusion) + val inductionInstanceOnTheLeft5 = SC.LeftImplies(lhs + (premise ==> conclusion) |- conclusion, 3, 4, premise, conclusion) + val cutInductionInstance6 = SC.Cut(lhs |- conclusion, 2, 5, premise ==> conclusion) + IndexedSeq(base0, step1, instance2, inductionPremise3, hypConclusion4, inductionInstanceOnTheLeft5, cutInductionInstance6) + } + + val (y1, z1) = + (VariableLabel("y1"), VariableLabel("z1")) + + THEOREM("x + 0 = 0 + x") of "∀'x. +('x, 0) = +(0, 'x)" PROOF2 { + val refl0: SCProofStep = SC.RightRefl(() |- s(x) === s(x), s(x) === s(x)) + val subst1 = SC.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 = SC.RightImplies(() |- (x === plus(zero, x)) ==> (s(x) === s(plus(zero, x))), 1, x === plus(zero, x), s(x) === s(plus(zero, x))) + val transform3 = SC.RightSubstEq( + (plus(zero, s(x)) === s(plus(zero, x))) |- (x === plus(zero, x)) ==> (s(x) === plus(zero, s(x))), + 2, + (plus(zero, s(x)), s(plus(zero, x))) :: Nil, + LambdaTermFormula(Seq(y), (x === plus(zero, x)) ==> (s(x) === y)) + ) + + // result: ax4plusSuccessor |- 0+Sx = S(0 + x) + val instanceAx4_4 = SC.SCSubproof( + instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", zero), x), + Seq(-1) + ) + val cut5 = SC.Cut(() |- (x === plus(zero, x)) ==> (s(x) === plus(zero, s(x))), 4, 3, plus(zero, s(x)) === s(plus(zero, x))) + + val transform6 = SC.RightSubstEq( + Set(plus(x, zero) === x, plus(s(x), zero) === s(x)) |- (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), + 5, + (plus(x, zero), x) :: (plus(s(x), zero), s(x)) :: Nil, + LambdaTermFormula(Seq(y, z), (y === plus(zero, x)) ==> (z === plus(zero, s(x)))) + ) + val leftAnd7 = SC.LeftAnd( + (plus(x, zero) === x) /\ (plus(s(x), zero) === s(x)) |- (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), + 6, + plus(x, zero) === x, + plus(s(x), zero) === s(x) + ) + + val instancePlusZero8 = SC.SCSubproof( + instantiateForallImport(ax"ax3neutral", x), + Seq(-2) + ) + val instancePlusZero9 = SC.SCSubproof( + instantiateForallImport(ax"ax3neutral", s(x)), + Seq(-2) + ) + val rightAnd10 = SC.RightAnd(() |- (plus(x, zero) === x) /\ (plus(s(x), zero) === s(x)), Seq(8, 9), Seq(plus(x, zero) === x, plus(s(x), zero) === s(x))) + + val cut11 = SC.Cut( + () |- (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), + 10, + 7, + (plus(x, zero) === x) /\ (plus(s(x), zero) === s(x)) + ) + + val forall12 = SC.RightForall( + cut11.bot.left |- forall(x, (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x)))), + 11, + (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), + x + ) + + val inductionInstance: SCProofStep = SC.InstPredSchema( + () |- ((plus(zero, zero) === plus(zero, zero)) /\ forall(x, (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))))) ==> forall( + x, + plus(x, zero) === plus(zero, x) + ), + -3, + Map(sPhi -> LambdaTermFormula(Seq(x), plus(x, zero) === plus(zero, x))) + ) + + SCProof( + applyInduction( + SC.SCSubproof(SCProof(SC.RightRefl(() |- zero === zero, zero === zero))), + SC.SCSubproof( + SCProof( + IndexedSeq(refl0, subst1, implies2, transform3, instanceAx4_4, cut5, transform6, leftAnd7, instancePlusZero8, instancePlusZero9, rightAnd10, cut11, forall12), + IndexedSeq(ax"ax4plusSuccessor", ax"ax3neutral") + ), + Seq(-1, -2) + ), + inductionInstance + ), + IndexedSeq(ax"ax4plusSuccessor", ax"ax3neutral", ax"ax7induction") + ) + } using (ax"ax4plusSuccessor", ax"ax3neutral", ax"ax7induction") + show + + THEOREM("switch successor") of "∀'x. ∀'y. +('x, S('y)) = +(S('x), 'y)" PROOF2 { + //////////////////////////////////// Base: x + S0 = Sx + 0 /////////////////////////////////////////////// + val base0 = { + // x + 0 = x + val xEqXPlusZero0 = SC.SCSubproof(instantiateForallImport(ax"ax3neutral", x), IndexedSeq(-1)) + // Sx + 0 = Sx + val succXEqSuccXPlusZero1 = SC.SCSubproof(instantiateForallImport(ax"ax3neutral", s(x)), IndexedSeq(-1)) + // x + S0 = S(x + 0) + val xPlusSuccZero2 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", x), zero), IndexedSeq(-2)) + + // ------------------- x + 0 = x, Sx + 0 = Sx, x + S0 = S(x + 0) |- Sx + 0 = x + S0 --------------------- + val succX3 = SC.RightRefl(() |- s(x) === s(x), s(x) === s(x)) + val substEq4 = SC.RightSubstEq( + Set(s(x) === plus(s(x), zero), x === plus(x, zero)) |- plus(s(x), zero) === s(plus(x, zero)), + 3, + (s(x), plus(s(x), zero)) :: (VariableTerm(x), plus(x, zero)) :: Nil, + LambdaTermFormula(Seq(y, z), y === s(z)) + ) + val substEq5 = SC.RightSubstEq( + Set(s(x) === plus(s(x), zero), x === plus(x, zero), s(plus(x, zero)) === plus(x, s(zero))) |- plus(s(x), zero) === plus(x, s(zero)), + 4, + (s(plus(x, zero)), plus(x, s(zero))) :: Nil, + LambdaTermFormula(Seq(z), plus(s(x), zero) === z) + ) + // ------------------------------------------------------------------------------------------------------- + val cut6 = SC.Cut(Set(s(x) === plus(s(x), zero), x === plus(x, zero)) |- plus(s(x), zero) === plus(x, s(zero)), 2, 5, s(plus(x, zero)) === plus(x, s(zero))) + val cut7 = SC.Cut(x === plus(x, zero) |- plus(s(x), zero) === plus(x, s(zero)), 1, 6, s(x) === plus(s(x), zero)) + val cut8 = SC.Cut(() |- plus(s(x), zero) === plus(x, s(zero)), 0, 7, x === plus(x, zero)) + SC.SCSubproof( + SCProof( + IndexedSeq(xEqXPlusZero0, succXEqSuccXPlusZero1, xPlusSuccZero2, succX3, substEq4, substEq5, cut6, cut7, cut8), + IndexedSeq(ax"ax3neutral", ax"ax4plusSuccessor") + ), + IndexedSeq(-1, -2) + ) + } + + /////////////// Induction step: ?y. (x + Sy === Sx + y) ==> (x + SSy === Sx + Sy) //////////////////// + val inductionStep1 = { + // x + SSy = S(x + Sy) + val moveSuccessor0 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", x), s(y)), IndexedSeq(-2)) + + // Sx + Sy = S(Sx + y) + val moveSuccessor1 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", s(x)), y), IndexedSeq(-2)) + + // ----------- x + SSy = S(x + Sy), x + Sy = Sx + y, S(Sx + y) = Sx + Sy |- x + SSy = Sx + Sy ------------ + val middleEq2 = SC.RightRefl(() |- s(plus(x, s(y))) === s(plus(x, s(y))), s(plus(x, s(y))) === s(plus(x, s(y)))) + val substEq3 = + SC.RightSubstEq( + Set(plus(x, s(y)) === plus(s(x), y)) |- s(plus(x, s(y))) === s(plus(s(x), y)), + 2, + (plus(x, s(y)), plus(s(x), y)) :: Nil, + LambdaTermFormula(Seq(z1), s(plus(x, s(y))) === s(z1)) + ) + val substEq4 = + SC.RightSubstEq( + Set(plus(x, s(y)) === plus(s(x), y), plus(x, s(s(y))) === s(plus(x, s(y)))) |- plus(x, s(s(y))) === s(plus(s(x), y)), + 3, + (plus(x, s(s(y))), s(plus(x, s(y)))) :: Nil, + LambdaTermFormula(Seq(z1), z1 === s(plus(s(x), y))) + ) + val substEq5 = + SC.RightSubstEq( + Set(plus(x, s(s(y))) === s(plus(x, s(y))), plus(x, s(y)) === plus(s(x), y), s(plus(s(x), y)) === plus(s(x), s(y))) |- plus(x, s(s(y))) === plus(s(x), s(y)), + 4, + (s(plus(s(x), y)), plus(s(x), s(y))) :: Nil, + LambdaTermFormula(Seq(z1), plus(x, s(s(y))) === z1) + ) + // ------------------------------------------------------------------------------------------------------- + val cut6 = SC.Cut(Set(plus(x, s(y)) === plus(s(x), y), s(plus(s(x), y)) === plus(s(x), s(y))) |- plus(x, s(s(y))) === plus(s(x), s(y)), 0, 5, plus(x, s(s(y))) === s(plus(x, s(y)))) + val cut7 = SC.Cut(plus(x, s(y)) === plus(s(x), y) |- plus(x, s(s(y))) === plus(s(x), s(y)), 1, 6, s(plus(s(x), y)) === plus(s(x), s(y))) + val implies8 = SC.RightImplies(() |- (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y))), 7, plus(x, s(y)) === plus(s(x), y), plus(x, s(s(y))) === plus(s(x), s(y))) + val forall9 = SC.RightForall( + () |- forall(y, (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y)))), + 8, + (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y))), + y + ) + SC.SCSubproof( + SCProof( + IndexedSeq(moveSuccessor0, moveSuccessor1, middleEq2, substEq3, substEq4, substEq5, cut6, cut7, implies8, forall9), + IndexedSeq(ax"ax3neutral", ax"ax4plusSuccessor") + ), + IndexedSeq(-1, -2) + ) + } + + val inductionInstance = { + val inductionOnY0 = SC.Rewrite(() |- (sPhi(zero) /\ forall(y, sPhi(y) ==> sPhi(s(y)))) ==> forall(y, sPhi(y)), -1) + val inductionInstance1 = SC.InstPredSchema( + () |- + ((plus(s(x), zero) === plus(x, s(zero))) /\ + forall(y, (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y))))) ==> + forall(y, plus(x, s(y)) === plus(s(x), y)), + 0, + Map(sPhi -> LambdaTermFormula(Seq(y), plus(x, s(y)) === plus(s(x), y))) + ) + SC.SCSubproof(SCProof(IndexedSeq(inductionOnY0, inductionInstance1), IndexedSeq(ax"ax7induction")), Seq(-3)) + } + val inductionApplication = applyInduction(base0, inductionStep1, inductionInstance) + val addForall = SC.RightForall(() |- forall(x, forall(y, plus(x, s(y)) === plus(s(x), y))), inductionApplication.size - 1, forall(y, plus(x, s(y)) === plus(s(x), y)), x) + val proof: SCProof = SCProof( + inductionApplication :+ addForall, + IndexedSeq(ax"ax3neutral", ax"ax4plusSuccessor", ax"ax7induction") + ) + proof + } using (ax"ax3neutral", ax"ax4plusSuccessor", ax"ax7induction") + show + + THEOREM("additivity of addition") of "" PROOF2 { + val base0 = SC.SCSubproof(instantiateForallImport(thm"x + 0 = 0 + x", x), Seq(-3)) + val inductionStep1 = { + val start0 = SC.RightRefl(() |- plus(x, s(y)) === plus(x, s(y)), plus(x, s(y)) === plus(x, s(y))) + val applyPlusSuccAx1 = + SC.RightSubstEq(plus(x, s(y)) === s(plus(x, y)) |- plus(x, s(y)) === s(plus(x, y)), 0, (plus(x, s(y)), s(plus(x, y))) :: Nil, LambdaTermFormula(Seq(z1), plus(x, s(y)) === z1)) + val applyInductionPremise2 = + SC.RightSubstEq( + Set(plus(x, s(y)) === s(plus(x, y)), plus(x, y) === plus(y, x)) |- plus(x, s(y)) === s(plus(y, x)), + 1, + (plus(x, y), plus(y, x)) :: Nil, + LambdaTermFormula(Seq(z1), plus(x, s(y)) === s(z1)) + ) + val applyPlusSuccAx3 = + SC.RightSubstEq( + Set(plus(x, s(y)) === s(plus(x, y)), plus(x, y) === plus(y, x), s(plus(y, x)) === plus(y, s(x))) |- plus(x, s(y)) === plus(y, s(x)), + 2, + (s(plus(y, x)), plus(y, s(x))) :: Nil, + LambdaTermFormula(Seq(z1), plus(x, s(y)) === z1) + ) + val applySwitchSuccessor4 = + SC.RightSubstEq( + Set(plus(x, s(y)) === s(plus(x, y)), plus(x, y) === plus(y, x), s(plus(y, x)) === plus(y, s(x)), plus(y, s(x)) === plus(s(y), x)) |- plus(x, s(y)) === plus(s(y), x), + 3, + (plus(y, s(x)), plus(s(y), x)) :: Nil, + LambdaTermFormula(Seq(z1), plus(x, s(y)) === z1) + ) + + val xPlusSYInstance5 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", x), y), Seq(-1)) + val cutXPlusSY6 = + SC.Cut(Set(plus(x, y) === plus(y, x), s(plus(y, x)) === plus(y, s(x)), plus(y, s(x)) === plus(s(y), x)) |- plus(x, s(y)) === plus(s(y), x), 5, 4, plus(x, s(y)) === s(plus(x, y))) + val yPlusSXInstance7 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", y), x), Seq(-1)) + val cutYPlusSX8 = SC.Cut(Set(plus(x, y) === plus(y, x), plus(y, s(x)) === plus(s(y), x)) |- plus(x, s(y)) === plus(s(y), x), 7, 6, s(plus(y, x)) === plus(y, s(x))) + val swichSuccessorInstance9 = SC.SCSubproof(instantiateForall(instantiateForallImport(thm"switch successor", y), x), Seq(-2)) + val cutSwitchSuccessor10 = SC.Cut(plus(x, y) === plus(y, x) |- plus(x, s(y)) === plus(s(y), x), 9, 8, plus(y, s(x)) === plus(s(y), x)) + val rightImplies11 = SC.RightImplies(() |- (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x)), 10, plus(x, y) === plus(y, x), plus(x, s(y)) === plus(s(y), x)) + val forall12 = SC.RightForall(() |- forall(y, (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x))), 11, (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x)), y) + SC.SCSubproof( + SCProof( + IndexedSeq( + start0, + applyPlusSuccAx1, + applyInductionPremise2, + applyPlusSuccAx3, + applySwitchSuccessor4, + xPlusSYInstance5, + cutXPlusSY6, + yPlusSXInstance7, + cutYPlusSX8, + swichSuccessorInstance9, + cutSwitchSuccessor10, + rightImplies11, + forall12 + ), + IndexedSeq(ax"ax4plusSuccessor", thm"switch successor") + ), + IndexedSeq(-1, -4) + ) + } + + val inductionInstance = { + val inductionOnY0 = SC.Rewrite(() |- (sPhi(zero) /\ forall(y, sPhi(y) ==> sPhi(s(y)))) ==> forall(y, sPhi(y)), -1) + val inductionInstance1 = SC.InstPredSchema( + () |- + ((plus(x, zero) === plus(zero, x)) /\ + forall(y, (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x)))) ==> + forall(y, plus(x, y) === plus(y, x)), + 0, + Map(sPhi -> LambdaTermFormula(Seq(y), plus(x, y) === plus(y, x))) + ) + SC.SCSubproof(SCProof(IndexedSeq(inductionOnY0, inductionInstance1), IndexedSeq(ax"ax7induction")), Seq(-2)) + } + val inductionApplication = applyInduction(base0, inductionStep1, inductionInstance) + val addForall = SC.RightForall(() |- forall(x, forall(y, plus(x, y) === plus(y, x))), inductionApplication.size - 1, forall(y, plus(x, y) === plus(y, x)), x) + val proof: SCProof = SCProof( + inductionApplication :+ addForall, + IndexedSeq(ax"ax4plusSuccessor", ax"ax7induction", thm"x + 0 = 0 + x", thm"switch successor") + ) + proof + } using (ax"ax4plusSuccessor", ax"ax7induction", thm"x + 0 = 0 + x", thm"switch successor") + show + + */ +} diff --git a/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala b/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala new file mode 100644 index 000000000..e422b6f15 --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala @@ -0,0 +1,39 @@ +package lisa.examples.peano_example + +import lisa.kernel.fol.FOL.* +import lisa.kernel.proof.RunningTheory +import lisa.utils.KernelHelpers.{_, given} + +object PeanoArithmetics extends lisa.prooflib.Library { + export lisa.fol.FOL.{*, given} + final val (x, y, z) = + (variable, variable, variable) + + final val zero = Constant("0") + final val s = ConstantFunctionLabel("S", 1) + final val plus = ConstantFunctionLabel("+", 2) + final val times = ConstantFunctionLabel("*", 2) + final val sPhi = predicate[1] + val theory: RunningTheory = new RunningTheory() + final val ax1ZeroSuccessor: Formula = forall(x, !(s(x) === zero)) + final val ax2Injectivity: Formula = forall(x, forall(y, (s(x) === s(y)) ==> (x === y))) + final val ax3neutral: Formula = forall(x, plus(x, zero) === x) + final val ax4plusSuccessor: Formula = forall(x, forall(y, plus(x, s(y)) === s(plus(x, y)))) + final val ax5timesZero: Formula = forall(x, times(x, zero) === zero) + final val ax6timesDistrib: Formula = forall(x, forall(y, times(x, s(y)) === plus(times(x, y), x))) + final val ax7induction: Formula = (sPhi(zero) /\ forall(x, sPhi(x) ==> sPhi(s(x)))) ==> forall(x, sPhi(x)) + + final val functions: Set[ConstantTermLabel[?]] = Set(ConstantFunctionLabel("0", 0), s, plus, times) + functions.foreach(l => theory.addSymbol(l.underlyingLabel)) + + private val peanoAxioms: Set[(String, Formula)] = Set( + ("ax1ZeroSuccessor", ax1ZeroSuccessor), + ("ax2Injectivity", ax2Injectivity), + ("ax3neutral", ax3neutral), + ("ax4plusSuccessor", ax4plusSuccessor), + ("ax5timesZero", ax5timesZero), + ("ax6timesDistrib", ax6timesDistrib), + ("ax7induction", ax7induction) + ) + peanoAxioms.foreach(a => theory.addAxiom(a._1, a._2.underlying)) +} diff --git a/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala b/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala new file mode 100644 index 000000000..3f9d423fe --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala @@ -0,0 +1,7 @@ +package lisa.examples.peano_example + +import lisa.examples.peano_example + +trait PeanoArithmeticsLibrary extends lisa.prooflib.BasicMain { + export PeanoArithmetics.* +} diff --git a/lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala b/lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala new file mode 100644 index 000000000..9800e4fe9 --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala @@ -0,0 +1,20 @@ +package lisa.proven + +import lisa.test.ProofCheckerSuite +import lisa.utils.Printer + +class InitialProofsTests extends ProofCheckerSuite { + import lisa.SetTheoryLibrary.* + + /* + test("File SetTheory initialize well") { + lisa.proven.mathematics.SetTheory + succeed + } + + test("File Mapping initialize well") { + lisa.proven.mathematics.Mapping + succeed + } + */ +} diff --git a/lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala b/lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala new file mode 100644 index 000000000..7b7a67c38 --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala @@ -0,0 +1,83 @@ +package lisa.utilities + +import org.scalatest.funsuite.AnyFunSuite + +class ComprehensionsTests extends AnyFunSuite { + + object ComprehensionsTests extends lisa.Main { + import lisa.maths.settheory.SetTheory.* + import lisa.maths.settheory.Comprehensions.* + + private val x = variable + private val x_1 = variable + private val y = variable + private val z = variable + private val s = variable + private val t = variable + private val A = variable + private val B = variable + private val C = variable + private val P = predicate[2] + private val Q = predicate[1] + private val Filter = predicate[1] + private val Map = function[1] + private val f = function[1] + + val singletonMap = Lemma( + ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) <=> (x === f(∅)) + ) { + val s1 = have(∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) ==> (x === f(∅))) subproof { + have(x === f(∅) |- x === f(∅)) by Restate + thenHave((x_1 === ∅, x === f(x_1)) |- x === f(∅)) by Substitution.ApplyRules(x_1 === ∅) + thenHave((x_1 === ∅) /\ (x === f(x_1)) |- x === f(∅)) by Restate + thenHave((in(x_1, singleton(∅))) /\ ((x === f(x_1))) |- x === f(∅)) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) + thenHave(∃(x_1, in(x_1, singleton(∅)) /\ ((x === f(x_1)))) |- x === f(∅)) by LeftExists + + } + + val s2 = have((x === f(∅)) ==> ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) subproof { + have(x === f(∅) |- (∅ === ∅) /\ (x === f(∅))) by Restate + thenHave(x === f(∅) |- in(∅, singleton(∅)) /\ (x === f(∅))) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) + thenHave(x === f(∅) |- ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) by RightExists + thenHave(thesis) by Restate.from + + } + + have(thesis) by Tautology.from(s1, s2) + } + + val testCollector = Theorem( + ∃(s, ∀(x, in(x, s) <=> (x === f(∅)))) + ) { + val r = singleton(∅).collect(lambda(x, top), f) + + have(in(x, r) <=> (x === f(∅))) by Substitution.ApplyRules(singletonMap)(r.elim(x)) + thenHave(∀(x, in(x, r) <=> (x === f(∅)))) by RightForall + thenHave(thesis) by RightExists + } + + val testMap = Theorem( + ∃(s, ∀(x, in(x, s) <=> (x === f(∅)))) + ) { + val r = singleton(∅).map(f) + have(in(x, r) <=> (x === f(∅))) by Substitution.ApplyRules(singletonMap)(r.elim(x)) + thenHave(∀(x, in(x, r) <=> (x === f(∅)))) by RightForall + thenHave(thesis) by RightExists + } + + val testFilter = Theorem( + ∃(x, Q(x)) |- ∃(z, ∀(t, in(t, z) <=> ∀(y, Q(y) ==> in(t, y)))) + ) { + val s1 = assume(∃(x_1, Q(x_1))) + val x = witness(s1) + val z = x.filter(lambda(t, ∀(y, Q(y) ==> in(t, y)))) + have(∀(y, Q(y) ==> in(t, y)) <=> ((t ∈ x) /\ ∀(y, Q(y) ==> in(t, y)))) by Tableau + have(in(t, z) <=> ∀(y, Q(y) ==> (t ∈ y))) by Substitution.ApplyRules(lastStep)(z.elim(t)) + thenHave(∀(t, in(t, z) <=> ∀(y, Q(y) ==> in(t, y)))) by RightForall + thenHave(thesis) by RightExists + + } + } + assert(ComprehensionsTests.theory.getTheorem("testFilter").nonEmpty) + +} diff --git a/lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala b/lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala new file mode 100644 index 000000000..29826e761 --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala @@ -0,0 +1,188 @@ +package lisa.test.utils + +import lisa.automation.Tautology +import lisa.test.automation.TableauTest +import lisa.utils.K +import lisa.utils.K.getJustification +import lisa.utils.K.|- +import lisa.utils.Serialization.* +import org.scalatest.funsuite.AnyFunSuite + +import java.io._ + +//import lisa.automation.TableauTest + +class SerializationTest extends AnyFunSuite { + + val theory = K.RunningTheory() + + val testfile = "SerializationTestuioavrebvtaevslbxfgh" // chances of collision with an existing file is quite low + + def test(proof: K.SCProof, name: String, theory: K.RunningTheory, justs: List[theory.Justification]) = { + thmsToFile(testfile, theory, List((name, proof, justs.map(("test", _))))) + val thm = thmsFromFile(testfile, theory) + File(testfile + ".trees").delete() + File(testfile + ".proof").delete() + thm.head + } + + def testMulti(theory: K.RunningTheory, thms: List[(String, K.SCProof, List[theory.Justification])]) = { + thmsToFile(testfile, theory, thms.map(thm => (thm._1, thm._2, thm._3.map(("test", _))))) + val thm = thmsFromFile(testfile, theory) + File(testfile + ".trees").delete() + File(testfile + ".proof").delete() + thm + } + + test("exporting a proof to a file and back should work, propositional tableau") { + val proofs = TableauTest.posi + proofs.foreach(p => + try { + val proof = p._4.get + val no = p._1 + test(proof, "posi" + no, theory, Nil) + } catch { + case e: Exception => + println("Exception thrown for test case posi" + p._1) + throw e + } + ) + } + + test("exporting a proof to a file and back should work, propositional OL tautology") { + val proofs = TableauTest.posi + proofs.foreach(p => + try { + val formula = p._2 + val no = p._1 + val proof = Tautology.solveSequent(() |- formula.underlying) match { + case Left(proof) => proof + case Right(_) => throw new Exception("OLPropositionalSolver failed to prove a tautology") + } + test(proof, "posiOL" + no, theory, Nil) + } catch { + case e: Exception => + println("Exception thrown for test case posi" + p._1) + throw e + } + ) + } + + test("exporting a proof to a file and back should work, first order quantifier free tableau") { + val proofs = TableauTest.posqf + proofs.foreach(p => + try { + val proof = p._4.get + val no = p._1 + test(proof, "posqf" + no, theory, Nil) + } catch { + case e: Exception => + println("Exception thrown for test case posqf" + p._1) + throw e + } + ) + } + + test("exporting a proof to a file and back should work, first order quantifier free OL tautology") { + val proofs = TableauTest.posqf + proofs.foreach(p => + try { + val formula = p._2 + val no = p._1 + val proof = Tautology.solveSequent(() |- formula.underlying) match { + case Left(proof) => proof + case Right(_) => throw new Exception("OLPropositionalSolver failed to prove a tautology") + } + test(proof, "posqfOL" + no, theory, Nil) + } catch { + case e: Exception => + println("Exception thrown for test case posqf" + p._1) + throw e + } + ) + } + + test("exporting a proof to a file and back should work, first order easy tableau") { + val proofs = TableauTest.poseasy + proofs.foreach(p => + try { + val proof = p._4.get + val no = p._1 + test(proof, "poseasy" + no, theory, Nil) + } catch { + case e: Exception => + println("Exception thrown for test case poseasy" + p._1) + throw e + } + ) + } + + test("exporting a proof to a file and back should work, first order hard tableau") { + val proofs = TableauTest.poshard + proofs.foreach(p => + try { + val proof = p._4.get + val no = p._1 + test(proof, "poshard" + no, theory, Nil) + } catch { + case e: Exception => + println("Exception thrown for test case poshard" + p._1) + throw e + } + ) + } + + test("exporting a proof to a file and back should work, with imports") { + import lisa.maths.settheory.SetTheory as ST + val thms = List( + // ("russelsParadox", ST.russelsParadox), + ("setUnionMembership", ST.setUnionMembership), + ("inductiveSetExists", ST.inductiveSetExists), + ("setWithNoElementsIsEmpty", ST.setWithNoElementsIsEmpty), + ("emptySetIsItsOwnOnlySubset", ST.emptySetIsItsOwnOnlySubset) + ) + thms.foreach(thm => + try { + val proof = thm._2.kernelProof.get + val justifs = thm._2.highProof.get.justifications.map(_.innerJustification) + + test(proof, thm._1 + "_test", ST.theory, justifs) + } catch { + case e: Exception => + println("Exception thrown for string encoding-decoding of theorem " + thm._1) + throw e + } + ) + } + + test("exporting multiple theorems at once to a file and back should work") { + import lisa.maths.settheory.SetTheory as ST + val thms = List( + // ("russelsParadox", ST.russelsParadox), + ("setUnionMembership", ST.setUnionMembership), + ("inductiveSetExists", ST.inductiveSetExists), + ("setWithNoElementsIsEmpty", ST.setWithNoElementsIsEmpty), + ("emptySetIsItsOwnOnlySubset", ST.emptySetIsItsOwnOnlySubset) + ) + + val thmBack = testMulti( + ST.theory, + thms.map(thm => + val proof = thm._2.kernelProof.get + val justifs = thm._2.highProof.get.justifications.map(_.innerJustification) + + (thm._1, proof, justifs) + ) + ) + assert(thmBack.length == thms.length) + thmBack + .zip(thms) + .foreach(pair => { + val thm = pair._1 + val thmOrig = pair._2 + assert(thm._1.name == thmOrig._1) + assert(thm._1.proposition == thmOrig._2.innerJustification.proposition) + }) + } + +} diff --git a/lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala b/lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala new file mode 100644 index 000000000..b69b8e4ef --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala @@ -0,0 +1,119 @@ +package lisa.automation + +import lisa.automation.Substitution +import lisa.kernel.proof.RunningTheory +import lisa.kernel.proof.SequentCalculus as SC +import lisa.test.ProofTacticTestLib +import lisa.utils.parsing.FOLParser.* +import lisa.utils.parsing.FOLPrinter.* +import org.scalatest.funsuite.AnyFunSuite + +class SubstitutionTacticTest extends ProofTacticTestLib { + /* + // subst with formula list + test("Tactic Tests: Substitution - From theorems and formulas (LR)") { + val correct = List( + // ( + // "sequent before substitution", "sequent after substitution", + // List("substSequent1", "|- p <=> q", ...), List("substFormula1", "p <=> q", "p = q",...) + // ), + ( + "'P('x) |- 'P('x)", + "'P('x); 'x = 'y |- 'P('y)", + List(), + List("'x = 'y") + ), + ( + "'P('x) |- 'P('x)", + "'P('x); 'x = 'y |- 'P('x)", + List(), + List("'x = 'y") + ), + ( + "'P('x) |- 'P('x)", + "'P('x) |- 'P('y)", + List("|- 'x = 'y"), + List() + ), + ( + "'P('x) |- 'P('x)", + "'P('x) |- 'P('x)", + List("|- 'x = 'y"), + List() + ), + ( + "'P('x) |- 'P('x) \\/ 'R('y)", + "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'Q('x)", + List("|- 'x = 'y"), + List("'R('y) <=> 'Q('x)") + ), + ( + "'P('x) |- 'P('x) \\/ 'R('y)", + "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'Q('x)", + List("|- 'x = 'y", "|- 'R('y) <=> 'Q('x)"), + List() + ), + ( + "'P('x) |- 'P('x) \\/ 'R('y)", + "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'R('y)", + List("|- 'x = 'y"), + List("'R('y) <=> 'Q('x)") + ), + ( + "'P('x) |- 'P('x) \\/ 'R('y)", + "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'R('y)", + List("|- 'x = 'y", "|- 'R('y) <=> 'Q('x)"), + List() + ) + ) + + val incorrect = List( + // ( + // "sequent before substitution", "sequent after substitution", + // List("substSequent1", "|- p <=> q", ...), List("substFormula1", "p <=> q", "p = q",...) + // ), + ( + "'P('x); 'Q('z) |- 'P('x)", + "'P('x); 'Q('z); 'x = 'y |- 'P('y)", + List(), + List("'z = 'y") + ), + ( + "'P('x) |- 'P('x)", + "'P('x); 'x = 'y |- 'P('z)", + List(), + List("'x = 'y") + ), + ( + "'P('x); 'Q('z) |- 'P('x)", + "'P('x); 'Q('z) |- 'P('y)", + List("|- 'x = 'z"), + List() + ), + ( + "'P('x); 'Q('y) |- 'P('x)", + "'P('x); 'Q('y) |- 'P('z)", + List("|- 'x = 'y"), + List() + ), + ( + "'P('x) |- 'P('x) \\/ 'R('y)", + "'P('x) |- 'P('z) \\/ 'Q('x)", + List("|- 'x = 'y"), + List("'R('y) <=> 'Q('x)") + ) + ) + + testTacticCases(using testProof)(correct, incorrect) { (stmt1, stmt2, premiseSequents, formSubsts) => + val prem = introduceSequent(using testProof)(stmt1) + val substPrem: Seq[testProof.Fact | Formula | RunningTheory#Justification] = premiseSequents.map(introduceSequent(using testProof)(_)) + val substForm: Seq[testProof.Fact | Formula | RunningTheory#Justification] = formSubsts.map(parseFormula(_)) + val substJust: Seq[testProof.Fact | Formula | RunningTheory#Justification] = Nil + Substitution + .ApplyRules(using lisa.test.TestTheoryLibrary, testProof)( + (substPrem ++ substForm ++ substJust).asInstanceOf[Seq[testProof.Fact | Formula | RunningTheory#Justification]]: _* + )(prem)(lisa.utils.parsing.FOLParser.parseSequent(stmt2)) + } + } + */ +} diff --git a/lisa-topology/src/test/scala/lisa/utilities/TestMain.scala b/lisa-topology/src/test/scala/lisa/utilities/TestMain.scala new file mode 100644 index 000000000..6d93e5d05 --- /dev/null +++ b/lisa-topology/src/test/scala/lisa/utilities/TestMain.scala @@ -0,0 +1,16 @@ +package lisa + +import lisa.prooflib.* + +trait TestMain extends lisa.Main { + + override val om: OutputManager = new OutputManager { + def finishOutput(exception: Exception): Nothing = { + log(exception) + main(Array[String]()) + throw exception + } + val stringWriter: java.io.StringWriter = new java.io.StringWriter() + } + +} From 8b44b70aca9516e61dbdfda898e4ea2aacb124a0 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Thu, 14 Nov 2024 17:54:57 +0100 Subject: [PATCH 02/38] MWE --- build.sbt | 7 + lisa-topology/src/main/scala/lisa/Main.scala | 36 +- .../main/scala/lisa/SetTheoryLibrary.scala | 256 -- .../src/main/scala/lisa/TopologyLibrary.scala | 28 + .../main/scala/lisa/automation/Apply.scala | 274 -- .../scala/lisa/automation/CommonTactics.scala | 262 -- .../scala/lisa/automation/Congruence.scala | 622 ---- .../scala/lisa/automation/Substitution.scala | 641 ---- .../main/scala/lisa/automation/Tableau.scala | 491 ---- .../scala/lisa/automation/Tautology.scala | 257 -- .../scala/lisa/automation/atp/Goeland.scala | 121 - .../settheory/SetTheoryTactics.scala | 200 -- .../main/scala/lisa/maths/Quantifiers.scala | 254 -- .../lisa/maths/settheory/Comprehensions.scala | 201 -- .../lisa/maths/settheory/InductiveSets.scala | 159 - .../lisa/maths/settheory/SetTheory.scala | 2614 ----------------- .../lisa/maths/settheory/SetTheory2.scala | 108 - .../functions/FunctionProperties.scala | 428 --- .../settheory/functions/Functionals.scala | 1365 --------- .../maths/settheory/functions/package.scala | 74 - .../settheory/orderings/InclusionOrders.scala | 207 -- .../maths/settheory/orderings/Induction.scala | 325 -- .../maths/settheory/orderings/Orderings.scala | 5 - .../maths/settheory/orderings/Ordinals.scala | 338 --- .../settheory/orderings/PartialOrders.scala | 356 --- .../maths/settheory/orderings/Recursion.scala | 1943 ------------ .../maths/settheory/orderings/Segments.scala | 225 -- .../settheory/orderings/WellOrders.scala | 140 - .../maths/settheory/types/TypeSystem.scala | 530 ---- .../maths/settheory/types/adt/Frontend.scala | 566 ---- .../maths/settheory/types/adt/Functions.scala | 133 - .../maths/settheory/types/adt/Helpers.scala | 1032 ------- .../maths/settheory/types/adt/Tactics.scala | 174 -- .../maths/settheory/types/adt/Typed.scala | 285 -- .../maths/settheory/types/adt/Untyped.scala | 1747 ----------- .../maths/settheory/types/adt/package.scala | 200 -- .../lisa/automation/CongruenceTest.scala | 913 ------ .../scala/lisa/automation/TableauTest.scala | 158 - .../lisa/examples/peano_example/Peano.scala | 344 --- .../peano_example/PeanoArithmetics.scala | 39 - .../PeanoArithmeticsLibrary.scala | 7 - .../lisa/proven/InitialProofsTests.scala | 20 - .../lisa/utilities/ComprehensionsTests.scala | 83 - .../lisa/utilities/SerializationTest.scala | 188 -- .../utilities/SubstitutionTacticTest.scala | 119 - .../test/scala/lisa/utilities/TestMain.scala | 16 - project/project/project/metals.sbt | 8 + 47 files changed, 44 insertions(+), 18455 deletions(-) delete mode 100644 lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala create mode 100644 lisa-topology/src/main/scala/lisa/TopologyLibrary.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/Apply.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/Congruence.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/Substitution.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/Tableau.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/Tautology.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala delete mode 100644 lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Recursion.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala delete mode 100644 lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala delete mode 100644 lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala delete mode 100644 lisa-topology/src/test/scala/lisa/automation/TableauTest.scala delete mode 100644 lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala delete mode 100644 lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala delete mode 100644 lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala delete mode 100644 lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala delete mode 100644 lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala delete mode 100644 lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala delete mode 100644 lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala delete mode 100644 lisa-topology/src/test/scala/lisa/utilities/TestMain.scala create mode 100644 project/project/project/metals.sbt diff --git a/build.sbt b/build.sbt index 4a3136c13..cb09c4f39 100644 --- a/build.sbt +++ b/build.sbt @@ -75,6 +75,13 @@ lazy val sets = Project( .settings(commonSettings3) .dependsOn(kernel, withTests(utils)) +lazy val topologies = Project( + id = "lisa-topology", + base = file("lisa-topology") +) + .settings(commonSettings3) + .dependsOn(kernel, withTests(utils)) + lazy val utils = Project( id = "lisa-utils", base = file("lisa-utils") diff --git a/lisa-topology/src/main/scala/lisa/Main.scala b/lisa-topology/src/main/scala/lisa/Main.scala index c96ac042b..25dcf2513 100644 --- a/lisa-topology/src/main/scala/lisa/Main.scala +++ b/lisa-topology/src/main/scala/lisa/Main.scala @@ -1,37 +1,3 @@ package lisa -import lisa.SetTheoryLibrary -import lisa.prooflib.BasicMain - -/** - * The parent trait of all theory files containing mathematical development - */ -trait Main extends BasicMain { - - export lisa.fol.FOL.{*, given} - export SetTheoryLibrary.{given, _} - export lisa.prooflib.BasicStepTactic.* - export lisa.prooflib.SimpleDeducedSteps.* - - export lisa.automation.Tautology - export lisa.automation.Substitution - export lisa.automation.Tableau - export lisa.automation.Apply - export lisa.automation.Exact - - knownDefs.update(emptySet, Some(emptySetAxiom)) - knownDefs.update(unorderedPair, Some(pairAxiom)) - knownDefs.update(union, Some(unionAxiom)) - knownDefs.update(powerSet, Some(powerAxiom)) - knownDefs.update(subset, Some(subsetAxiom)) - - extension (symbol: ConstantLabel[?]) { - def definition: JUSTIFICATION = { - getDefinition(symbol).get - } - def shortDefinition: JUSTIFICATION = { - getShortDefinition(symbol).get - } - } - -} +import lisa.TopologyLibrary \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala b/lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala deleted file mode 100644 index addae4c89..000000000 --- a/lisa-topology/src/main/scala/lisa/SetTheoryLibrary.scala +++ /dev/null @@ -1,256 +0,0 @@ -package lisa - -import lisa.fol.FOL.{_, given} -import lisa.kernel.proof.RunningTheory -import lisa.prooflib.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.prooflib.Library { - - val theory = new RunningTheory() - - // Predicates - /** - * The symbol for the set membership predicate. - */ - final val in = ConstantPredicateLabel("elem", 2) - - /** - * The symbol for the subset predicate. - */ - final val subset = ConstantPredicateLabel("subsetOf", 2) - - /** - * The symbol for the equicardinality predicate. Needed for Tarski's axiom. - */ - final val sim = ConstantPredicateLabel("sameCardinality", 2) // Equicardinality - /** - * Set Theory basic predicates - */ - final val predicates = Set(in, subset, sim) - // val choice - - // Functions - /** - * The symbol for the empty set constant. - */ - final val emptySet = Constant("emptySet") - - /** - * The symbol for the unordered pair function. - */ - final val unorderedPair = ConstantFunctionLabel("unorderedPair", 2) - - /** - * The symbol for the powerset function. - */ - final val powerSet = ConstantFunctionLabel("powerSet", 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) - - /** - * Set Theory basic functions. - */ - final val functions = Set(unorderedPair, powerSet, union, universe) - - /** - * The kernel theory loaded with Set Theory symbols and axioms. - */ - // val runningSetTheory: RunningTheory = new RunningTheory() - // given RunningTheory = runningSetTheory - - predicates.foreach(s => addSymbol(s)) - functions.foreach(s => addSymbol(s)) - addSymbol(emptySet) - - private val x = variable - private val y = variable - private val z = variable - final val φ = predicate[1] - private val A = variable - private val B = variable - private val P = predicate[2] - - //////////// - // Axioms // - //////////// - - // Z - //////// - - /** - * Extensionality Axiom --- Two sets are equal iff they have the same - * elements. - * - * `() |- (x = y) ⇔ ∀ z. z ∈ x ⇔ z ∈ y` - */ - final val extensionalityAxiom: this.AXIOM = Axiom(forall(z, in(z, x) <=> in(z, y)) <=> (x === y)) - - /** - * Pairing Axiom --- For any sets `x` and `y`, there is a set that contains - * exactly `x` and `y`. This set is denoted mathematically as `{x, y}` and - * here as `unorderedPair(x, y)`. - * - * `() |- z ∈ {x, y} ⇔ (z === x ∨ z === y)` - * - * This axiom defines [[unorderedPair]] as the function symbol representing - * this set. - */ - final val pairAxiom: AXIOM = Axiom(in(z, unorderedPair(x, y)) <=> (x === z) \/ (y === z)) - - /** - * Comprehension/Separation Schema --- For a formula `ϕ(_, _)` and a set `z`, - * there exists a set `y` which contains only the elements `x` of `z` that - * satisfy `ϕ(x, z)`. This is represented mathematically as `y = {x ∈ z | ϕ(x, - * z)}`. - * - * `() |- ∃ y. ∀ x. x ∈ y ⇔ (x ∈ z ∧ ϕ(x, z))` - * - * This schema represents an infinite collection of axioms, one for each - * formula `ϕ(x, z)`. - */ - final val comprehensionSchema: AXIOM = Axiom(exists(y, forall(x, in(x, y) <=> (in(x, z) /\ φ(x))))) - - /** - * Empty Set Axiom --- From the Comprehension Schema follows the existence of - * a set containing no elements, the empty set. - * - * `∅ = {x ∈ X | x != x}`. - * - * This axiom defines [[emptySet]] as the constant symbol representing this set. - * - * `() |- !(x ∈ ∅)` - */ - final val emptySetAxiom: AXIOM = Axiom(!in(x, emptySet)) - - /** - * Union Axiom --- For any set `x`, there exists a set `union(x)` which is the - * union of its elements. For every element of `union(x)`, there is an element - * `y` of `x` which contains it. - * - * `() |- z ∈ union(x) ⇔ ∃ y. y ∈ x ∧ z ∈ y` - * - * Mathematically, we write `union(x)` as `∪ x`. - * - * This axiom defines [[union]] as the function symbol representing this set. - */ - final val unionAxiom: AXIOM = Axiom(in(z, union(x)) <=> exists(y, in(y, x) /\ in(z, y))) - - /** - * Subset Axiom --- For sets `x` and `y`, `x` is a subset of `y` iff every - * element of `x` is in `y`. Denoted `x ⊆ y`. - * - * `() |- x ⊆ y ⇔ (z ∈ x ⇒ z ∈ y)` - * - * This axiom defines the [[subset]] symbol as this predicate. - */ - final val subsetAxiom: AXIOM = Axiom(subset(x, y) <=> forall(z, in(z, x) ==> in(z, y))) - - /** - * Power Set Axiom --- For a set `x`, there exists a power set of `x`, denoted - * `PP(x)` or `power(x)` which contains every subset of x. - * - * `() |- z ∈ power(x) ⇔ z ⊆ x` - * - * This axiom defines [[powerSet]] as the function symbol representing this - * set. - */ - final val powerAxiom: AXIOM = Axiom(in(x, powerSet(y)) <=> subset(x, y)) - - /** - * Infinity Axiom --- There exists an infinite set. - * - * The definition requires a notion of finiteness, which generally corresponds - * to natural numbers. Since the naturals have not yet been defined, their - * definition and structure is imitated in the definition of an inductive set. - * - * `inductive(x) ⇔ (∅ ∈ x ∧ ∀ y. y ∈ x ⇒ y ∪ {y} ∈ x)` - * - * This axiom postulates that there exists an inductive set. - * - * `() |- ∃ x. inductive(x)` - */ - final val infinityAxiom: AXIOM = Axiom(exists(x, in(emptySet, x) /\ forall(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)))) - - /** - * Foundation/Regularity Axiom --- Every non-empty set `x` has an `∈`-minimal - * element. Equivalently, the relation `∈` on any family of sets is - * well-founded. - * - * `() |- (x != ∅) ==> ∃ y ∈ x. ∀ z. z ∈ x ⇒ ! z ∈ y` - */ - final val foundationAxiom: AXIOM = Axiom(!(x === emptySet) ==> exists(y, in(y, x) /\ forall(z, in(z, x) ==> !in(z, y)))) - - // ZF - ///////// - - /** - * Replacement Schema --- If a predicate `P` is 'functional' over `x`, i.e., - * given `a ∈ x`, there is a unique `b` such that `P(x, a, b)`, then the - * 'image' of `x` in P exists and is a set. It contains exactly the `b`'s that - * satisfy `P` for each `a ∈ x`. - */ - final val replacementSchema: AXIOM = Axiom( - forall(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))) ==> - exists(B, forall(y, in(y, B) <=> exists(x, in(x, A) /\ P(x, y)))) - ) - - final val tarskiAxiom: AXIOM = Axiom( - forall( - x, - in(x, universe(x)) /\ - forall( - y, - in(y, universe(x)) ==> (in(powerSet(y), universe(x)) /\ subset(powerSet(y), universe(x))) /\ - forall(z, subset(z, universe(x)) ==> (sim(y, universe(x)) /\ in(y, universe(x)))) - ) - ) - ) - - /** - * The set of all axioms of Tarski-Grothedick (TG) set theory. - * - * @return - */ - def axioms: Set[(String, AXIOM)] = Set( - ("EmptySet", emptySetAxiom), - ("extensionalityAxiom", extensionalityAxiom), - ("pairAxiom", pairAxiom), - ("unionAxiom", unionAxiom), - ("subsetAxiom", subsetAxiom), - ("powerAxiom", powerAxiom), - ("foundationAxiom", foundationAxiom), - ("infinityAxiom", infinityAxiom), - ("comprehensionSchema", comprehensionSchema), - ("replacementSchema", replacementSchema), - ("TarskiAxiom", tarskiAxiom) - ) - - ///////////// - // Aliases // - ///////////// - - // Unicode symbols - - val ∅ = emptySet - val ∈ = in - - extension (thi: Term) { - def ∈(that: Term): Formula = in(thi, that) - def ⊆(that: Term): Formula = subset(thi, that) - - def =/=(that: Term): Formula = !(thi === that) - - } - -} diff --git a/lisa-topology/src/main/scala/lisa/TopologyLibrary.scala b/lisa-topology/src/main/scala/lisa/TopologyLibrary.scala new file mode 100644 index 000000000..6c920ae76 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/TopologyLibrary.scala @@ -0,0 +1,28 @@ +package lisa + +import lisa.fol.FOL.{_, given} +import lisa.kernel.proof.RunningTheory +import lisa.prooflib.Library + +/** + * Specific implementation of [[utilities.Library]] for Set Theory, with a RunningTheory that is supposed to be used by the standard library. + */ +object TopologyLibrary extends lisa.prooflib.Library { + + val theory = new RunningTheory() + final val T = variable + + // Predicates + /** + * The symbol for the set membership predicate. + */ + final val isTopology = ConstantPredicateLabel("isTopology", 1) + + final val WhatTopologyIs: Formula = forall(T, isTopology(T)) + + private val peanoAxioms: Set[(String, Formula)] = Set( + ("ax1ZeroSuccessor", WhatTopologyIs), + ) + peanoAxioms.foreach(a => theory.addAxiom(a._1, a._2.underlying)) + +} diff --git a/lisa-topology/src/main/scala/lisa/automation/Apply.scala b/lisa-topology/src/main/scala/lisa/automation/Apply.scala deleted file mode 100644 index eb3896284..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/Apply.scala +++ /dev/null @@ -1,274 +0,0 @@ -package lisa.automation - -import lisa.fol.FOL.* -import lisa.prooflib.BasicStepTactic.* -import lisa.prooflib.ProofTacticLib.* -import lisa.prooflib.SimpleDeducedSteps.* -import lisa.prooflib.* -import lisa.utils.unification.UnificationUtils.* - -import scala.util.boundary - -import boundary.break - - -/** - * A tactic object that applies given arguments to a theorem by guessing its good instantiation. - * - * '''Example usage:''' - *{{{ - * val thm = have(transitive(z) |- x ∈ y ==> (y ∈ z ==> x ∈ z)) by ... - * val fact1 = have(a ∈ b) by ... - * val fact2 = have(b ∈ c) by ... - * have(transitive(c) |- a ∈ c) by (Apply(thm) on (fact1, fact2)) - * }}} - * - * where `x`, `y`, `z` are variables and `a`, `b`, `c` are constants. - * - * In some cases, this tactic also guesses the good instantiation of the facts. This feature - * however is not reliable at the moment. - * - * @param lib the library that is being used - * @param proof the ongoing proof object in which the tactic is called - * @param thm the reference theorem on which the arguments are applied. - * - * TODO: reimplement with a Horn clause solver - */ -class Apply(using val lib: Library, val proof: lib.Proof)(thm: proof.Fact) extends ProofTactic { - - /** - * Converts a substitution map to a sequence of [[SubstPair]] - */ - extension (subst: TermSubstitution) def toTSubstPair: Seq[SubstPair] = subst.map((k, v) => SubstPair(k, v)).toSeq - - extension (subst: FormulaSubstitution) def toFSubstPair: Seq[SubstPair] = subst.map((k, v) => SubstPair(k, v)).toSeq - - - /** - * Converts a sequent to a normal form where implications are passed to the left and where conjuctions are split in the premises. - * - * @param seq the sequent to be reduced in normal form - */ - private def normalForm(seq: Sequent): Sequent = - def removeImplies(s: Sequent): Sequent = - if s.right.isEmpty then - s - else - s.right.head match - case AppliedConnector(Implies, Seq(phi, psi)) => removeImplies(s.left + phi |- psi) - case _ => s - - - - def removeConjunctions(s: Sequent): Sequent = - def rec(f: Formula): Set[Formula] = - f match - case AppliedConnector(And, Seq(phi, psi)) => rec(phi) ++ rec(psi) - case _ => Set(f) - s.left.flatMap(rec) |- s.right - - removeConjunctions(removeImplies(seq)) - - /** - * Search the premises of a sequent to find one that is matched by a given formula. - * Performing the resulting substitution inside this premise gives the formula passed as argument. - * - * @param seq the sequent whose premises are references - * @param f the formula to match - * @param takenTVars any term variable in the template which cannot be substituted, i.e., treated as constant - * @param takenFVars any formula variable in the template which cannot be substituted, i.e., treated as constant - * @return a variable assignment such that substituting the variables of seq makes f appear in one of its premises. If no such substitution exists return None. - */ - private def searchPremises(seq: Sequent, f: Formula, takenTVars: Iterable[Variable] = Iterable.empty, takenFVars: Iterable[VariableFormula] = Iterable.empty): Option[(FormulaSubstitution, TermSubstitution)] = - seq.left.foldLeft[Option[(FormulaSubstitution, TermSubstitution)]](None)((assignment, cclPrem) => - assignment match - case None => matchFormula(f, cclPrem, takenTVars, takenFVars) - case _ => assignment - ) - - /** - * Search the premises of a sequent to find one that matches a given formula. - * Performing the resulting substitution inside this formula gives the premise. - * - * @param seq the sequent whose premises are being searched - * @param f the reference formula - * @param takenTVars any term variable in the template which cannot be substituted, i.e., treated as constant - * @param takenFVars any formula variable in the template which cannot be substituted, i.e., treated as constant - * @return a variable assignment such that substituting the variables of f makes f appear in one of seq's premises. If no such substitution exists return None. - */ - private def searchPremisesRev(seq: Sequent, f: Formula, takenTVars: Iterable[Variable] = Iterable.empty, takenFVars: Iterable[VariableFormula] = Iterable.empty): Option[(FormulaSubstitution, TermSubstitution)] = - seq.left.foldLeft[Option[(FormulaSubstitution, TermSubstitution)]](None)((assignment, cclPrem) => - assignment match - case None => matchFormula(cclPrem, f, takenTVars, takenFVars) - case _ => assignment - ) - - /** - * Substitute the variables of a fact with given assignments. - * - * @param proof the ongoing proof object in which the substitution happens - * @param fact the fact whose variables are being substituted - * @param fSubst the assignment for formula variables - * @param tSubst the assignment for term variables - */ - private def substitute(using _proof: lib.Proof)(fact: _proof.Fact, fSubst: FormulaSubstitution, tSubst: TermSubstitution): _proof.Fact = - fact.of(fSubst.toFSubstPair*).of(tSubst.toTSubstPair*) - - /** - * Applies on method with a varargs instead of a sequence. - */ - infix def on(premises: proof.Fact*)(bot: Sequent): proof.ProofTacticJudgement = on(premises.toSeq)(bot) - - - /** - * Executes the tactic. See class description for use cases. - * - * @param premises the facts that are applied to the theorem - * @param excluding the variables that are not to be substituted - * @param bot the sequent the user want to prove - */ - infix def on(premises: Seq[proof.Fact], excluding: Iterable[Variable | VariableFormula] = Set())(bot: Sequent): proof.ProofTacticJudgement = - - if thm == null then - proof.InvalidProofTactic("The theorem is null. Please check that it has been defined earlier in the proof.") - else if premises.exists(_ == null) then - proof.InvalidProofTactic("One of the premises is null. Please check that they all have been defined earlier in the proof.") - else - // STEP 0: Separate the variables and the variable formula in their respective sets - val (excludingTVars, excludingFVars) = excluding.foldLeft[(Set[Variable], Set[VariableFormula])](Set(), Set())((acc, v) => v match - case v: Variable => (acc._1 + v, acc._2) - case v: VariableFormula => (acc._1, acc._2 + v) - ) - - boundary{ - TacticSubproof { sp ?=> - - // STEP 1: Convert the given theorem, facts and sequent to their normal forms - val botNormalized: Sequent = normalForm(bot) - val thmNormalized: sp.Fact = lib.have(normalForm(thm.statement)) by Restate.from(thm) - val premisesNormalized = premises.map(p => lib.have(normalForm(p.statement)) by Restate.from(p)) - - // STEP 2: Unify the conclusion of the sequent the user want to prove and the conclusion - val thmAfterCclUnification: sp.Fact = - (botNormalized.right.isEmpty, thmNormalized.statement.right.isEmpty) match - case (true, true) => thmNormalized - case (true, false) => break(proof.InvalidProofTactic(s"The given theorem could not prove the sequent.")) - case (false, true) => break(proof.InvalidProofTactic(s"The given theorem could not prove the sequent.")) - case (false, false) => - val botCcl: Formula = botNormalized.right.head - val thmCcl: Formula = thmNormalized.statement.right.head - - matchFormula(botCcl, thmCcl, excludingTVars, excludingFVars) match - //Unification succeeded, subtitution can be performed - case Some((formulaCclAssignment, termCclAssignment)) => substitute(thmNormalized, formulaCclAssignment, termCclAssignment) - // STEP 2 failed - case None => break(proof.InvalidProofTactic(s"The conclusion of the goal and the theorem could not be unified.")) - - // STEP 3: Process each fact - val thmAfterPrems: sp.Fact = { - - premisesNormalized.foldLeft(lib.have(thmAfterCclUnification))((updatedThm, premNormalized) => { - - - val premCcl: Formula = premNormalized.statement.right.head - - // STEP 3.1: Unify the conclusion of the fact with a premise of the theorem - // Three possibilities: - // - the unification succeeded and the variables in the theorem are subtituted to match the conclusion of the fact; - // - if the unification did not succeeded, try the unification in the other direction, i.e. try to specify the fact - // instead of the theorem. If this works, make the susbtitution inside the fact; - // - both unifications do not succeed and the fact is dropped. - // TODO: add a warning when the fact is dropped - val conclusionsUnification: Option[(sp.Fact, sp.Fact)] = - searchPremises(updatedThm.statement, premCcl, excludingTVars, excludingFVars) match - case Some((fSubstAfterPrem, tSubstAfterPrem)) => Some((substitute(updatedThm, fSubstAfterPrem, tSubstAfterPrem), premNormalized)) - case None => - searchPremisesRev(updatedThm.statement, premCcl, excludingTVars, excludingFVars) match - case Some((fSubstAfterPrem, tSubstAfterPrem)) => Some((updatedThm, substitute(premNormalized, fSubstAfterPrem, tSubstAfterPrem))) - case None => None - - conclusionsUnification match - case Some(updatedThmAfterCclUnification, premAfterCclUnification) => - - // STEP 3.2: For each premise of the fact: - // - check if it is in the sequent the user want to prove. If yes, add it to the preconditions of the theorem - // using weakening; - // - else if it matches one of the premises of the theorem specify the theorem by using the appropriate sustitution. - // When performing this operation, the conclusion of the fact must be temporarily removed from the premises of the theorem - // to avoid buggy situations in case the fact is of the form p |- p; - // - else add the premise to the premises of the theorem using weakening. - val premCclAfterCclUnification: Formula = premAfterCclUnification.statement.right.head - - val updatedThmAfterWeakening: sp.Fact = - premAfterCclUnification.statement.left.foldLeft(updatedThmAfterCclUnification)((updatedThmDuringWeakening, premPrem) => { - if botNormalized.left.contains(premPrem) then - lib.have(updatedThmDuringWeakening.statement +<< premPrem) by Weakening(updatedThmDuringWeakening) - else - searchPremises(updatedThmDuringWeakening.statement - - substitute(updatedThmDuringWeakening, fSubstDuringWeakening, tSubstDuringWeakening) - case None => - lib.have(updatedThmDuringWeakening.statement +<< premPrem) by Weakening(updatedThmDuringWeakening) - }) - - - // STEP 3.3 Use cut to apply the conclusion of the fact to the theorem - // TODO: maybe throw a warning when the fact cannot be applied instead of making the proof crash - val cutStep: sp.ProofTacticJudgement = Cut(premAfterCclUnification, updatedThmAfterWeakening)(updatedThmAfterWeakening.statement - updatedThm - - }) - } - - // STEP 4: If some cases, after all these operations, the theorem can remain too general to prove the sequent. - // To avoid such situation, perform a last unification between the premises of the sequent and those - // of the theorem. - val thmAfterLastUnification: sp.Fact = botNormalized.left.foldLeft(thmAfterPrems)((updatedThm, premBot) => { - searchPremises(updatedThm.statement, premBot, excludingTVars, excludingFVars) match - case Some(fSubst, tSubst) => substitute(updatedThm, fSubst, tSubst) - case None => updatedThm - }) - - // STEP 5: Prove the sequent using the theorem obtained with the previous steps. Weakening is necessary in case - // additional preconditions that do not appear in the theorem are present in the sequent. - val finalStep: sp.ProofTacticJudgement = Weakening(thmAfterLastUnification)(bot) - if finalStep.isValid then - lib.have(finalStep) - else - break(proof.InvalidProofTactic(s"The given theorem could not prove the sequent.\n Deduced theorem: ${thmAfterLastUnification.statement}\n Expected: ${bot}")) - - - } - } -} - -/** - * Helper that creates an [[Apply]] object. - */ -object Apply { - def apply(using lib: Library, _proof: lib.Proof)(thm: _proof.Fact): Apply{val proof: _proof.type} = (new Apply(using lib, _proof)(thm)).asInstanceOf -} - -/** - * Helper to call the Apply tactic without arguments. When no facts are provided by the user, they can call - * {{{ - * Exact(thm) - * }}} - * as a shortcut for - * {{{ - * Apply(thm).on() - * }}} - * - * TODO: find an appropriate name for this tactic or replace it with Apply - */ -object Exact { - def apply(using lib: Library, _proof: lib.Proof)(thm: _proof.Fact)(bot: Sequent): _proof.ProofTacticJudgement = Apply(thm).on()(bot) -} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala b/lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala deleted file mode 100644 index 2d2b8219d..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/CommonTactics.scala +++ /dev/null @@ -1,262 +0,0 @@ -package lisa.automation.kernel - -import lisa.automation.Tautology -import lisa.fol.FOLHelpers.* -import lisa.fol.FOL as F -import lisa.prooflib.BasicStepTactic.* -import lisa.prooflib.ProofTacticLib.{_, given} -import lisa.prooflib.SimpleDeducedSteps.* -import lisa.prooflib.* -import lisa.utils.K - -object CommonTactics { - - /** - *
-   * Γ |- ∃x. φ, Δ   Γ', φ, φ[y/x] |- x = y, Δ'
-   * -------------------------------------------
-   * Γ, Γ' |- ∃!x. φ, Δ, Δ'
-   * 
- * - * This tactic separates the existence and the uniqueness proofs, which are often easier to prove independently, at - * the expense of brevity. - * - * @see [[RightExistsOne]]. - */ - object ExistenceAndUniqueness extends ProofTactic { - def withParameters(using lib: Library, proof: lib.Proof, om: OutputManager)(phi: F.Formula, x: F.Variable, y: F.Variable)(existence: proof.Fact, uniqueness: proof.Fact)( - bot: F.Sequent - ): proof.ProofTacticJudgement = { - val existenceSeq = proof.getSequent(existence) - val uniquenessSeq = proof.getSequent(uniqueness) - - lazy val substPhi = phi.substitute(x := y) - lazy val existenceFormula = F.∃(x, phi) - lazy val uniqueExistenceFormula = F.∃!(x, phi) - - // Checking that all formulas are present - if (x == y) { - proof.InvalidProofTactic("x and y can not be equal.") - } else if (!F.contains(existenceSeq.right, existenceFormula)) { - proof.InvalidProofTactic(s"Existence sequent conclusion does not contain ∃x. φ.") - } else if (!F.contains(uniquenessSeq.left, phi)) { - proof.InvalidProofTactic("Uniqueness sequent premises do not contain φ.") - } else if (!F.contains(uniquenessSeq.left, substPhi)) { - proof.InvalidProofTactic(s"Uniqueness sequent premises do not contain φ[y/x].") - } else if (!F.contains(uniquenessSeq.right, x === y) && !F.contains(uniquenessSeq.right, y === x)) { - proof.InvalidProofTactic(s"Uniqueness sequent conclusion does not contain x = y") - } else if (!F.contains(bot.right, uniqueExistenceFormula)) { - proof.InvalidProofTactic(s"Bottom sequent conclusion does not contain ∃!x. φ") - } - - // Checking pivots - else if (!F.isSameSet(existenceSeq.left ++ uniquenessSeq.left, bot.left + phi + substPhi)) { - proof.InvalidProofTactic("Could not infer correct left pivots.") - } else if (!F.isSameSet(existenceSeq.right ++ uniquenessSeq.right + uniqueExistenceFormula, bot.right + existenceFormula + (x === y))) { - proof.InvalidProofTactic("Could not infer correct right pivots.") - } else { - val gammaPrime = uniquenessSeq.left.filter(f => !F.isSame(f, phi) && !F.isSame(f, substPhi)) - - TacticSubproof { - // There's got to be a better way of importing have/thenHave/assume methods - // but I did not find one - - val forward = lib.have(phi |- ((x === y) ==> substPhi)) subproof { - lib.assume(phi) - lib.thenHave((x === y) |- substPhi) by RightSubstEq.withParametersSimple(List((x, y)), F.lambda(x, phi)) - lib.thenHave((x === y) ==> substPhi) by Restate - } - - for (f <- gammaPrime) { - lib.assume(f) - } - - val backward = lib.have(phi |- (substPhi ==> (x === y))) by Restate.from(uniqueness) - - lib.have(phi |- ((x === y) <=> substPhi)) by RightIff(forward, backward) - lib.thenHave(phi |- F.∀(y, (x === y) <=> substPhi)) by RightForall - lib.thenHave(phi |- F.∃(x, F.∀(y, (x === y) <=> substPhi))) by RightExists - lib.thenHave(F.∃(x, phi) |- F.∃(x, F.∀(y, (x === y) <=> substPhi))) by LeftExists - lib.thenHave(F.∃(x, phi) |- F.∃!(x, phi)) by RightExistsOne - - lib.have(bot) by Cut(existence, lib.lastStep) - } - } - } - - def apply(using lib: Library, proof: lib.Proof, om: OutputManager)(phi: F.Formula)(existence: proof.Fact, uniqueness: proof.Fact)(bot: F.Sequent): proof.ProofTacticJudgement = { - val existenceSeq = proof.getSequent(existence) - val uniquenessSeq = proof.getSequent(uniqueness) - - // Try to infer x from the premises - // Specifically, find variables in the correct quantifiers, common to all three sequents - val existsVars: Set[F.Variable] = existenceSeq.right.collect { - case F.BinderFormula(F.Exists, x, f) if F.isSame(f, phi) => x - } - if (existsVars.isEmpty) { - return proof.InvalidProofTactic("Missing existential quantifier in the existence sequent.") - } - - val commonVars = bot.right.collect { - case F.BinderFormula(F.ExistsOne, x, f) if F.isSame(f, phi) && existsVars.contains(x) => x - } - if (commonVars.size != 1) { - return proof.InvalidProofTactic("Could not infer correct variable x in quantifiers.") - } - - val x = commonVars.head - - // Infer y from the equalities in the uniqueness sequent - uniquenessSeq.right.collectFirst { - case F.AppliedPredicate(F.`equality`, Seq(`x`, (y: F.Variable))) if x != y && F.contains(uniquenessSeq.left, phi.substitute(x := y)) => - y - - case F.AppliedPredicate(F.`equality`, List(F.AppliedFunctional(y: F.Variable, _), F.AppliedFunctional(`x`, _))) if x != y && F.contains(uniquenessSeq.left, phi.substitute(x := y)) => - y - } match { - case Some(y) => ExistenceAndUniqueness.withParameters(phi, x, y)(existence, uniqueness)(bot) - case None => proof.InvalidProofTactic("Could not infer correct variable y in uniqueness sequent.") - } - } - } - - /** - *
-   *
-   * -------------  if f(xs) = The(y, P(y)) is a function definition
-   * |- P(f(xs))
-   * 
- * Here `xs` is an arbitrary list of parameters. - * - * If `f(xs) = The(y, (φ ==> Q(y)) /\ (!φ ==> (y === t)))` is a conditional function definition, then: - *
-   *
-   * --------------
-   * φ |- Q(f(xs))
-   * 
- */ - object Definition extends ProofTactic { - def apply(using lib: Library, proof: lib.Proof)(f: F.ConstantFunctionLabel[?], uniqueness: proof.Fact)(xs: F.Term*)(bot: F.Sequent): proof.ProofTacticJudgement = { - val expr = lib.getDefinition(f) match { - case Some(value: lib.FunctionDefinition[?]) => value - case _ => return proof.InvalidProofTactic("Could not get definition of function.") - } - val method: (F.ConstantFunctionLabel[?], proof.Fact) => Seq[F.Term] => F.Sequent => proof.ProofTacticJudgement = - expr.f.substituteUnsafe(expr.vars.zip(xs).toMap) match { - case F.AppliedConnector( - F.And, - Seq( - F.AppliedConnector(F.Implies, Seq(a, _)), - F.AppliedConnector(F.Implies, Seq(b, _)) - ) - ) if F.isSame(F.Neg(a), b) => - conditional(using lib, proof) - - case _ => unconditional(using lib, proof) - } - method(f, uniqueness)(xs)(bot) - } - - /** - *
-     *
-     * -------------  if f(xs) = The(y, P(y)) is a function definition
-     * |- P(f(xs))
-     * 
- */ - def unconditional(using lib: Library, proof: lib.Proof)(f: F.ConstantFunctionLabel[?], uniqueness: proof.Fact)(xs: F.Term*)(bot: F.Sequent): proof.ProofTacticJudgement = { - lib.getDefinition(f) match { - case Some(definition: lib.FunctionDefinition[?]) => - if (bot.right.size != 1) { - return proof.InvalidProofTactic("Right-hand side of bottom sequent should contain only 1 formula.") - } - val y = definition.out - val vars = definition.vars - val fxs = f.applyUnsafe(xs) - - // Instantiate terms in the definition - val subst = vars.zip(xs).map(tup => tup._1 := tup._2) - val P = definition.f.substitute(subst*) - val expected = P.substitute(y := fxs) - if (!F.isSame(expected, bot.right.head)) { - return proof.InvalidProofTactic("Right-hand side of bottom sequent should be of the form P(f(xs)).") - } - - TacticSubproof { - lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst*)) - lib.thenHave((y === fxs) <=> P) by InstantiateForall(y) - lib.thenHave((fxs === fxs) <=> P.substitute(y := fxs)) by InstFunSchema(Map(y -> fxs)) - lib.thenHave(P.substitute(y := fxs)) by Restate - } - - case _ => proof.InvalidProofTactic("Could not get definition of function.") - } - } - - /** - *
-     *
-     * -------------- if f(xs) = The(y, (φ ==> Q(y)) /\ (!φ ==> R(y)))
-     * φ |- Q(f(xs))
-     * 
- */ - def conditional(using lib: Library, proof: lib.Proof)(f: F.ConstantFunctionLabel[?], uniqueness: proof.Fact)(xs: F.Term*)(bot: F.Sequent): proof.ProofTacticJudgement = { - lib.getDefinition(f) match { - case Some(definition: lib.FunctionDefinition[?]) => - if (bot.right.size != 1) { - return proof.InvalidProofTactic("Right-hand side of bottom sequent should contain exactly 1 formula.") - } else if (bot.left.isEmpty) { - return proof.InvalidProofTactic("Left-hand side of bottom sequent should not be empty.") - } - val y = definition.out - val vars = definition.vars - - // Extract variable labels to instantiate them later in the proof - // val F.LambdaTermFormula(vars, _) = expr - // val instantiations: Seq[(F.SchematicTermLabel, F.LambdaTermTerm)] = vars.zip(xs.map(x => F.LambdaTermTerm(Seq(), x))) - - val subst = vars.zip(xs).map(tup => tup._1 := tup._2) - val P = definition.f.substitute(subst*) - // Instantiate terms in the definition - // val P = F.LambdaTermFormula(Seq(y), expr(xs)) - - // Unfold the conditional definition to find Q - val phi = F.And(bot.left.toSeq) - val Q: F.LambdaExpression[F.Term, F.Formula, 1] = P.body match { - case F.AppliedConnector( - F.And, - Seq( - F.AppliedConnector(F.Implies, Seq(a, f)), - F.AppliedConnector(F.Implies, Seq(b, g)) - ) - ) if F.isSame(F.Neg(a), b) => - if (F.isSame(a, phi)) F.lambda(y, f) - else if (F.isSame(b, phi)) F.lambda(y, g) - else return proof.InvalidProofTactic("Condition of definition is not satisfied.") - - case _ => - return proof.InvalidProofTactic("Definition is not conditional.") - } - - val fxs = f.applyUnsafe(xs) - - val expected = P.substitute(y := fxs) - if (!F.isSame(expected, bot.right.head)) { - return proof.InvalidProofTactic("Right-hand side of bottom sequent should be of the form Q(fxs).") - } - - TacticSubproof { - lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst*)) - lib.thenHave((y === fxs) <=> P) by InstantiateForall(y) - lib.thenHave((fxs === fxs) <=> P.substitute(y := fxs)) by InstFunSchema(Map(y -> fxs)) - lib.thenHave(P.substitute(y := fxs)) by Restate - lib.thenHave(phi ==> Q(fxs)) by Tautology - lib.thenHave(phi |- Q(fxs)) by Restate - } - - case _ => proof.InvalidProofTactic("Could not get definition of function.") - } - } - } - -} diff --git a/lisa-topology/src/main/scala/lisa/automation/Congruence.scala b/lisa-topology/src/main/scala/lisa/automation/Congruence.scala deleted file mode 100644 index 8c60de410..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/Congruence.scala +++ /dev/null @@ -1,622 +0,0 @@ -package lisa.automation -import lisa.fol.FOL.{*, given} -import lisa.prooflib.BasicStepTactic.* -import lisa.prooflib.ProofTacticLib.* -import lisa.prooflib.SimpleDeducedSteps.* -import lisa.prooflib.* -import lisa.utils.parsing.UnreachableException -import leo.datastructures.TPTP.CNF.AtomicFormula - -/** - * This tactic tries to prove a sequent by congruence. - * Consider the congruence closure of all terms and formulas in the sequent, with respect to all === and <=> left of the sequent. - * The sequent is provable by congruence if one of the following conditions is met: - * - The right side contains an equality s === t or equivalence a <=> b provable in the congruence closure. - * - The left side contains an negated equality !(s === t) or equivalence !(a <=> b) provable in the congruence closure. - * - There is a formula a on the left and b on the right such that a and b are congruent. - * - There are two formulas a and !b on the left such that a and b are congruent. - * - There are two formulas a and !b on the right such that a and b are congruent. - * - The sequent is Ol-valid without equality reasoning - * Note that complete congruence closure modulo OL is an open problem. - * - * The tactic uses an egraph datastructure to compute the congruence closure. - * The egraph itselfs relies on two underlying union-find datastructure, one for terms and one for formulas. - * The union-finds are equiped with an `explain` method that produces a path between any two elements in the same equivalence class. - * Each edge of the path can come from an external equality, or be the consequence of congruence. - * The tactic uses uses this path to produce needed proofs. - * - */ -object Congruence extends ProofTactic with ProofSequentTactic { - def apply(using lib: Library, proof: lib.Proof)(bot: Sequent): proof.ProofTacticJudgement = TacticSubproof { - import lib.* - - val egraph = new EGraphTerms() - egraph.addAll(bot.left) - egraph.addAll(bot.right) - - bot.left.foreach{ - case (left === right) => egraph.merge(left, right) - case (left <=> right) => egraph.merge(left, right) - case _ => () - } - - if isSameSequent(bot, ⊤) then - have(bot) by Restate - else if bot.left.exists { lf => - bot.right.exists { rf => - if egraph.idEq(lf, rf) then - val base = have(bot.left |- (bot.right + lf) ) by Restate - val eq = have(egraph.proveFormula(lf, rf, bot)) - val a = formulaVariable - have((bot.left + (lf <=> rf)) |- (bot.right) ) by RightSubstIff.withParametersSimple(List((lf, rf)), lambda(a, a))(base) - have(bot) by Cut(eq, lastStep) - true - else false - } || - bot.left.exists{ - case rf2 @ Neg(rf) if egraph.idEq(lf, rf)=> - val base = have((bot.left + !lf) |- bot.right ) by Restate - val eq = have(egraph.proveFormula(lf, rf, bot)) - val a = formulaVariable - have((bot.left + (lf <=> rf)) |- (bot.right) ) by LeftSubstIff.withParametersSimple(List((lf, rf)), lambda(a, !a))(base) - have(bot) by Cut(eq, lastStep) - true - case _ => false - } || { - lf match - case !(a === b) if egraph.idEq(a, b) => - have(egraph.proveTerm(a, b, bot)) - true - case !(a <=> b) if egraph.idEq(a, b) => - have(egraph.proveFormula(a, b, bot)) - true - case _ => false - } - - } then () - else if bot.right.exists { rf => - bot.right.exists{ - case lf2 @ Neg(lf) if egraph.idEq(lf, rf)=> - val base = have((bot.left) |- (bot.right + !rf) ) by Restate - val eq = have(egraph.proveFormula(lf, rf, bot)) - val a = formulaVariable - have((bot.left + (lf <=> rf)) |- (bot.right) ) by RightSubstIff.withParametersSimple(List((lf, rf)), lambda(a, !a))(base) - have(bot) by Cut(eq, lastStep) - true - case _ => false - } || { - rf match - case (a === b) if egraph.idEq(a, b) => - have(egraph.proveTerm(a, b, bot)) - true - case (a <=> b) if egraph.idEq(a, b) => - have(egraph.proveFormula(a, b, bot)) - true - case _ => false - } - } then () - else - return proof.InvalidProofTactic(s"No congruence found to show sequent\n $bot") - } - - -} - - -class UnionFind[T] { - // parent of each element, leading to its root. Uses path compression - val parent = scala.collection.mutable.Map[T, T]() - // original parent of each element, leading to its root. Does not use path compression. Used for explain. - val realParent = scala.collection.mutable.Map[T, (T, ((T, T), Boolean, Int))]() - //keep track of the rank (i.e. number of elements bellow it) of each element. Necessary to optimize union. - val rank = scala.collection.mutable.Map[T, Int]() - //tracks order of ancientness of unions. - var unionCounter = 0 - - /** - * add a new element to the union-find. - */ - def add(x: T): Unit = { - parent(x) = x - realParent(x) = (x, ((x, x), true, 0)) - rank(x) = 0 - } - - /** - * - * @param x the element whose parent we want to find - * @return the root of x - */ - def find(x: T): T = { - if parent(x) == x then - x - else - var root = x - while parent(root) != root do - root = parent(root) - var y = x - while parent(y) != root do - parent(y) = root - y = parent(y) - root - } - - /** - * Merges the classes of x and y - */ - def union(x: T, y: T): Unit = { - unionCounter += 1 - val xRoot = find(x) - val yRoot = find(y) - if (xRoot == yRoot) return - if (rank(xRoot) < rank(yRoot)) { - parent(xRoot) = yRoot - realParent(xRoot) = (yRoot, ((x, y), true, unionCounter)) - } else if (rank(xRoot) > rank(yRoot)) { - parent(yRoot) = xRoot - realParent(yRoot) = (xRoot, ((x, y), false, unionCounter)) - } else { - parent(yRoot) = xRoot - realParent(yRoot) = (xRoot, ((x, y), false, unionCounter)) - rank(xRoot) = rank(xRoot) + 1 - } - } - - private def getPathToRoot(x: T): List[T] = { - if x == find(x) then - List(x) - else - val next = realParent(x) - x :: getPathToRoot(next._1) - - } - - private def getExplanationFromTo(x:T, c: T): List[(T, ((T, T), Boolean, Int))] = { - if x == c then - List() - else - val next = realParent(x) - next :: getExplanationFromTo(next._1, c)} - - private def lowestCommonAncestor(x: T, y: T): Option[T] = { - val pathX = getPathToRoot(x) - val pathY = getPathToRoot(y) - pathX.find(pathY.contains) - } - - /** - * Returns a path from x to y made of pairs of elements (u, v) - * such that union(u, v) was called. - */ - def explain(x:T, y:T): Option[List[(T, T)]]= { - - if (x == y) then return Some(List()) - val lca = lowestCommonAncestor(x, y) - lca match - case None => None - case Some(lca) => - var max :((T, T), Boolean, Int) = ((x, x), true, 0) - var itX = x - while itX != lca do - val (next, ((u1, u2), b, c)) = realParent(itX) - if c > max._3 then - max = ((u1, u2), b, c) - itX = next - - var itY = y - while itY != lca do - val (next, ((u1, u2), b, c)) = realParent(itY) - if c > max._3 then - max = ((u1, u2), !b, c) - itY = next - - val u1 = max._1._1 - val u2 = max._1._2 - if max._2 then - Some(explain(x, u1).get ++ List((u1, u2)) ++ explain(u2, y).get) - else - Some(explain(x, u2).get ++ List((u1, u2)) ++ explain(u1, y).get) - } - - - /** - * Returns the set of all roots of all classes - */ - def getClasses: Set[T] = parent.keys.map(find).toSet - - /** - * Add all elements in the collection to the union-find - */ - def addAll(xs: Iterable[T]): Unit = xs.foreach(add) - -} - - -/////////////////////////////// -///////// E-graph ///////////// -/////////////////////////////// - -import scala.collection.mutable - -class EGraphTerms() { - - val termParentsT = mutable.Map[Term, mutable.Set[AppliedFunctional]]() - val termParentsF = mutable.Map[Term, mutable.Set[AppliedPredicate]]() - val termUF = new UnionFind[Term]() - - - val formulaParents = mutable.Map[Formula, mutable.Set[AppliedConnector]]() - val formulaUF = new UnionFind[Formula]() - - - def find(id: Term): Term = termUF.find(id) - def find(id: Formula): Formula = formulaUF.find(id) - - trait TermStep - case class TermExternal(between: (Term, Term)) extends TermStep - case class TermCongruence(between: (Term, Term)) extends TermStep - - trait FormulaStep - case class FormulaExternal(between: (Formula, Formula)) extends FormulaStep - case class FormulaCongruence(between: (Formula, Formula)) extends FormulaStep - - val termProofMap = mutable.Map[(Term, Term), TermStep]() - val formulaProofMap = mutable.Map[(Formula, Formula), FormulaStep]() - - def explain(id1: Term, id2: Term): Option[List[TermStep]] = { - val steps = termUF.explain(id1, id2) - steps.map(_.foldLeft((id1, List[TermStep]())) { - case ((prev, acc), step) => - termProofMap(step) match - case s @ TermExternal((l, r)) => - if l == prev then - (r, s :: acc) - else if r == prev then - (l, TermExternal(r, l) :: acc) - else throw new Exception("Invalid proof recovered: It is not a chain") - case s @ TermCongruence((l, r)) => - if l == prev then - (r, s :: acc) - else if r == prev then - (l, TermCongruence(r, l) :: acc) - else throw new Exception("Invalid proof recovered: It is not a chain") - - }._2.reverse) - } - - def explain(id1: Formula, id2: Formula): Option[List[FormulaStep]] = { - val steps = formulaUF.explain(id1, id2) - steps.map(_.foldLeft((id1, List[FormulaStep]())) { - case ((prev, acc), step) => - formulaProofMap(step) match - case s @ FormulaExternal((l, r)) => - if l == prev then - (r, s :: acc) - else if r == prev then - (l, FormulaExternal(r, l) :: acc) - else throw new Exception("Invalid proof recovered: It is not a chain") - case s @ FormulaCongruence((l, r)) => - if l == prev then - (r, s :: acc) - else if r == prev then - (l, FormulaCongruence(r, l) :: acc) - else throw new Exception("Invalid proof recovered: It is not a chain") - - }._2.reverse) - } - - - def makeSingletonEClass(node:Term): Term = { - termUF.add(node) - termParentsT(node) = mutable.Set() - termParentsF(node) = mutable.Set() - node - } - def makeSingletonEClass(node:Formula): Formula = { - formulaUF.add(node) - formulaParents(node) = mutable.Set() - node - } - - def idEq(id1: Term, id2: Term): Boolean = find(id1) == find(id2) - def idEq(id1: Formula, id2: Formula): Boolean = find(id1) == find(id2) - - - - def canonicalize(node: Term): Term = node match - case AppliedFunctional(label, args) => - AppliedFunctional(label, args.map(t => find(t))) - case _ => node - - - def canonicalize(node: Formula): Formula = { - node match - case AppliedPredicate(label, args) => AppliedPredicate(label, args.map(find)) - case AppliedConnector(label, args) => AppliedConnector(label, args.map(find)) - case node => node - } - - def add(node: Term): Term = - if termUF.parent.contains(node) then return node - makeSingletonEClass(node) - codes(node) = codes.size - node match - case node @ AppliedFunctional(_, args) => - args.foreach(child => - add(child) - termParentsT(find(child)).add(node) - ) - case _ => () - termSigs(canSig(node)) = node - node - - def add(node: Formula): Formula = - if formulaUF.parent.contains(node) then return node - makeSingletonEClass(node) - node match - case node @ AppliedPredicate(_, args) => - args.foreach(child => - add(child) - termParentsF(find(child)).add(node) - ) - node - case node @ AppliedConnector(_, args) => - args.foreach(child => - add(child) - formulaParents(find(child)).add(node) - ) - node - case _ => node - - def addAll(nodes: Iterable[Term|Formula]): Unit = - nodes.foreach{ - case node: Term => add(node) - case node: Formula => add(node) - } - - - - - def merge(id1: Term, id2: Term): Unit = { - mergeWithStep(id1, id2, TermExternal((id1, id2))) - } - def merge(id1: Formula, id2: Formula): Unit = { - mergeWithStep(id1, id2, FormulaExternal((id1, id2))) - } - - type Sig = (TermLabel[?]|Term, List[Int]) - val termSigs = mutable.Map[Sig, Term]() - val codes = mutable.Map[Term, Int]() - - def canSig(node: Term): Sig = node match - case AppliedFunctional(label, args) => - (label, args.map(a => codes(find(a))).toList) - case _ => (node, List()) - - protected def mergeWithStep(id1: Term, id2: Term, step: TermStep): Unit = { - if find(id1) == find(id2) then () - else - termProofMap((id1, id2)) = step - val parentsT1 = termParentsT(find(id1)) - val parentsF1 = termParentsF(find(id1)) - - val parentsT2 = termParentsT(find(id2)) - val parentsF2 = termParentsF(find(id2)) - val preSigs : Map[Term, Sig] = parentsT1.map(t => (t, canSig(t))).toMap - codes(find(id2)) = codes(find(id1)) //assume parents(find(id1)) >= parents(find(id2)) - termUF.union(id1, id2) - val newId = find(id1) - - val formulaSeen = mutable.Map[Formula, AppliedPredicate]() - var formWorklist = List[(Formula, Formula, FormulaStep)]() - var termWorklist = List[(Term, Term, TermStep)]() - - parentsT2.foreach { - case pTerm: AppliedFunctional => - val canonicalPTerm = canSig(pTerm) - if termSigs.contains(canonicalPTerm) then - val qTerm = termSigs(canonicalPTerm) - termWorklist = (pTerm, qTerm, TermCongruence((pTerm, qTerm))) :: termWorklist - else - termSigs(canonicalPTerm) = pTerm - } - (parentsF2 ++ parentsF1).foreach { - case pFormula: AppliedPredicate => - val canonicalPFormula = canonicalize(pFormula) - if formulaSeen.contains(canonicalPFormula) then - val qFormula = formulaSeen(canonicalPFormula) - formWorklist = (pFormula, qFormula, FormulaCongruence((pFormula, qFormula))) :: formWorklist - else - formulaSeen(canonicalPFormula) = pFormula - } - termParentsT(newId) = termParentsT(id1) - termParentsT(newId).addAll(termParentsT(id2)) - termParentsF(newId) = formulaSeen.values.to(mutable.Set) - formWorklist.foreach { case (l, r, step) => mergeWithStep(l, r, step) } - termWorklist.foreach { case (l, r, step) => mergeWithStep(l, r, step) } - } - - protected def mergeWithStep(id1: Formula, id2: Formula, step: FormulaStep): Unit = - if find(id1) == find(id2) then () - else - formulaProofMap((id1, id2)) = step - val newparents = formulaParents(find(id1)) ++ formulaParents(find(id2)) - formulaUF.union(id1, id2) - val newId = find(id1) - - val formulaSeen = mutable.Map[Formula, AppliedConnector]() - var formWorklist = List[(Formula, Formula, FormulaStep)]() - - newparents.foreach { - case pFormula: AppliedConnector => - val canonicalPFormula = canonicalize(pFormula) - if formulaSeen.contains(canonicalPFormula) then - val qFormula = formulaSeen(canonicalPFormula) - formWorklist = (pFormula, qFormula, FormulaCongruence((pFormula, qFormula))) :: formWorklist - //mergeWithStep(pFormula, qFormula, FormulaCongruence((pFormula, qFormula))) - else - formulaSeen(canonicalPFormula) = pFormula - } - formulaParents(newId) = formulaSeen.values.to(mutable.Set) - formWorklist.foreach { case (l, r, step) => mergeWithStep(l, r, step) } - - - def proveTerm(using lib: Library, proof: lib.Proof)(id1: Term, id2:Term, base: Sequent): proof.ProofTacticJudgement = - TacticSubproof { proveInnerTerm(id1, id2, base) } - - def proveInnerTerm(using lib: Library, proof: lib.Proof)(id1: Term, id2:Term, base: Sequent): Unit = { - import lib.* - val steps = explain(id1, id2) - steps match { - case None => throw new Exception("No proof found in the egraph") - case Some(steps) => - if steps.isEmpty then have(base.left |- (base.right + (id1 === id2))) by Restate - steps.foreach { - case TermExternal((l, r)) => - val goalSequent = base.left |- (base.right + (id1 === r)) - if l == id1 then - have(goalSequent) by Restate - else - val x = freshVariable(id1) - have(goalSequent) by RightSubstEq.withParametersSimple(List((l, r)), lambda(x, id1 === x))(lastStep) - case TermCongruence((l, r)) => - val prev = if id1 != l then lastStep else null - val leqr = have(base.left |- (base.right + (l === r))) subproof { sp ?=> - (l, r) match - case (AppliedFunctional(labell, argsl), AppliedFunctional(labelr, argsr)) if labell == labelr && argsl.size == argsr.size => - var freshn = freshId((l.freeVariables ++ r.freeVariables).map(_.id), "n").no - val ziped = (argsl zip argsr) - var zip = List[(Term, Term)]() - var children = List[Term]() - var vars = List[Variable]() - var steps = List[(Formula, sp.ProofStep)]() - ziped.reverse.foreach { (al, ar) => - if al == ar then children = al :: children - else { - val x = Variable(Identifier("n", freshn)) - freshn = freshn + 1 - children = x :: children - vars = x :: vars - steps = (al === ar, have(proveTerm(al, ar, base))) :: steps - zip = (al, ar) :: zip - } - } - have(base.left |- (base.right + (l === l))) by Restate - val eqs = zip.map((l, r) => l === r) - val goal = have((base.left ++ eqs) |- (base.right + (l === r))).by.bot - have((base.left ++ eqs) |- (base.right + (l === r))) by RightSubstEq.withParametersSimple(zip, lambda(vars, l === labelr.applyUnsafe(children)))(lastStep) - steps.foreach { s => - have( - if s._2.bot.left.contains(s._1) then lastStep.bot else lastStep.bot -<< s._1 - ) by Cut(s._2, lastStep) - } - case _ => - println(s"l: $l") - println(s"r: $r") - throw UnreachableException - - } - if id1 != l then - val goalSequent = base.left |- (base.right + (id1 === r)) - val x = freshVariable(id1) - have(goalSequent +<< (l === r)) by RightSubstEq.withParametersSimple(List((l, r)), lambda(x, id1 === x))(prev) - have(goalSequent) by Cut(leqr, lastStep) - } - } - } - - def proveFormula(using lib: Library, proof: lib.Proof)(id1: Formula, id2:Formula, base: Sequent): proof.ProofTacticJudgement = - TacticSubproof { proveInnerFormula(id1, id2, base) } - - def proveInnerFormula(using lib: Library, proof: lib.Proof)(id1: Formula, id2:Formula, base: Sequent): Unit = { - import lib.* - val steps = explain(id1, id2) - steps match { - case None => throw new Exception("No proof found in the egraph") - case Some(steps) => - if steps.isEmpty then have(base.left |- (base.right + (id1 <=> id2))) by Restate - steps.foreach { - case FormulaExternal((l, r)) => - val goalSequent = base.left |- (base.right + (id1 <=> r)) - if l == id1 then - have(goalSequent) by Restate - else - val x = freshVariableFormula(id1) - have(goalSequent) by RightSubstIff.withParametersSimple(List((l, r)), lambda(x, id1 <=> x))(lastStep) - case FormulaCongruence((l, r)) => - val prev = if id1 != l then lastStep else null - val leqr = have(base.left |- (base.right + (l <=> r))) subproof { sp ?=> - (l, r) match - case (AppliedConnector(labell, argsl), AppliedConnector(labelr, argsr)) if labell == labelr && argsl.size == argsr.size => - var freshn = freshId((l.freeVariableFormulas ++ r.freeVariableFormulas).map(_.id), "n").no - val ziped = (argsl zip argsr) - var zip = List[(Formula, Formula)]() - var children = List[Formula]() - var vars = List[VariableFormula]() - var steps = List[(Formula, sp.ProofStep)]() - ziped.reverse.foreach { (al, ar) => - if al == ar then children = al :: children - else { - val x = VariableFormula(Identifier("n", freshn)) - freshn = freshn + 1 - children = x :: children - vars = x :: vars - steps = (al <=> ar, have(proveFormula(al, ar, base))) :: steps - zip = (al, ar) :: zip - } - } - have(base.left |- (base.right + (l <=> l))) by Restate - val eqs = zip.map((l, r) => l <=> r) - val goal = have((base.left ++ eqs) |- (base.right + (l <=> r))).by.bot - have((base.left ++ eqs) |- (base.right + (l <=> r))) by RightSubstIff.withParametersSimple(zip, lambda(vars, l <=> labelr.applyUnsafe(children)))(lastStep) - steps.foreach { s => - have( - if s._2.bot.left.contains(s._1) then lastStep.bot else lastStep.bot -<< s._1 - ) by Cut(s._2, lastStep) - } - - case (AppliedPredicate(labell, argsl), AppliedPredicate(labelr, argsr)) if labell == labelr && argsl.size == argsr.size => - var freshn = freshId((l.freeVariableFormulas ++ r.freeVariableFormulas).map(_.id), "n").no - val ziped = (argsl zip argsr) - var zip = List[(Term, Term)]() - var children = List[Term]() - var vars = List[Variable]() - var steps = List[(Formula, sp.ProofStep)]() - ziped.reverse.foreach { (al, ar) => - if al == ar then children = al :: children - else { - val x = Variable(Identifier("n", freshn)) - freshn = freshn + 1 - children = x :: children - vars = x :: vars - steps = (al === ar, have(proveTerm(al, ar, base))) :: steps - zip = (al, ar) :: zip - } - } - have(base.left |- (base.right + (l <=> l))) by Restate - val eqs = zip.map((l, r) => l === r) - val goal = have((base.left ++ eqs) |- (base.right + (l <=> r))).by.bot - have((base.left ++ eqs) |- (base.right + (l <=> r))) by RightSubstEq.withParametersSimple(zip, lambda(vars, l <=> labelr.applyUnsafe(children)))(lastStep) - steps.foreach { s => - have( - if s._2.bot.left.contains(s._1) then lastStep.bot else lastStep.bot -<< s._1 - ) by Cut(s._2, lastStep) - } - case _ => - println(s"l: $l") - println(s"r: $r") - throw UnreachableException - - } - if id1 != l then - val goalSequent = base.left |- (base.right + (id1 <=> r)) - val x = freshVariableFormula(id1) - have(goalSequent +<< (l <=> r)) by RightSubstIff.withParametersSimple(List((l, r)), lambda(x, id1 <=> x))(prev) - have(goalSequent) by Cut(leqr, lastStep) - - } - } - } - - -} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/automation/Substitution.scala b/lisa-topology/src/main/scala/lisa/automation/Substitution.scala deleted file mode 100644 index 87eab43cd..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/Substitution.scala +++ /dev/null @@ -1,641 +0,0 @@ -package lisa.automation -import lisa.fol.FOL as F -import lisa.kernel.proof.RunningTheory -import lisa.kernel.proof.SCProof -import lisa.kernel.proof.SequentCalculus -import lisa.prooflib.BasicStepTactic.* -import lisa.prooflib.ProofTacticLib.{_, given} -import lisa.prooflib.* -import lisa.utils.FOLPrinter -import lisa.utils.K -import lisa.utils.UserLisaException -import lisa.utils.parsing.FOLPrinter -import lisa.utils.unification.UnificationUtils -import lisa.utils.unification.UnificationUtils.getContextFormulaSet - -import scala.annotation.nowarn -import scala.collection.mutable.{Map as MMap} - -import F.{*, given} -import F.|- - -object Substitution { - def validRule(using lib: lisa.prooflib.Library, proof: lib.Proof)(r: (proof.Fact | F.Formula | lib.JUSTIFICATION)): Boolean = - r match { - case F.equality(_, _) => true - case F.Iff(_, _) => true - case _: Formula => false - case j: lib.JUSTIFICATION => j.statement.right.size == 1 && validRule(j.statement.right.head) - case f: proof.Fact @unchecked => proof.sequentOfFact(f).right.size == 1 && validRule(proof.sequentOfFact(f).right.head) - // case j: RunningTheory#Justification => - // proof.sequentOfFact(j.asInstanceOf[lib.theory.Justification]).right.size == 1 && validRule(proof.sequentOfFact(j.asInstanceOf[lib.theory.Justification]).right.head) - } - - object ApplyRules extends ProofTactic { - - def apply(using lib: lisa.prooflib.Library, proof: lib.Proof)(substitutions: (proof.Fact | F.Formula | lib.JUSTIFICATION)*)( - premise: proof.Fact - )(bot: F.Sequent): proof.ProofTacticJudgement = { - // figure out instantiations for rules - // takes a premise - val premiseSequent: F.Sequent = proof.getSequent(premise) - - // make sure substitutions are all valid - val violatingSubstitutions = substitutions.collect { - case f : proof.Fact @unchecked if !validRule(f) => proof.sequentOfFact(f) - case j: lib.JUSTIFICATION if !validRule(j) => j.statement - } - - val violatingFormulas = substitutions.collect { - case f: F.Formula if !validRule(f) => f - } - - if (!violatingSubstitutions.isEmpty) - // return error - proof.InvalidProofTactic("Substitution rules must have a single equality or equivalence on the right-hand side. Violating sequents passed:\n" + violatingSubstitutions.zipWithIndex.map { - (s, i) => - s"${i + 1}. ${s.toString}" - }) - else if (!violatingFormulas.isEmpty) - proof.InvalidProofTactic("Substitution rules must be equalities or equivalences. Violating formulas passed:\n" + violatingFormulas.zipWithIndex.map { (s, i) => - s"${i + 1}. ${s.toString}" - }) - else { - // proceed as usual - - // maintain a list of where subtitutions come from - val sourceOf: MMap[(F.Formula, F.Formula) | (F.Term, F.Term), proof.Fact] = MMap() - val takenTermVars: Set[lisa.fol.FOL.Variable] = - premiseSequent.left.flatMap(_.freeVariables).toSet union substitutions.collect { case f: F.Formula => f.freeVariables.toSet }.foldLeft(Set.empty)(_.union(_)) - val takenFormulaVars: Set[lisa.fol.FOL.VariableFormula] = premiseSequent.left.flatMap(_.freeVariableFormulas).toSet union substitutions - .collect { case f: F.Formula => f.freeVariableFormulas.toSet } - .foldLeft(Set.empty)(_.union(_)) // TODO: should this just be the LHS of the premise sequent instead? - - var freeEqualitiesPre = List[(F.Term, F.Term)]() - var confinedEqualitiesPre = List[(F.Term, F.Term)]() - var freeIffsPre = List[(F.Formula, F.Formula)]() - var confinedIffsPre = List[(F.Formula, F.Formula)]() - - def updateSource(t: (F.Formula, F.Formula) | (F.Term, F.Term), f: proof.Fact) = { - sourceOf.update(t, f) - sourceOf.update(t.swap.asInstanceOf[(F.Formula, F.Formula) | (F.Term, F.Term)], f) - } - - // collect substitutions into the right buckets - substitutions.foreach { - case f: F.Formula => - f match { - case F.AppliedPredicate(F.equality, Seq(l, r)) => - confinedEqualitiesPre = (l, r) :: confinedEqualitiesPre - case F.AppliedConnector(F.Iff, Seq(l, r)) => - confinedIffsPre = (l, r) :: confinedIffsPre - case _ => () - } - case j: lib.JUSTIFICATION => - j.statement.right.head match { - case F.AppliedPredicate(F.equality, Seq(l, r)) => - updateSource((l, r), j) - freeEqualitiesPre = (l, r) :: freeEqualitiesPre - case F.AppliedConnector(F.Iff, Seq(l, r)) => - updateSource((l, r), j) - freeIffsPre = (l, r) :: freeIffsPre - case _ => () - } - case f: proof.Fact @unchecked => - proof.sequentOfFact(f).right.head match { - case F.AppliedPredicate(F.equality, Seq(l, r)) => - updateSource((l, r), f) - confinedEqualitiesPre = (l, r) :: confinedEqualitiesPre - case F.AppliedConnector(F.Iff, Seq(l, r)) => - updateSource((l, r), f) - confinedIffsPre = (l, r) :: confinedIffsPre - case _ => () - } - } - - // get the original and swapped versions - val freeEqualities: List[(F.Term, F.Term)] = freeEqualitiesPre ++ freeEqualitiesPre.map(_.swap) - val confinedEqualities: List[(F.Term, F.Term)] = confinedEqualitiesPre ++ confinedEqualitiesPre.map(_.swap) - val freeIffs: List[(F.Formula, F.Formula)] = freeIffsPre ++ freeIffsPre.map(_.swap) - val confinedIffs: List[(F.Formula, F.Formula)] = confinedIffsPre ++ confinedIffsPre.map(_.swap) - - val filteredPrem: Seq[F.Formula] = (premiseSequent.left filter { - case F.AppliedPredicate(F.equality, Seq(l, r)) if freeEqualities.contains((l, r)) || confinedEqualities.contains((l, r)) => false - case F.AppliedConnector(F.Iff, Seq(l, r)) if freeIffs.contains((l, r)) || confinedIffs.contains((l, r)) => false - case _ => true - }).toSeq - - val filteredBot: Seq[F.Formula] = (bot.left filter { - case F.AppliedPredicate(F.equality, Seq(l, r)) if freeEqualities.contains((l, r)) || confinedEqualities.contains((l, r)) => false - case F.AppliedConnector(F.Iff, Seq(l, r)) if freeIffs.contains((l, r)) || confinedIffs.contains((l, r)) => false - case _ => true - }).toSeq - - // construct the right instantiations - lazy val leftContextsOpt: Option[Seq[UnificationUtils.FormulaRewriteLambda]] = getContextFormulaSet( - filteredPrem, - filteredBot, - freeEqualities, - freeIffs, - confinedEqualities, - takenTermVars, - confinedIffs, - takenFormulaVars - ) - - lazy val rightContextsOpt: Option[Seq[UnificationUtils.FormulaRewriteLambda]] = getContextFormulaSet( - premiseSequent.right.toSeq, - bot.right.toSeq, - freeEqualities, - freeIffs, - confinedEqualities, - takenTermVars, - confinedIffs, - takenFormulaVars - ) - - lazy val rightPairs = premiseSequent.right zip premiseSequent.right.map(x => - bot.right.find(y => - UnificationUtils - .getContextFormula( - x, - y, - freeEqualities, - freeIffs, - confinedEqualities, - takenTermVars, - confinedIffs, - takenFormulaVars - ) - .isDefined - ) - ) - - lazy val leftPairs = filteredPrem zip filteredPrem.map(x => - filteredBot.find(y => - UnificationUtils - .getContextFormula( - x, - y, - freeEqualities, - freeIffs, - confinedEqualities, - takenTermVars, - confinedIffs, - takenFormulaVars - ) - .isDefined - ) - ) - - lazy val violatingFormulaLeft = leftPairs.find(_._2.isEmpty) - lazy val violatingFormulaRight = rightPairs.find(_._2.isEmpty) - - if (leftContextsOpt.isEmpty) - proof.InvalidProofTactic(s"Could not rewrite LHS of premise into conclusion with given substitutions.\nViolating Formula: ${violatingFormulaLeft.get}") - else if (rightContextsOpt.isEmpty) - proof.InvalidProofTactic(s"Could not rewrite RHS of premise into conclusion with given substitutions.\nViolating Formula: ${violatingFormulaRight.get}") - else { - // actually construct proof - TacticSubproof { - - def eq(rule: (Term, Term)) = AppliedPredicate(equality, Seq(rule._1, rule._2)) - def iff(rule: (Formula, Formula)) = AppliedConnector(Iff, Seq(rule._1, rule._2)) - - def eqSource(rule: (Term, Term)) = lib.have(eq(rule) |- eq(rule)) by SimpleDeducedSteps.Restate - def iffSource(rule: (Formula, Formula)) = lib.have(iff(rule) |- iff(rule)) by SimpleDeducedSteps.Restate - val leftContexts: Seq[UnificationUtils.FormulaRewriteLambda] = leftContextsOpt.get // remove the options - val rightContexts: Seq[UnificationUtils.FormulaRewriteLambda] = rightContextsOpt.get // remove the options - - val leftBody = AppliedConnector(And, leftContexts.map(f => f.body)) - - val defaultLeft = UnificationUtils.FormulaRewriteLambda(body = leftBody) - - val leftContextReduced = leftContexts.foldLeft(defaultLeft) { (f, s) => - UnificationUtils.FormulaRewriteLambda( - termRules = f.termRules ++ s.termRules, - formulaRules = f.formulaRules ++ s.formulaRules, - leftBody - ) - } - - val rightBody = AppliedConnector(Or, rightContexts.map(f => f.body)) - - val defaultRight = UnificationUtils.FormulaRewriteLambda(body = rightBody) - - val rightContextReduced = rightContexts.foldLeft(defaultRight) { (f, s) => - UnificationUtils.FormulaRewriteLambda( - termRules = f.termRules ++ s.termRules, - formulaRules = f.formulaRules ++ s.formulaRules, - rightBody - ) - } - - // find the justifications for each rule, or generate them, as required - val leftDischarges = - leftContextReduced.termRules.map { case (_, (rule, subst)) => - sourceOf.get(rule) match { - case Some(f: proof.Fact) => - f.of(subst.toSeq.map((l, r) => (l := r))*) - // case Some(j: lib.theory.Justification) => - // j.of(subst.toSeq.map((l, r) => (l, lambda(Seq(), r))): _*) - case _ => - eqSource(rule).of() - } - } ++ - leftContextReduced.formulaRules.map { case (_, (rule, subst)) => - sourceOf.get(rule) match { - case Some(f: proof.Fact) => - f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r))*) - // case Some(j: lib.theory.Justification) => - // j.of(subst._1.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))) ++ subst._2.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))): _*) - case _ => - iffSource(rule).of() - } - } - val rightDischarges = - rightContextReduced.termRules.map { case (_, (rule, subst)) => - sourceOf.get(rule) match { - case Some(f: proof.Fact) => - f.of(subst.toSeq.map((l, r) => (l := r))*) - // case Some(j: lib.theory.Justification) => - // j.of(subst.toSeq.map((l, r) => (l, lambda(Seq(), r))): _*) - case None => - eqSource(rule).of() - } - } ++ - rightContextReduced.formulaRules.map { case (_, (rule, subst)) => - sourceOf.get(rule) match { - case Some(f: proof.Fact) => - f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r))*) - // case Some(j: lib.theory.Justification) => - // j.of(subst._1.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))) ++ subst._2.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))): _*) - case None => - iffSource(rule).of() - } - } - - val discharges = leftDischarges ++ rightDischarges - // ------------------- - // LEFT SUBSTITUTIONS - // ------------------- - val nextSequent = { - // we have a lambda like λx. Λp. body - // where the p are formula variables, and the x are term variables - val ctx = leftContextReduced - - val termVars = ctx.termRules.map(_._1) - - val termInputs = ctx.termRules.map { case (_, (rule: (Term, Term), subst: UnificationUtils.TermSubstitution)) => - ( - rule._1.substituteUnsafe2(subst), - rule._2.substituteUnsafe2(subst) - ) - } - - lazy val (termInputsL, termInputsR) = (termInputs.map(_._1), termInputs.map(_._2)) - - val formulaVars = ctx.formulaRules.map(_._1) - - val formulaInputs = ctx.formulaRules.map { case (_, (rule, subst)) => - ( - rule._1.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1), - rule._2.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1) - ) - } - val (formulaInputsL, formulaInputsR) = (formulaInputs.map(_._1), formulaInputs.map(_._2)) - - // get premise into the right form - val prem = AppliedConnector(And, filteredPrem.toSeq) |- AppliedConnector(Or, premiseSequent.right.toSeq) - val eqs = termInputs.map(eq(_)) - val iffs = formulaInputs.map(iff(_)) - val premiseWithSubst = prem ++<< (eqs |- ()) ++<< (iffs |- ()) - lib.have(premiseWithSubst) by BasicStepTactic.Weakening(premise) - - // left === - val eqSubst = Map((termVars zip termInputsR)*) - val formSubstL = Map((formulaVars zip formulaInputsL)*) - val lhsAfterEq = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstL) - val sequentAfterEqPre = lhsAfterEq |- premiseWithSubst.right - val sequentAfterEq = sequentAfterEqPre ++<< (eqs |- ()) ++<< (iffs |- ()) - - // this uses the "lambda" (λx. Λp. body) (p = left formulas) - lib.thenHave(sequentAfterEq) by BasicStepTactic.LeftSubstEq.withParametersSimple(termInputs.toList, lambda(termVars, ctx.body.substituteUnsafe2(formSubstL))) - - // left <=> - val formSubstR = Map((formulaVars zip formulaInputsR)*) - val lhsAfterIff = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstR) - val sequentAfterIffPre = lhsAfterIff |- sequentAfterEq.right - val sequentAfterIff = sequentAfterIffPre ++<< (eqs |- ()) ++<< (iffs |- ()) - - // this uses the "lambda" (λx. Λp. body) (x = right terms) - lib.thenHave(sequentAfterIff) by BasicStepTactic.LeftSubstIff.withParametersSimple(formulaInputs.toList, lambda(formulaVars, ctx.body.substituteUnsafe2(eqSubst))) - sequentAfterIff - } - - // ------------------- - // RIGHT SUBSTITUTIONS - // ------------------- - val finalSequent = { - // we have a lambda like λx. Λp. body - // where the p are formula variables, and the x are term variables - val ctx = rightContextReduced - - val termVars = ctx.termRules.map(_._1) - - val termInputs = ctx.termRules.map { case (_, (rule, subst)) => - ( - rule._1.substituteUnsafe2(subst), - rule._2.substituteUnsafe2(subst) - ) - } - - lazy val (termInputsL, termInputsR) = (termInputs.map(_._1), termInputs.map(_._2)) - - val formulaVars = ctx.formulaRules.map(_._1) - - val formulaInputs = ctx.formulaRules.map { case (_, (rule, subst)) => - ( - rule._1.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1), - rule._2.substituteUnsafe2(subst._2).substituteUnsafe2(subst._1) - ) - } - val (formulaInputsL, formulaInputsR) = (formulaInputs.map(_._1), formulaInputs.map(_._2)) - - // get premise into the right form - val prem = nextSequent - val eqs = termInputs.map(eq(_)) - val iffs = formulaInputs.map(iff(_)) - val premiseWithSubst = prem ++<< (eqs |- ()) ++<< (iffs |- ()) - lib.thenHave(premiseWithSubst) by BasicStepTactic.Weakening - - // right === - val eqSubst = Map((termVars zip termInputsR)*) - val formSubstL = Map((formulaVars zip formulaInputsL)*) - val rhsAfterEq = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstL) - val sequentAfterEqPre = premiseWithSubst.left |- rhsAfterEq - val sequentAfterEq = sequentAfterEqPre ++<< (eqs |- ()) ++<< (iffs |- ()) - - // this uses the "lambda" (λx. Λp. body) (p = left formulas) - lib.thenHave(sequentAfterEq) by BasicStepTactic.RightSubstEq.withParametersSimple(termInputs.toList, lambda(termVars, ctx.body.substituteUnsafe2(formSubstL))) - - // right <=> - val formSubstR = Map((formulaVars zip formulaInputsR)*) - val rhsAfterIff = ctx.body.substituteUnsafe2(eqSubst).substituteUnsafe2(formSubstR) - val sequentAfterIffPre = sequentAfterEq.left |- rhsAfterIff - val sequentAfterIff = sequentAfterIffPre ++<< (eqs |- ()) ++<< (iffs |- ()) - - // this uses the "lambda" (λx. Λp. body) (x = right terms) - lib.thenHave(sequentAfterIff) by BasicStepTactic.RightSubstIff.withParametersSimple(formulaInputs.toList, lambda(formulaVars, ctx.body.substituteUnsafe2(eqSubst))) - - } - // discharge any assumptions - - // custom discharge - // invariant: all facts are known to have only one formula in their RHS - discharges.foreach { f => - lib.thenHave(lib.lastStep.bot +<< f.result.right.head) by BasicStepTactic.Weakening // in case of double discharges, add the formula back in - lib.have(lib.lastStep.bot - isSameTerm(t, s._2)) - if (eq.nonEmpty) (eq.get._1, true) - else { - val induct = condflat(t.args.map(te => findSubterm2(te, subs))) - if (!induct._2) (t, false) - else - (t.label.applySeq(induct._1), true) - - } - - } - private def findSubterm2(f: Formula, subs: Seq[(Variable, Term)]): (Formula, Boolean) = { - f match { - case f: VariableFormula => (f, false) - case f: ConstantFormula => (f, false) - case AppliedPredicate(label, args) => - val induct = condflat(args.map(findSubterm2(_, subs))) - if (!induct._2) (f, false) - else (AppliedPredicate(label, induct._1), true) - case AppliedConnector(label, args) => - val induct = condflat(args.map(findSubterm2(_, subs))) - if (!induct._2) (f, false) - else (AppliedConnector(label, induct._1), true) - case BinderFormula(label, bound, inner) => - val fv_in_f = subs.flatMap(e => e._2.freeVariables + e._1) - if (!fv_in_f.contains(bound)) { - val induct = findSubterm2(inner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, bound, induct._1), true) - } else { - val newv = Variable(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) - val newInner = inner.substitute(bound := newv) - val induct = findSubterm2(newInner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, newv, induct._1), true) - } - } - } - - private def findSubformula2(f: Formula, subs: Seq[(VariableFormula, Formula)]): (Formula, Boolean) = { - val eq = subs.find(s => isSame(f, s._2)) - if (eq.nonEmpty) (eq.get._1, true) - else - f match { - case f: AtomicFormula => (f, false) - case AppliedConnector(label, args) => - val induct = condflat(args.map(findSubformula2(_, subs))) - if (!induct._2) (f, false) - else (AppliedConnector(label, induct._1), true) - case BinderFormula(label, bound, inner) => - val fv_in_f = subs.flatMap(_._2.freeVariables) - if (!fv_in_f.contains(bound)) { - val induct = findSubformula2(inner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, bound, induct._1), true) - } else { - val newv = Variable(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) - val newInner = inner.substitute(bound := newv) - val induct = findSubformula2(newInner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, newv, induct._1), true) - } - } - } - - def findSubterm(t: Term, subs: Seq[(Variable, Term)]): Option[LambdaExpression[Term, Term, ?]] = { - val vars = subs.map(_._1) - val r = findSubterm2(t, subs) - if (r._2) Some(LambdaExpression(vars, r._1, vars.size)) - else None - } - - def findSubterm(f: Formula, subs: Seq[(Variable, Term)]): Option[LambdaExpression[Term, Formula, ?]] = { - val vars = subs.map(_._1) - val r = findSubterm2(f, subs) - if (r._2) Some(LambdaExpression(vars, r._1, vars.size)) - else None - } - - def findSubformula(f: Formula, subs: Seq[(VariableFormula, Formula)]): Option[LambdaExpression[Formula, Formula, ?]] = { - val vars = subs.map(_._1) - val r = findSubformula2(f, subs) - if (r._2) Some(LambdaExpression(vars, r._1, vars.size)) - else None - } - - def applyLeftRight(using lib: lisa.prooflib.Library, proof: lib.Proof)( - phi: Formula - )(premise: proof.Fact)(rightLeft: Boolean = false, toLeft: Boolean = true, toRight: Boolean = true): proof.ProofTacticJudgement = { - import lisa.utils.K - val originSequent = proof.getSequent(premise) - val leftOrigin = AppliedConnector(And, originSequent.left.toSeq) - val rightOrigin = AppliedConnector(Or, originSequent.right.toSeq) - - if (!toLeft && !toRight) return proof.InvalidProofTactic("applyLeftRight called with no substitution selected (toLeft or toRight).") - - phi match { - case AppliedPredicate(label, args) if label == equality => - val left = args(0) - val right = args(1) - val fv_in_phi = (originSequent.left ++ originSequent.right).flatMap(_.allSchematicLabels).map(_.id) - val v = Variable(nFreshId(fv_in_phi, 1).head) - lazy val isolatedLeft = originSequent.left.filterNot(f => isSame(f, phi)).map(f => (f, findSubterm(f, IndexedSeq(v -> left)))) - lazy val isolatedRight = originSequent.right.map(f => (f, findSubterm(f, IndexedSeq(v -> left)))) - if ((!toLeft || isolatedLeft.forall(_._2.isEmpty)) && (!toRight || isolatedRight.forall(_._2.isEmpty))) - if (rightLeft) - return proof.InvalidProofTactic(s"There is no instance of ${right} to replace.") - else - applyLeftRight(equality(right, left))(premise)(true, toLeft, toRight) match { - case proof.InvalidProofTactic(m) => return proof.InvalidProofTactic(s"There is no instance of ${left} to replace.") - case v: proof.ValidProofTactic => return v - } - - val leftForm = AppliedConnector(And, isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) - val rightForm = AppliedConnector(Or, isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) - val newleft = if (toLeft) isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.left - val newright = if (toRight) isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.right - val result1: Sequent = (AppliedConnector(And, newleft.toSeq), phi) |- rightOrigin - val result2: Sequent = result1.left |- AppliedConnector(Or, newright.toSeq) - var scproof: Seq[K.SCProofStep] = Seq(K.Restate((leftOrigin |- rightOrigin).underlying, -1)) - if (toLeft) - scproof = scproof :+ K.LeftSubstEq( - result1.underlying, - scproof.length - 1, - List(K.LambdaTermTerm(Seq(), left.underlying) -> (K.LambdaTermTerm(Seq(), right.underlying))), - (Seq(v.underlyingLabel), leftForm.underlying) - ) - if (toRight) - scproof = scproof :+ K.RightSubstEq( - result2.underlying, - scproof.length - 1, - List(K.LambdaTermTerm(Seq(), left.underlying) -> (K.LambdaTermTerm(Seq(), right.underlying))), - (Seq(v.underlyingLabel), rightForm.underlying) - ) - val bot = newleft + phi |- newright - scproof = scproof :+ K.Restate(bot.underlying, scproof.length - 1) - - proof.ValidProofTactic( - bot, - scproof, - Seq(premise) - ) - - case AppliedConnector(label, args) if label == Iff => - val left = args(0) - val right = args(1) - val fv_in_phi = (originSequent.left ++ originSequent.right).flatMap(_.allSchematicLabels).map(_.id) - val H = VariableFormula(nFreshId(fv_in_phi, 1).head) - lazy val isolatedLeft = originSequent.left.filterNot(f => isSame(f, phi)).map(f => (f, findSubformula(f, IndexedSeq(H -> left)))) - lazy val isolatedRight = originSequent.right.map(f => (f, findSubformula(f, IndexedSeq(H -> left)))) - if ((!toLeft || isolatedLeft.forall(_._2.isEmpty)) && (!toRight || isolatedRight.forall(_._2.isEmpty))) - if (rightLeft) - return proof.InvalidProofTactic(s"There is no instance of ${right} to replace.") - else - applyLeftRight(Iff(right, left))(premise)(true, toLeft, toRight) match { - case proof.InvalidProofTactic(m) => return proof.InvalidProofTactic(s"There is no instance of ${left} to replace.") - case v: proof.ValidProofTactic => return v - } - - val leftForm = AppliedConnector(And, isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) - val rightForm = AppliedConnector(Or, isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.body).toSeq) - val newleft = if (toLeft) isolatedLeft.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.left - val newright = if (toRight) isolatedRight.map((f, ltf) => if (ltf.isEmpty) f else ltf.get.applyUnsafe(Seq(right))) else originSequent.right - val result1: Sequent = (AppliedConnector(And, newleft.toSeq), phi) |- rightOrigin - val result2: Sequent = result1.left |- AppliedConnector(Or, newright.toSeq) - - var scproof: Seq[K.SCProofStep] = Seq(K.Restate((leftOrigin |- rightOrigin).underlying, -1)) - if (toLeft) - scproof = scproof :+ K.LeftSubstIff( - result1.underlying, - scproof.length - 1, - List(K.LambdaTermFormula(Seq(), left.underlying) -> (K.LambdaTermFormula(Seq(), right.underlying))), - (Seq(H.underlyingLabel), leftForm.underlying) - ) - if (toRight) - scproof = scproof :+ K.RightSubstIff( - result2.underlying, - scproof.length - 1, - List(K.LambdaTermFormula(Seq(), left.underlying) -> (K.LambdaTermFormula(Seq(), right.underlying))), - (Seq(H.underlyingLabel), rightForm.underlying) - ) - - val bot = newleft + phi |- newright - scproof = scproof :+ K.Restate(bot.underlying, scproof.length - 1) - - proof.ValidProofTactic( - bot, - scproof, - Seq(premise) - ) - case _ => proof.InvalidProofTactic(s"Formula in applySingleSimp need to be of the form a=b or q<=>p and not ${phi}") - } - } - - @nowarn("msg=.*the type test for proof.Fact cannot be checked at runtime*") - def apply(using - lib: lisa.prooflib.Library, - proof: lib.Proof, - line: sourcecode.Line, - file: sourcecode.File - )(f: proof.Fact | Formula, rightLeft: Boolean = false, toLeft: Boolean = true, toRight: Boolean = true)( - premise: proof.Fact - ): proof.ProofTacticJudgement = { - f match { - case phi: Formula => applyLeftRight(phi)(premise)(rightLeft, toLeft, toRight) - case f: proof.Fact => - val seq = proof.getSequent(f) - val phi = seq.right.head - val sp = TacticSubproof { - val x = applyLeftRight(phi)(premise)(rightLeft, toLeft, toRight) - proof.library.have(x) - proof.library.andThen(SimpleDeducedSteps.Discharge(f)) - } - - BasicStepTactic.unwrapTactic(sp)("Subproof substitution fail.") - } - - } - - def toLeft(using lib: lisa.prooflib.Library, proof: lib.Proof, line: sourcecode.Line, file: sourcecode.File)(f: proof.Fact | Formula, rightLeft: Boolean = false)( - premise: proof.Fact - ): proof.ProofTacticJudgement = apply(f, rightLeft, toLeft = true, toRight = false)(premise) - - def toRight(using lib: lisa.prooflib.Library, proof: lib.Proof, line: sourcecode.Line, file: sourcecode.File)(f: proof.Fact | Formula, rightLeft: Boolean = false)( - premise: proof.Fact - ): proof.ProofTacticJudgement = apply(f, rightLeft, toLeft = false, toRight = true)(premise) - - } -} diff --git a/lisa-topology/src/main/scala/lisa/automation/Tableau.scala b/lisa-topology/src/main/scala/lisa/automation/Tableau.scala deleted file mode 100644 index de6f6bcbd..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/Tableau.scala +++ /dev/null @@ -1,491 +0,0 @@ -package lisa.automation -import lisa.fol.FOL as F -import lisa.prooflib.Library -import lisa.prooflib.OutputManager.* -import lisa.prooflib.ProofTacticLib.* -import lisa.utils.K -import lisa.utils.K.{_, given} -import lisa.utils.parsing.FOLPrinter.prettyFormula -import lisa.utils.parsing.FOLPrinter.prettySCProof -import lisa.utils.parsing.FOLPrinter.prettySequent -import lisa.utils.parsing.FOLPrinter.prettyTerm - -import scala.collection.immutable.HashMap -import scala.collection.immutable.HashSet - -/** - * Now need to deal with variables unifying with terms containing themselves - * optimiye list siye computation - * Then, optimize unification check by not checking all pairs all the time - * Then, shortcut branches by checking if they are OL-true or OL-false - * - * Next test: No quantifiers but actual terms with variables - */ - -object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequentTactic { - - var debug = true - def pr(s: Object) = if debug then println(s) - - def apply(using lib: Library, proof: lib.Proof)(bot: F.Sequent): proof.ProofTacticJudgement = { - solve(bot) match { - case Some(value) => proof.ValidProofTactic(bot, value.steps, Seq()) - case None => proof.InvalidProofTactic("Could not prove the statement.") - } - } - - /** - * Given a targeted conclusion sequent, try to prove it using laws of propositional logic and reflexivity and symmetry of equality. - * Uses the given already proven facts as assumptions to reach the desired goal. - * - * @param proof The ongoing proof object in which the step happens. - * @param premise A previously proven step necessary to reach the conclusion. - * @param bot The desired conclusion. - */ - def apply(using lib: Library, proof: lib.Proof)(premise: proof.Fact)(bot: F.Sequent): proof.ProofTacticJudgement = - from(using lib, proof)(Seq(premise)*)(bot) - - def from(using lib: Library, proof: lib.Proof)(premises: proof.Fact*)(bot: F.Sequent): proof.ProofTacticJudgement = { - val botK = bot.underlying - val premsFormulas: Seq[((proof.Fact, Formula), Int)] = premises.map(p => (p, sequentToFormula(proof.getSequent(p).underlying))).zipWithIndex - val initProof = premsFormulas.map(s => Restate(() |- s._1._2, -(1 + s._2))).toList - val sqToProve = botK ++<< (premsFormulas.map(s => s._1._2).toSet |- ()) - - solve(sqToProve) match { - case Some(value) => - val subpr = SCSubproof(value) - val stepsList = premsFormulas.foldLeft[List[SCProofStep]](List(subpr))((prev: List[SCProofStep], cur) => { - val ((prem, form), position) = cur - Cut(prev.head.bot -<< form, position, initProof.length + prev.length - 1, form) :: prev - }) - val steps = (initProof ++ stepsList.reverse).toIndexedSeq - proof.ValidProofTactic(bot, steps, premises) - case None => - proof.InvalidProofTactic("Could not prove the statement.") - } - } - - inline def solve(sequent: F.Sequent): Option[SCProof] = solve(sequent.underlying) - - def solve(sequent: K.Sequent): Option[SCProof] = { - - val f = K.ConnectorFormula(K.And, (sequent.left.toSeq ++ sequent.right.map(f => K.ConnectorFormula(K.Neg, List(f))))) - val taken = f.schematicTermLabels - val nextIdNow = if taken.isEmpty then 0 else taken.maxBy(_.id.no).id.no + 1 - val (fnamed, nextId, _) = makeVariableNamesUnique(f, nextIdNow, f.freeVariables) - - val nf = reducedNNFForm(fnamed) - val uv = VariableLabel(Identifier("§", nextId)) - val proof = decide(Branch.empty(nextId + 1, uv).prepended(nf)) - proof match - case None => None - case Some((p, _)) => Some(SCProof((Restate(sequent, p.length) :: Weakening(nf |- (), p.length - 1) :: p).reverse.toIndexedSeq, IndexedSeq.empty)) - - } - - /** - * A branch represent a sequent (whose right hand side is empty) that is being proved. - * It is assumed that the sequent is in negation normal form, negations are only applied to atoms. - * Formulas are sorted according to their shape : - * Conjunctions are in alpha - * Disjunctions are in beta - * Existential quantifiers are in delta - * Universal quantifiers are in gamma - * Atoms are in atoms (split into positive and negative) - * At each step of the procedure, a formula is deconstructed in accordance with the rules of the tableau calculus. - * Then that formula is removed from the branch as it is no longer needed. - * Variables coming from universal quantifiers are marked as suitable for unification in unifiable - * Instantiations that have been done already are stored in triedInstantiation, to avoid infinite loops. - * When a quantifier Q1 is below a universal quantifier Q2, Q2 can be instantiated multiple times. - * Then, Q1 may also need to be instantiated multiple versions, requiring fresh variable names. - * maxIndex stores an index that is used to generate fresh variable names. - */ - case class Branch( - alpha: List[ConnectorFormula], // label = And - beta: List[ConnectorFormula], // label = Or - delta: List[BinderFormula], // Exists(...)) - gamma: List[BinderFormula], // Forall(...) - atoms: (List[AtomicFormula], List[AtomicFormula]), // split into positive and negatives! - unifiable: Map[VariableLabel, (BinderFormula, Int)], // map between metavariables and the original formula they came from, with the penalty associated to the complexity of the formula. - numberInstantiated: Map[VariableLabel, Int], // map between variables and the number of times they have been instantiated - - skolemized: Set[VariableLabel], // set of variables that have been skolemized - triedInstantiation: Map[VariableLabel, Set[Term]], // map between metavariables and the term they were already instantiated with - maxIndex: Int, // the maximum index used for skolemization and metavariables - varsOrder: Map[VariableLabel, Int], // the order in which variables were instantiated. In particular, if the branch contained the formula ∀x. ∀y. ... then x > y. - unusedVar: VariableLabel // a variable the is neither free nor bound in the original formula. - ) { - def pop(f: Formula): Branch = f match - case f @ ConnectorFormula(Or, args) => - if (beta.nonEmpty && beta.head.uniqueNumber == f.uniqueNumber) copy(beta = beta.tail) else throw Exception("First formula of beta is not f") - case f @ BinderFormula(Exists, x, inner) => - if (delta.nonEmpty && delta.head.uniqueNumber == f.uniqueNumber) copy(delta = delta.tail) else throw Exception("First formula of delta is not f") - case f @ BinderFormula(Forall, x, inner) => - if (gamma.nonEmpty && gamma.head.uniqueNumber == f.uniqueNumber) copy(gamma = gamma.tail) else throw Exception("First formula of gamma is not f") - case ConnectorFormula(And, args) => - if (alpha.nonEmpty && alpha.head.uniqueNumber == f.uniqueNumber) copy(alpha = alpha.tail) else throw Exception("First formula of alpha is not f") - case f @ AtomicFormula(id, args) => - throw Exception("Should not pop Atoms") - case f @ ConnectorFormula(Neg, List(AtomicFormula(id, args))) => - throw Exception("Should not pop Atoms") - case _ => ??? - - def prepended(f: Formula): Branch = f match - case f @ ConnectorFormula(And, args) => this.copy(alpha = f :: alpha) - case f @ ConnectorFormula(Or, args) => this.copy(beta = f :: beta) - case f @ BinderFormula(Exists, x, inner) => this.copy(delta = f :: delta) - case f @ BinderFormula(Forall, x, inner) => this.copy(gamma = f :: gamma) - case f @ AtomicFormula(id, args) => - this.copy(atoms = (f :: atoms._1, atoms._2)) - case ConnectorFormula(Neg, List(f @ AtomicFormula(id, args))) => - this.copy(atoms = (atoms._1, f :: atoms._2)) - case _ => ??? - - def prependedAll(l: Seq[Formula]): Branch = l.foldLeft(this)((a, b) => a.prepended(b)) - - def asSequent: Sequent = (beta ++ delta ++ gamma ++ atoms._1 ++ atoms._2.map(a => !a)).toSet |- Set() // inefficient, not used - - import Branch.* - override def toString(): String = - val pretUnif = unifiable.map((x, f) => x.id + " -> " + prettyFormula(f._1) + " : " + f._2).mkString("Unif(", ", ", ")") - // val pretTried = triedInstantiation.map((x, t) => x.id + " -> " + prettyTerm(t, true)).mkString("Tried(", ", ", ")") - (s"Branch(" + - s"${RED(prettyIte(alpha, "alpha"))}, " + - s"${GREEN(prettyIte(beta, "beta"))}, " + - s"${BLUE(prettyIte(delta, "delta"))}, " + - s"${YELLOW(prettyIte(gamma, "gamma"))}, " + - s"${MAGENTA(prettyIte(atoms._1, "+"))}, ${CYAN(prettyIte(atoms._2, "-"))}, " + - s"$pretUnif, _, _)").split("'").mkString("").split("_").mkString("") - - } - object Branch { - def empty = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Map.empty, Set.empty, Map.empty, 1, Map.empty, VariableLabel(Identifier("§uv", 0))) - def empty(n: Int, uv: VariableLabel) = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Map.empty, Set.empty, Map.empty, n, Map.empty, uv) - def prettyIte(l: Iterable[Formula], head: String): String = l match - case Nil => "Nil" - case _ => l.map(prettyFormula(_, true)).mkString(head + "(", ", ", ")") - - } - - def makeVariableNamesUnique(f: Formula, nextId: Int, seen: Set[VariableLabel]): (Formula, Int, Set[VariableLabel]) = f match - case ConnectorFormula(label, args) => - val (nArgs, nnId, nSeen) = args.foldLeft((List(): Seq[Formula], nextId, seen))((prev, next) => - val (l, n, s) = prev - val (nf, nn, ns) = makeVariableNamesUnique(next, n, s) - (l :+ nf, nn, ns) - ) - (ConnectorFormula(label, nArgs), nnId, nSeen) - case pf: AtomicFormula => (pf, nextId, seen) - case BinderFormula(label, x, inner) => - if (seen.contains(x)) - val (nInner, nnId, nSeen) = makeVariableNamesUnique(inner, nextId + 1, seen) - val newX = VariableLabel(Identifier(x.id, nextId)) - (BinderFormula(label, newX, substituteVariablesInFormula(nInner, Map(x -> newX), Seq())), nnId, nSeen) - else - val (nInner, nnId, nSeen) = makeVariableNamesUnique(inner, nextId, seen + x) - (BinderFormula(label, x, nInner), nnId, nSeen) - - type Substitution = Map[VariableLabel, Term] - val Substitution = HashMap - def prettySubst(s: Substitution): String = s.map((x, t) => x.id + " -> " + prettyTerm(t, true)).mkString("Subst(", ", ", ")") - - /** - * Detect if two terms can be unified, and if so, return a substitution that unifies them. - */ - def unify(t1: Term, t2: Term, current: Substitution, br: Branch): Option[Substitution] = (t1, t2) match - case (VariableTerm(x), VariableTerm(y)) if (br.unifiable.contains(x) || x.id.no > br.maxIndex) && (br.unifiable.contains(y) || y.id.no > br.maxIndex) => - if x == y then Some(current) - else if current.contains(x) then unify(current(x), t2, current, br) - else if current.contains(y) then unify(t1, current(y), current, br) - else Some(current + (x -> y)) - case (VariableTerm(x), t2: Term) if br.unifiable.contains(x) || x.id.no > br.maxIndex => - val newt2 = substituteVariablesInTerm(t2, current) - if newt2.freeVariables.contains(x) then None - else if (current.contains(x)) unify(current(x), newt2, current, br) - else Some(current + (x -> newt2)) - case (t1: Term, VariableTerm(y)) if br.unifiable.contains(y) || y.id.no > br.maxIndex => - val newt1 = substituteVariablesInTerm(t1, current) - if newt1.freeVariables.contains(y) then None - else if (current.contains(y)) unify(newt1, current(y), current, br) - else Some(current + (y -> newt1)) - case (Term(label1, args1), Term(label2, args2)) => - if label1 == label2 && args1.size == args2.size then - args1 - .zip(args2) - .foldLeft(Some(current): Option[Substitution])((prev, next) => - prev match - case None => None - case Some(s) => unify(next._1, next._2, s, br) - ) - else None - - /** - * Detect if two atoms can be unified, and if so, return a substitution that unifies them. - */ - def unifyPred(pos: AtomicFormula, neg: AtomicFormula, br: Branch): Option[Substitution] = { - (pos, neg) match - case (AtomicFormula(id1, args1), AtomicFormula(id2, args2)) if (id1 == id2 && args1.size == args2.size) => - args1 - .zip(args2) - .foldLeft(Some(Substitution.empty): Option[Substitution])((prev, next) => - prev match - case None => None - case Some(s) => unify(next._1, next._2, s, br) - ) - case _ => None - - } - - /** - * Detect if a branch can be closed, and if so, return a list of substitutions that closes it along with the formulas used to close it - * If it can't be closed, returns None - * The substitution cannot do substitutions that were already done in branch.triedInstantiation. - * When multiple substitutions are possible, the one with the smallest size is returned. (Maybe there is a better heuristic, like distance from the root?) - */ - def close(branch: Branch): Option[(Substitution, Set[Formula])] = { - val newMap = branch.atoms._1 - .flatMap(pred => pred.freeVariables.filter(v => branch.unifiable.contains(v))) - .map(v => v -> VariableLabel(Identifier(v.id.name, v.id.no + branch.maxIndex + 1))) - .toMap - val newMapTerm = newMap.map((k, v) => k -> VariableTerm(v)) - val inverseNewMap = newMap.map((k, v) => v -> k).toMap - val inverseNewMapTerm = inverseNewMap.map((k, v) => k -> VariableTerm(v)) - val pos = branch.atoms._1.map(pred => substituteVariablesInFormula(pred, newMapTerm, Seq())).asInstanceOf[List[AtomicFormula]].iterator - var substitutions: List[(Substitution, Set[Formula])] = Nil - - while (pos.hasNext) { - val p = pos.next() - if (p.label == bot) return Some((Substitution.empty, Set(bot))) - val neg = branch.atoms._2.iterator - while (neg.hasNext) { - val n = neg.next() - unifyPred(p, n, branch) match - case None => () - case Some(unif) => - substitutions = (unif, Set(p, !n)) :: substitutions - } - } - - val cr1 = substitutions.map((sub, set) => - ( - sub.flatMap((v, t) => - if v.id.no > branch.maxIndex then - if t == inverseNewMapTerm(v) then None - else Some(inverseNewMap(v) -> substituteVariablesInTerm(t, inverseNewMapTerm.map((v, t) => v -> substituteVariablesInTerm(t, sub)))) - else if newMap.contains(v) && t == newMap(v) then None - else Some(v -> substituteVariablesInTerm(t, inverseNewMapTerm)) - ), - set.map(f => substituteVariablesInFormula(f, inverseNewMapTerm, Seq())) - ) - ) - - val cr = cr1.filterNot(s => - s._1.exists((x, t) => - val v = branch.triedInstantiation.contains(x) && branch.triedInstantiation(x).contains(t) - v - ) - ) - - bestSubst(cr, branch) - - } - - def bestSubst(substs: List[(Substitution, Set[Formula])], branch: Branch): Option[(Substitution, Set[Formula])] = { - if substs.isEmpty then return None - val minSize = substs.minBy(_._1.size) - val smallSubst = substs.filter(_._1.size == minSize._1.size) - // Up to this, it is necessary for completeness. From this, it is heuristic. - - val best = smallSubst.minBy(s => substitutionScore(s._1, branch)) - Some(best) - } - def formulaPenalty(f: Formula, branch: Branch): Int = f match - case ConnectorFormula(And, args) => 10 + args.map(formulaPenalty(_, branch)).sum - case ConnectorFormula(Or, args) => 40 + args.map(formulaPenalty(_, branch)).sum - case BinderFormula(Exists, x, inner) => 30 + formulaPenalty(inner, branch) - case BinderFormula(Forall, x, inner) => 200 + formulaPenalty(inner, branch) - case AtomicFormula(id, args) => 0 - case ConnectorFormula(Neg, List(AtomicFormula(id, args))) => 0 - case _ => ??? - - def substitutionScore(subst: Substitution, branch: Branch): Int = { - def pairPenalty(v: VariableLabel, t: Term) = { - val variablePenalty = branch.unifiable(v)._2 + branch.numberInstantiated(v) * 20 - def termPenalty(t: Term): Int = t match - case VariableTerm(x) => if branch.unifiable.contains(x) then branch.unifiable(x)._2 * 1 else 0 - case Term(label, args) => 100 + args.map(termPenalty).sum - variablePenalty + termPenalty(t) - } - subst.map((v, t) => pairPenalty(v, t)).sum - } - - /** - * Explodes one And formula - * The alpha list of the branch must not be empty - */ - def alpha(branch: Branch): Branch = { - val f = branch.alpha.head - branch.copy(alpha = branch.alpha.tail).prependedAll(f.args) - } - - /** - * Explodes one Or formula, and alpha-simplifies it - * Add the exploded formula to the used list, if one beta formula is found - * The beta list of the branch must not be empty - */ - def beta(branch: Branch): List[(Branch, Formula)] = { - val f = branch.beta.head - val b1 = branch.copy(beta = branch.beta.tail) - val resList = f.args.toList.map(disjunct => { - ((b1.prepended(disjunct), disjunct)) - }) - resList - } - - /** - * Explodes one Exists formula - * Add the unquantified formula to the branch - * Since the bound variable is not marked as suitable for instantiation, it behaves as a constant symbol (skolem) - */ - def delta(branch: Branch): (Branch, VariableLabel, Formula) = { - val f = branch.delta.head - if branch.skolemized.contains(branch.delta.head.bound) then - val newX = VariableLabel(Identifier(f.bound.id.name, branch.maxIndex)) - val newInner = substituteVariablesInFormula(f.inner, Map(f.bound -> newX), Seq()) - (branch.copy(delta = branch.delta.tail, maxIndex = branch.maxIndex + 1).prepended(newInner), newX, newInner) - else (branch.copy(delta = branch.delta.tail, skolemized = branch.skolemized + f.bound).prepended(f.inner), f.bound, f.inner) - } - - /** - * Explodes one Forall formula - * Add the unquantified formula to the branch and mark the bound variable as suitable for unification - * This step will most of the time be cancelled when building the proof, unless any arbitrary instantiation is sufficient to get a proof. - */ - def gamma(branch: Branch): (Branch, VariableLabel, Formula) = { - val f = branch.gamma.head - val (ni, nb) = branch.unifiable.get(f.bound) match - case None => - (f.inner, f.bound) - case Some(value) => - val newBound = VariableLabel(Identifier(f.bound.id.name, branch.maxIndex)) - val newInner = substituteVariablesInFormula(f.inner, Map(f.bound -> newBound), Seq()) - (newInner, newBound) - val b1 = branch.copy( - gamma = branch.gamma.tail, - unifiable = branch.unifiable + (nb -> (f, formulaPenalty(f.inner, branch))), - numberInstantiated = branch.numberInstantiated + (nb -> (branch.numberInstantiated.getOrElse(f.bound, 0))), - maxIndex = branch.maxIndex + 1, - varsOrder = branch.varsOrder + (nb -> branch.varsOrder.size) - ) - (b1.prepended(ni), nb, ni) - } - - /** - * When a closing unification has been found, apply it to the branch - * This does not backtracking: The metavariable remains available if it needs further instantiation. - */ - def applyInst(branch: Branch, x: VariableLabel, t: Term): (Branch, Formula) = { - val f = branch.unifiable(x)._1 - val newTried = branch.triedInstantiation.get(x) match - case None => branch.triedInstantiation + (x -> Set(t)) - case Some(s) => branch.triedInstantiation + (x -> (s + t)) - - val inst = instantiate(f.inner, f.bound, t) - val r = branch - .prepended(inst) - .copy( - triedInstantiation = newTried, - numberInstantiated = branch.numberInstantiated + (x -> (branch.numberInstantiated(x) + 1)) - ) - (r, inst) - } - - /** - * Decide if a branch can be closed, and if not, explode it. - * Main routine of the decision procedure. If it succeeds, return a proof of the branch. - * Note that the proof actually proves a subset of a branch when possible, to cut short on unneeded steps and formulas. - * The return integer is the size of the proof: Used to avoid computing the size every time in linear time. - */ - def decide(branch: Branch): Option[(List[SCProofStep], Int)] = { - - val closeSubst = close(branch) - if (closeSubst.nonEmpty && closeSubst.get._1.isEmpty) // If branch can be closed without Instantiation (Hyp) - Some((List(RestateTrue(Sequent(closeSubst.get._2, Set()))), 0)) - else if (branch.alpha.nonEmpty) // If branch contains an Alpha formula (LeftAnd) - val rec = alpha(branch) - decide(rec).map((proof, step) => - if branch.alpha.head.args.exists(proof.head.bot.left.contains) then - val sequent = proof.head.bot.copy(left = (proof.head.bot.left -- branch.alpha.head.args) + branch.alpha.head) - (Weakening(sequent, proof.size - 1) :: proof, step + 1) - else (proof, step) - ) - else if (branch.delta.nonEmpty) // If branch contains a Delta formula (LeftExists) - val rec = delta(branch) - val upperProof = decide(rec._1) - upperProof.map((proof, step) => - if proof.head.bot.left.contains(rec._3) then - val sequent = (proof.head.bot -<< rec._3) +<< branch.delta.head - (LeftExists(sequent, step, rec._3, rec._2) :: proof, step + 1) - else (proof, step) - ) - else if (branch.beta.nonEmpty) // If branch contains a Beta formula (LeftOr) - val list = beta(branch) - val (proof, treversed, needed) = list.foldLeft((Some(Nil): Option[List[SCProofStep]], Nil: List[Int], true: Boolean))((prev, next) => - prev match - case (None, _, _) => prev // proof failed - case (_, _, false) => - prev // proof succeded early - case (Some(prevProof), t, true) => - val res = decide(next._1) - res match - case None => (None, t, true) - case Some((nextProof, step)) => - if nextProof.head.bot.left.contains(next._2) then // If the disjunct was used, encapsulate the subbranch in a Subproof - val subproofDisj = - if nextProof.size == 1 then nextProof.head - else SCSubproof(SCProof(nextProof.toIndexedSeq.reverse, IndexedSeq.empty), IndexedSeq.empty) - (Some(subproofDisj :: prevProof), prevProof.size :: t, true) - else - // If the disjunct was not used, then the subbranch is a proof of the whole statement and the split is not necessary. - (res.map(_._1), List(nextProof.size - 1), false) - ) - proof.map(proo => - if needed == true then - val sequent = ((proo.reverse.zip(list).flatMap((proof, bf) => proof.bot.left - bf._2).toSet + branch.beta.head) |- ()) - (LeftOr(sequent, treversed.reverse, branch.beta.head.args) :: proo, treversed.size) - else (proo, proo.size - 1) - ) - else if (branch.gamma.nonEmpty) // If branch contains a Gamma formula (LeftForall) - val rec = gamma(branch) - val upperProof = decide(rec._1) - // LeftForall(bot: Sequent, t1: Int, phi: Formula, x: VariableLabel, t: Term) - upperProof.map((proof, step) => - if proof.head.bot.left.contains(rec._3) then - val sequent = (proof.head.bot -<< rec._3) +<< branch.gamma.head - (LeftForall(sequent, step, branch.gamma.head.inner, branch.gamma.head.bound, rec._2()) :: proof, step + 1) - else (proof, step) - ) - else if (closeSubst.nonEmpty && closeSubst.get._1.nonEmpty) // If branch can be closed with Instantiation (LeftForall) - val (x, t) = closeSubst.get._1.minBy((x, t) => branch.varsOrder(x)) - val (recBranch, instantiated) = applyInst(branch, x, t) - val upperProof = decide(recBranch) - upperProof.map((proof, step) => - if proof.head.bot.left.contains(instantiated) then - val sequent = (proof.head.bot -<< instantiated) +<< branch.unifiable(x)._1 - (LeftForall(sequent, step, branch.unifiable(x)._1.inner, branch.unifiable(x)._1.bound, t) :: proof, step + 1) - else (proof, step) - ) - else None - // End of decide - } - - def containsAlpha(set: Set[Formula], f: Formula) = f match { - case ConnectorFormula(And, args) => args.exists(set.contains) - case _ => set.contains(f) - } - - def instantiate(f: Formula, x: VariableLabel, t: Term): Formula = f match - case ConnectorFormula(label, args) => ConnectorFormula(label, args.map(instantiate(_, x, t))) - case AtomicFormula(id, args) => AtomicFormula(id, args.map(substituteVariablesInTerm(_, Substitution(x -> t)))) - case BinderFormula(label, y, inner) => if (x == y) f else BinderFormula(label, y, instantiate(inner, x, t)) -} diff --git a/lisa-topology/src/main/scala/lisa/automation/Tautology.scala b/lisa-topology/src/main/scala/lisa/automation/Tautology.scala deleted file mode 100644 index d1367feeb..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/Tautology.scala +++ /dev/null @@ -1,257 +0,0 @@ -package lisa.automation - -import lisa.automation.Substitution -import lisa.fol.FOL as F -import lisa.prooflib.Library -import lisa.prooflib.ProofTacticLib.* -import lisa.utils.K.{_, given} - -/** - * A tactic object dedicated to solve any propositionaly provable sequent (possibly in exponential time). Can be used with arbitrary many premises. - * Leverages the OL algorithm for scalafmpropositional logic. - */ -object Tautology extends ProofTactic with ProofSequentTactic with ProofFactSequentTactic { - - /** - * Given a targeted conclusion sequent, try to prove it using laws of propositional logic and reflexivity and symmetry of equality. - * - * @param proof The ongoing proof object in which the step happens. - * @param bot The desired conclusion. - */ - def apply(using lib: Library, proof: lib.Proof)(bot: F.Sequent): proof.ProofTacticJudgement = { - val botK = bot.underlying - solveSequent(botK) match { - case Left(value) => proof.ValidProofTactic(bot, value.steps, Seq()) - case Right((msg, seq)) => proof.InvalidProofTactic(msg) - } - } - - /** - * Given a targeted conclusion sequent, try to prove it using laws of propositional logic and reflexivity and symmetry of equality. - * Uses the given already proven facts as assumptions to reach the desired goal. - * - * @param proof The ongoing proof object in which the step happens. - * @param premise A previously proven step necessary to reach the conclusion. - * @param bot The desired conclusion. - */ - def apply(using lib: Library, proof: lib.Proof)(premise: proof.Fact)(bot: F.Sequent): proof.ProofTacticJudgement = - from(using lib, proof)(Seq(premise)*)(bot) - - def from(using lib: Library, proof: lib.Proof)(premises: proof.Fact*)(bot: F.Sequent): proof.ProofTacticJudgement = { - val botK = bot.underlying - val premsFormulas: Seq[((proof.Fact, Formula), Int)] = premises.map(p => (p, sequentToFormula(proof.getSequent(p).underlying))).zipWithIndex - val initProof = premsFormulas.map(s => Restate(() |- s._1._2, -(1 + s._2))).toList - val sqToProve = botK ++<< (premsFormulas.map(s => s._1._2).toSet |- ()) - - solveSequent(sqToProve) match { - case Left(value) => - val subpr = SCSubproof(value) - val stepsList = premsFormulas.foldLeft[List[SCProofStep]](List(subpr))((prev: List[SCProofStep], cur) => { - val ((prem, form), position) = cur - if prev.head.bot.left.contains(form) then Cut(prev.head.bot -<< form, position, initProof.length + prev.length - 1, form) :: prev - else prev - }) - val steps = (initProof ++ stepsList.reverse).toIndexedSeq - proof.ValidProofTactic(bot, steps, premises) - case Right((msg, seq)) => - proof.InvalidProofTactic(msg) - } - } - - /** - * This function returns a proof of the given sequent if such a proof exists using only the rules of propositional logic and reflexivity and symmetry of equality. - * Be aware that the runtime and size of the proof may be exponential in the number of atoms (i.e. number of non-propositional subformulas of the input). - * The strategy consists in leveraging OL formula reduction by alternating between branching on an atom and reducing the formula. - * @param s A sequent that should be a propositional logic tautology. It can contain binders and schematic connector symbols, but they will be treated as atoms. - * @return A proof of the given sequent, if it exists - */ - def solveSequent(s: Sequent): Either[SCProof, (String, Sequent)] = { - val augSeq = augmentSequent(s) - val MaRvIn = VariableFormulaLabel(freshId(augSeq.formula.schematicFormulaLabels.map(_.id), "MaRvIn")) // arbitrary name that is unlikely to already exist in the formula - - try { - val steps = solveAugSequent(augSeq, 0)(using MaRvIn) - Left(SCProof((Restate(s, steps.length - 1) :: steps).reverse.toIndexedSeq)) - } catch - case e: NoProofFoundException => - Right( - ( - "The statement may be incorrect or not provable within propositional logic.\n" + - "The proof search failed because it needed the truth of the following sequent:\n" + - s"${lisa.utils.FOLPrinter.prettySequent(e.unsolvable)}", - e.unsolvable - ) - ) - - } - - // From there, private code. - - // Augmented Sequent - private case class AugSequent(decisions: (List[Formula], List[Formula]), formula: Formula) - - // Transform a sequent into a format more adequate for solving - private def augmentSequent(s: Sequent): AugSequent = { - val f = reducedForm(sequentToFormula(s)) - val atoms: scala.collection.mutable.Map[Formula, Int] = scala.collection.mutable.Map.empty - AugSequent((Nil, Nil), f) - } - - def reduceSequent(s: Sequent): Formula = { - val p = simplify(sequentToFormula(s)) - val nf = computeNormalForm(p) - val fln = fromLocallyNameless(nf, Map.empty, 0) - val res = toFormulaAIG(fln) - res - } - - // Find all "atoms" of the formula. - // We mean atom in the propositional logic sense, so any formula starting with a predicate symbol, a binder or a schematic connector is an atom here. - def findBestAtom(f: Formula): Option[Formula] = { - val atoms: scala.collection.mutable.Map[Formula, Int] = scala.collection.mutable.Map.empty - def findAtoms2(f: Formula, add: Formula => Unit): Unit = f match { - case AtomicFormula(label, _) if label != top && label != bot => add(f) - case AtomicFormula(_, _) => () - case ConnectorFormula(label, args) => - label match { - case label: ConstantConnectorLabel => args.foreach(c => findAtoms2(c, add)) - case SchematicConnectorLabel(id, arity) => add(f) - } - case BinderFormula(label, bound, inner) => add(f) - } - findAtoms2(f, a => atoms.update(a, { val g = atoms.get(a); if (g.isEmpty) 1 else g.get + 1 })) - if (atoms.isEmpty) None else Some(atoms.toList.maxBy(_._2)._1) - } - - private class NoProofFoundException(val unsolvable: Sequent) extends Exception - - // Given a sequent, return a proof of that sequent if on exists that only uses propositional logic rules and reflexivity of equality. - // Alternates between reducing the formulas using the OL algorithm for propositional logic and branching on an atom using excluded middle. - // An atom is a subformula of the input that is either a predicate, a binder or a schematic connector, i.e. a subformula that has not meaning in propositional logic. - private def solveAugSequent(s: AugSequent, offset: Int)(using MaRvIn: VariableFormulaLabel): List[SCProofStep] = { - val bestAtom = findBestAtom(s.formula) - val redF = reducedForm(s.formula) - if (redF == top()) { - List(RestateTrue(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- s.formula)) - } else if (bestAtom.isEmpty) { - assert(redF == bot()) // sanity check; If the formula has no atom left in it and is reduced, it should be either ⊤ or ⊥. - val res = s.decisions._1 |- redF :: s.decisions._2 // the branch that can't be closed - throw new NoProofFoundException(res) - } else { - val atom = bestAtom.get - val optLambda = findSubformula(redF, Seq((MaRvIn, atom))) - if (optLambda.isEmpty) return solveAugSequent(AugSequent(s.decisions, redF), offset) - val lambdaF = optLambda.get - - val seq1 = AugSequent((atom :: s.decisions._1, s.decisions._2), lambdaF(Seq(top()))) - val proof1 = solveAugSequent(seq1, offset) - val subst1 = RightSubstIff( - atom :: s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- redF, - offset + proof1.length - 1, - List((LambdaTermFormula(Seq(), atom), LambdaTermFormula(Seq(), top()))), - (lambdaF.vars, lambdaF.body) - ) - val seq2 = AugSequent((s.decisions._1, atom :: s.decisions._2), lambdaF(Seq(bot()))) - val proof2 = solveAugSequent(seq2, offset + proof1.length + 1) - val subst2 = RightSubstIff( - Neg(atom) :: s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- redF, - offset + proof1.length + proof2.length - 1 + 1, - List((LambdaTermFormula(Seq(), atom), LambdaTermFormula(Seq(), bot()))), - (lambdaF.vars, lambdaF.body) - ) - val red2 = Restate(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- (redF, atom), offset + proof1.length + proof2.length + 2 - 1) - val cutStep = Cut(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- redF, offset + proof1.length + proof2.length + 3 - 1, offset + proof1.length + 1 - 1, atom) - val redStep = Restate(s.decisions._1 ++ s.decisions._2.map((f: Formula) => Neg(f)) |- s.formula, offset + proof1.length + proof2.length + 4 - 1) - redStep :: cutStep :: red2 :: subst2 :: proof2 ++ (subst1 :: proof1) - - } - } - - private def condflat[T](s: Seq[(T, Boolean)]): (Seq[T], Boolean) = (s.map(_._1), s.exists(_._2)) - - private def findSubterm2(t: Term, subs: Seq[(VariableLabel, Term)]): (Term, Boolean) = { - val eq = subs.find(s => isSameTerm(t, s._2)) - if (eq.nonEmpty) (eq.get._1(), true) - else { - val induct = condflat(t.args.map(te => findSubterm2(te, subs))) - if (!induct._2) (t, false) - else (Term(t.label, induct._1), true) - - } - - } - - private def findSubterm2(f: Formula, subs: Seq[(VariableLabel, Term)]): (Formula, Boolean) = { - f match { - case AtomicFormula(label, args) => - val induct = condflat(args.map(findSubterm2(_, subs))) - if (!induct._2) (f, false) - else (AtomicFormula(label, induct._1), true) - case ConnectorFormula(label, args) => - val induct = condflat(args.map(findSubterm2(_, subs))) - if (!induct._2) (f, false) - else (ConnectorFormula(label, induct._1), true) - case BinderFormula(label, bound, inner) => - val fv_in_f = subs.flatMap(e => e._2.freeVariables + e._1) - if (!fv_in_f.contains(bound)) { - val induct = findSubterm2(inner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, bound, induct._1), true) - } else { - val newv = VariableLabel(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) - val newInner = substituteVariablesInFormula(inner, Map(bound -> newv()), Seq.empty) - val induct = findSubterm2(newInner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, newv, induct._1), true) - } - } - } - - private def findSubformula2(f: Formula, subs: Seq[(VariableFormulaLabel, Formula)]): (Formula, Boolean) = { - val eq = subs.find(s => isSame(f, s._2)) - if (eq.nonEmpty) (eq.get._1(), true) - else - f match { - case AtomicFormula(label, args) => - (f, false) - case ConnectorFormula(label, args) => - val induct = condflat(args.map(findSubformula2(_, subs))) - if (!induct._2) (f, false) - else (ConnectorFormula(label, induct._1), true) - case BinderFormula(label, bound, inner) => - val fv_in_f = subs.flatMap(_._2.freeVariables) - if (!fv_in_f.contains(bound)) { - val induct = findSubformula2(inner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, bound, induct._1), true) - } else { - val newv = VariableLabel(freshId((f.freeVariables ++ fv_in_f).map(_.id), bound.id)) - val newInner = substituteVariablesInFormula(inner, Map(bound -> newv()), Seq.empty) - val induct = findSubformula2(newInner, subs) - if (!induct._2) (f, false) - else (BinderFormula(label, newv, induct._1), true) - } - } - } - def findSubterm(t: Term, subs: Seq[(VariableLabel, Term)]): Option[LambdaTermTerm] = { - val vars = subs.map(_._1) - val r = findSubterm2(t, subs) - if (r._2) Some(LambdaTermTerm(vars, r._1)) - else None - } - - def findSubterm(f: Formula, subs: Seq[(VariableLabel, Term)]): Option[LambdaTermFormula] = { - val vars = subs.map(_._1) - val r = findSubterm2(f, subs) - if (r._2) Some(LambdaTermFormula(vars, r._1)) - else None - } - - def findSubformula(f: Formula, subs: Seq[(VariableFormulaLabel, Formula)]): Option[LambdaFormulaFormula] = { - val vars = subs.map(_._1) - val r = findSubformula2(f, subs) - if (r._2) Some(LambdaFormulaFormula(vars, r._1)) - else None - } - -} diff --git a/lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala b/lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala deleted file mode 100644 index e8d50cf0b..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/atp/Goeland.scala +++ /dev/null @@ -1,121 +0,0 @@ -package lisa.automation.atp -import lisa.fol.FOL as F -import lisa.prooflib.Library -import lisa.prooflib.OutputManager -import lisa.prooflib.ProofTacticLib.* -import lisa.utils.K -import lisa.utils.tptp.* - -import java.io.* -import scala.io.Source -import scala.util.Failure -import scala.util.Success -import scala.util.Try - -import ProofParser.* -import KernelParser.* -import sys.process._ - -/** - * Goéland is an automated theorem prover. This tactic calls the Goéland prover to solve the current sequent. - * Goéland is only available on Linux yet, but proofs generated by Goéland should be kept in the library for future use. - * To ensure that proofs are published and can be replayed in any system, proofs from an ATPcan only be generated in draft mode. - * When in non-draft mode, the proof file should be given as an argument to the tactic (the exact file is provided by Lisa upon run without draft mode). - */ -object Goeland extends ProofTactic with ProofSequentTactic { - private var i : Int = 0 - - val goelandExec = "../bin/goeland_linux_release" - - class OsNotSupportedException(msg: String) extends Exception(msg) - - val foldername = "goeland/" - - /** - * Fetch a proof of a sequent that was previously proven by Goéland. - * The file must be in SC-TPTP format. - */ - def apply(using lib: Library, proof: lib.Proof)(file:String)(bot: F.Sequent): proof.ProofTacticJudgement = { - val outputname = proof.owningTheorem.fullName+"_sol" - try { - val scproof = reconstructProof(new File(foldername+outputname+".p"))(using ProofParser.mapAtom, ProofParser.mapTerm, ProofParser.mapVariable) - proof.ValidProofTactic(bot, scproof.steps, Seq()) - } catch { - case e: FileNotFoundException => - throw FileNotFoundException("The file "+foldername+outputname+".p was not found. To produce a proof, use `by Goeland`. ") - case e => throw e - } - } - - - /** - * Solve a sequent using the Goéland automated theorem prover. - * At the moment, this option is only available on Linux system. - * The proof is generated and saved in a file in the `Goeland` folder. - */ - def apply(using lib: Library, proof: lib.Proof)(bot: F.Sequent): proof.ProofTacticJudgement = { - - - solve(Seq(), bot, proof.owningTheorem.fullName, lib.isDraft) match { - case Success(value) => proof.ValidProofTactic(bot, value.steps, Seq()) - case Failure(e) => e match - case e: FileNotFoundException => throw new Exception("For compatibility reasons, external provers can't be called in non-draft mode" + - " unless all proofs have already been generated and be available in static files. You can enable draft mode by adding `draft()` at the top of your working file.") - case e: OsNotSupportedException => throw e - case e => - throw e - } - } - - inline def solve(axioms: Seq[F.Sequent], sequent: F.Sequent, source: String, generateProofs : Boolean): Try[K.SCProof] = - solveK(axioms.map(_.underlying), sequent.underlying, source, generateProofs) - - - /** - * Solve a sequent using the Goéland automated theorem prover, and return the kernel proof. - * At the moment, this option is only available on Linux systems. - */ - def solveK(using line: sourcecode.Line, file: sourcecode.File)(axioms: Seq[K.Sequent], sequent: K.Sequent, source:String, generateProofs : Boolean): Try[K.SCProof] = { - val filename = source - val outputname = source+"_sol" - val directory = File(foldername) - if (directory != null) && !directory.exists() then directory.mkdirs() - - val freevars = (sequent.left.flatMap(_.freeVariables) ++ sequent.right.flatMap(_.freeVariables) ).toSet.map(x => x -> K.Term(K.VariableLabel(K.Identifier("X"+x.id.name, x.id.no)), Seq())).toMap - - val backMap = freevars.map{ - case (x: K.VariableLabel, K.Term(xx: K.VariableLabel, _)) => xx -> K.LambdaTermTerm(Seq(), K.Term(x, Seq())) - case _ => throw new Exception("This should not happen") - } - val r = problemToFile(foldername, filename, "question"+i, axioms, sequent, source) - i += 1 - - if generateProofs then - val OS = System.getProperty("os.name") - if OS.contains("nix") || OS.contains("nux") || OS.contains("aix") then - val ret = s"chmod u+x \"$goelandExec\"".! - val cmd = (s"$goelandExec -otptp -wlogs -no_id -quoted_pred -proof_file=$foldername$outputname $foldername$filename.p") - val res = try { - cmd.!! - } catch { - case e: Exception => - throw e - } - val proof = reconstructProof(new File(foldername+outputname+".p"))(using ProofParser.mapAtom, ProofParser.mapTerm, ProofParser.mapVariable) - Success(proof) - else if OS.contains("win") then - Failure(OsNotSupportedException("The Goeland automated theorem prover is not yet supported on Windows.")) - else - Failure(OsNotSupportedException("The Goeland automated theorem prover is only supported on Linux for now.")) - else - if File(foldername+outputname+".p").exists() then - val proof = reconstructProof(new File(foldername+outputname+".p"))(using ProofParser.mapAtom, ProofParser.mapTerm, ProofParser.mapVariable) - println(OutputManager.WARNING(s"WARNING: in ${file.value}:$line, For compatibility reasons, replace `by Goeland` with `by Goeland(\"$foldername$outputname\")`.")) - Success(proof) - - else Failure(Exception("For compatibility reasons, external provers can't be called in non-draft mode. You can enable draft mode by adding `draft()` at the top of your working file.")) - - - } - -} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala b/lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala deleted file mode 100644 index 651a3f7a1..000000000 --- a/lisa-topology/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala +++ /dev/null @@ -1,200 +0,0 @@ -package lisa.automation.settheory - -import lisa.SetTheoryLibrary.{_, given} -import lisa.automation.Tautology -import lisa.fol.FOL.{_, given} -import lisa.kernel.proof.SequentCalculus as SCunique -import lisa.maths.Quantifiers -import lisa.maths.settheory.SetTheory -import lisa.prooflib.BasicStepTactic.* -import lisa.prooflib.Library -import lisa.prooflib.ProofTacticLib.{_, given} -import lisa.prooflib.SimpleDeducedSteps.Restate -import lisa.prooflib.* -import lisa.utils.Printer -import lisa.utils.unification.UnificationUtils.FormulaSubstitution -import lisa.utils.unification.UnificationUtils.TermSubstitution -import lisa.utils.unification.UnificationUtils.matchFormula - -object SetTheoryTactics { - // var defs - private val x = variable - private val y = variable - private val z = variable - private val h = formulaVariable - private val P = predicate[1] - private val schemPred = predicate[1] - - /** - * Deduced Tactic --- Unique Comprehension - * - * Generates a unique existence proof. Given a set `x`, and a predicate `P(t, - * x)`, comprehension postulates there is a set containing the elements `t` of - * `x` satisfying `P(t, x)`, denoted `{t ∈ x | P(t, x)}`. This set is unique - * by extensionality. - * - * `() ⊢ ∃! z. ∀ t. t ∈ z ⇔ (t ∈ x ⋀ P(t, x))` - * - * @param originalSet the set to apply comprehension on - * @param separationPredicate the predicate to use for comprehension `(Term => - * Term => Boolean)` - * @return subproof for unique existence of the set defined by inputs - * - * @example - * Generates a subproof for the unique existence of the set `{t ∈ x | t ∈ y}`: - * {{{ - * have(() |- existsOne(z, forall(t, in(t, z) <=> (in(t, x) /\ in(t, y))))) by UniqueComprehension(x, lambda(Seq(t, x), in(t, y))) - * }}} - * See [[setIntersection]] or [[relationDomain]] for more usage. - */ - object UniqueComprehension extends ProofTactic { - def apply(using - proof: Proof, - line: sourcecode.Line, - file: sourcecode.File, - om: OutputManager - )(originalSet: Term, separationPredicate: LambdaTF[1])( // TODO dotty forgets that Term <:< LisaObject[Term] - bot: Sequent - ): proof.ProofTacticJudgement = { - require(separationPredicate.bounds.length == 1) // separationPredicate takes two args - given lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary - // fresh variable names to avoid conflicts - val botWithAssumptions = bot ++ (proof.getAssumptions |- ()) - val takenIDs = (botWithAssumptions.freeVariables ++ separationPredicate.body.freeVariables ++ originalSet.freeVariables).map(_.id) - val t1 = Variable(freshId(takenIDs, x.id)) - val t2 = Variable(freshId(takenIDs, y.id)) - - val prop = (in(t2, originalSet) /\ separationPredicate(t2)) // TODO (Seq(t2, originalSet) - def fprop(z: Term) = forall(t2, in(t2, z) <=> prop) - - /** - * Proof Summary: - * - * originalSet = x - * separationPredicate = \t x -> P(t, x) - * - * have () |- ∃ z. t ∈ z <=> (t ∈ x /\ P(t, x)) Comprehension Schema Instantiation - * import ∃ z. t ∈ z <=> (t ∈ x /\ P(t, x)) |- ∃! z. t ∈ z <=> (t ∈ x /\ P(t, x)) Unique by Extension [[uniqueByExtension]] Instantiation - * have () |- ∃! z. t ∈ z <=> (t ∈ x /\ P(t, x)) Cut - */ - val sp = TacticSubproof { // TODO check if isInstanceOf first - val existence = have(() |- exists(t1, fprop(t1))) by Weakening(comprehensionSchema of (z -> originalSet, φ -> separationPredicate)) - - val existsToUnique = have(exists(t1, fprop(t1)) |- existsOne(t1, fprop(t1))) by Weakening(SetTheory.uniqueByExtension of schemPred -> lambda(t2, prop)) - - // assumption elimination - have(() |- existsOne(t1, fprop(t1))) by Cut(existence, existsToUnique) - } - - // safely check, unwrap, and return the proof judgement - unwrapTactic(sp)("Subproof for unique comprehension failed.") - } - - /** - * Similarly to [[UniqueComprehension]], generates a unique existence proof - * but for a set comprehension that is not over some other set `x`. To do so, - * A proof that the predicate `P(t)` implies membership to the set `x` must be - * given. This then asserts the unique existence of the set `{t | P(t)}`. Useful - * when a definition includes a redundant membership condition. - * - * `P(t) ==> t ∈ x ⊢ ∃! z. ∀ t. t ∈ z ⇔ P(t)` - * - * @param originalSet the set to apply comprehension on - * @param separationPredicate the predicate to use for comprehension `(Term => - * Term => Boolean)` - * @param predicateImpliesInOriginalSet proof of the form `P(t) ==> t ∈ originalSet` - * @return subproof for unique existence of the set defined by inputs - * - * @example - * Generates a subproof for the unique existence of the set `{t | ∃x. x ∈ a ∧ t = {x}}`: - * {{{ - * val implicationProof = have(exists(x, in(x, a) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(a, a)))) subproof { - * // ... - * } - * have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, a) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( - * union(cartesianProduct(a, a)), - * lambda(t, exists(x, in(x, a) /\ (t === singleton(x)))), - * implicationProof - * ) - * }}} - * See [[UniqueComprehension]] for more usage. - */ - def fromOriginalSet(using - proof: Proof, - line: sourcecode.Line, - file: sourcecode.File, - om: OutputManager - )(originalSet: Term, separationPredicate: LambdaTF[1], predicateImpliesInOriginalSet: proof.Fact)( // TODO dotty forgets that Term <:< LisaObject[Term] - bot: Sequent - ): proof.ProofTacticJudgement = { - require(separationPredicate.bounds.length == 1) - given lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary - // fresh variable names to avoid conflicts - val botWithAssumptions = bot ++ (proof.getAssumptions |- ()) - val takenIDs = (botWithAssumptions.freeVariables ++ separationPredicate.body.freeVariables ++ originalSet.freeVariables).map(_.id) - val t1 = Variable(freshId(takenIDs, x.id)) - val t2 = Variable(freshId(takenIDs, y.id)) - - val separationCondition = separationPredicate(t2) - val targetDef = ∀(t2, in(t2, t1) <=> separationCondition) - val comprehension = ∀(t2, in(t2, t1) <=> in(t2, originalSet) /\ separationCondition) - - // prepare predicateImpliesInOriginalSet for usage in a proof: rename variables - val predicateImpliesInOriginalSetForm = separationCondition ==> in(t2, originalSet) - val predicateImpliesInOriginalSetReady = matchFormula( - separationCondition ==> in(t2, originalSet), - predicateImpliesInOriginalSet.statement.right.head - ) match - case None => - return proof.InvalidProofTactic(s"Unable to unify `predicateImpliesInOriginalSet` with the expected form: ${predicateImpliesInOriginalSetForm}") - case Some((formulaSubst, termSubst)) => - predicateImpliesInOriginalSet - .of(formulaSubst.map((k, v) => SubstPair(k, v)).toSeq*) - .of(termSubst.map((k, v) => SubstPair(k, v)).toSeq*) - - val sp = TacticSubproof { - // get uniqueness with the redundant original set membership - val uniq = have(∃!(t1, comprehension)) by UniqueComprehension( - originalSet, - lambda(t2, separationCondition) - ) - - // show that existence of the definition with the original set membership implies the - // existence of the definition without the original set membership - val transform = have( - ∃(t1, comprehension) |- ∃(t1, targetDef) - ) subproof { - // derive equivalence between t ∈ x /\ P(t) and P(t) from `predicateImpliesInOriginalSet` - val lhs = have(separationCondition ==> (in(t2, originalSet) /\ separationCondition)) by Tautology.from(predicateImpliesInOriginalSetReady) - val rhs = have(separationCondition /\ in(t2, originalSet) ==> separationCondition) by Restate - val subst = have(separationCondition <=> (in(t2, originalSet) /\ separationCondition)) by RightIff(lhs, rhs) - - // subtitute and introduce quantifiers - have((in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) by Hypothesis - val cutRhs = thenHave( - (in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition), separationCondition <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (separationCondition) - ) by RightSubstIff.withParametersSimple(List((separationCondition, in(t2, originalSet) /\ separationCondition)), lambda(h, in(t2, t1) <=> h)) - have((in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (separationCondition)) by Cut(subst, cutRhs) - thenHave(∀(t2, in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- in(t2, t1) <=> (separationCondition)) by LeftForall - thenHave(∀(t2, in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- ∀(t2, in(t2, t1) <=> (separationCondition))) by RightForall - thenHave(∀(t2, in(t2, t1) <=> (in(t2, originalSet) /\ separationCondition)) |- ∃(t1, ∀(t2, in(t2, t1) <=> (separationCondition)))) by RightExists - thenHave(thesis) by LeftExists - } - - val cutL = have( - ∃!(t1, comprehension) |- ∃(t1, comprehension) - ) by Restate.from(Quantifiers.existsOneImpliesExists of (P -> lambda(t1, comprehension))) - val cutR = have(∃(t1, targetDef) |- ∃!(t1, targetDef)) by Restate.from( - SetTheory.uniqueByExtension of (schemPred -> lambda(t2, separationCondition)) - ) - - val trL = have(∃!(t1, comprehension) |- ∃(t1, targetDef)) by Cut(cutL, transform) - val trR = have(∃!(t1, comprehension) |- ∃!(t1, targetDef)) by Cut(trL, cutR) - have(∃!(t1, targetDef)) by Cut.withParameters(∃!(t1, comprehension))(uniq, trR) - } - - // safely check, unwrap, and return the proof judgement - unwrapTactic(sp)("Subproof for unique comprehension failed.") - } - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala b/lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala deleted file mode 100644 index 1aeaf55ac..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/Quantifiers.scala +++ /dev/null @@ -1,254 +0,0 @@ -package lisa.maths - -/** - * Implements theorems about first-order logic. - */ -object Quantifiers extends lisa.Main { - - private val x = variable - private val y = variable - private val z = variable - private val a = variable - private val p = formulaVariable - private val P = predicate[1] - private val Q = predicate[1] - - /** - * Theorem --- A formula is equivalent to itself universally quantified if - * the bound variable is not free in it. - */ - val closedFormulaUniversal = Theorem( - () |- ∀(x, p) <=> p - ) { - have(thesis) by Tableau - } - - /** - * Theorem --- A formula is equivalent to itself existentially quantified if - * the bound variable is not free in it. - */ - val closedFormulaExistential = Theorem( - () |- ∃(x, p) <=> p - ) { - have(thesis) by Tableau - } - - /** - * Theorem --- If there exists a *unique* element satisfying a predicate, - * then we can say there *exists* an element satisfying it as well. - */ - val existsOneImpliesExists = Theorem( - ∃!(x, P(x)) |- ∃(x, P(x)) - ) { - have((x === y) <=> P(y) |- (x === y) <=> P(y)) by Hypothesis - thenHave(∀(y, (x === y) <=> P(y)) |- (x === y) <=> P(y)) by LeftForall - thenHave(∀(y, (x === y) <=> P(y)) |- P(x)) by InstFunSchema(Map(y -> x)) - thenHave(∀(y, (x === y) <=> P(y)) |- ∃(x, P(x))) by RightExists - thenHave(∃(x, ∀(y, (x === y) <=> P(y))) |- ∃(x, P(x))) by LeftExists - thenHave(thesis) by Restate - } - - /** - * Theorem --- Equality relation is transitive. - */ - val equalityTransitivity = Theorem( - (x === y) /\ (y === z) |- (x === z) - ) { - have((x === y) |- (x === y)) by Hypothesis - thenHave(((x === y), (y === z)) |- (x === z)) by RightSubstEq.withParametersSimple(List((y, z)), lambda(y, x === y)) - thenHave(thesis) by Restate - } - - /** - * Theorem --- Conjunction and universal quantification commute. - */ - val universalConjunctionCommutation = Theorem( - () |- forall(x, P(x) /\ Q(x)) <=> forall(x, P(x)) /\ forall(x, Q(x)) - ) { - have(thesis) by Tableau - } - - /** - * Theorem -- Existential quantification distributes conjunction. - */ - val existentialConjunctionDistribution = Theorem( - exists(x, P(x) /\ Q(x)) |- exists(x, P(x)) /\ exists(x, Q(x)) - ) { - have(thesis) by Tableau - } - - /** - * Theorem -- Existential quantification fully distributes when the conjunction involves one closed formula. - */ - val existentialConjunctionWithClosedFormula = Theorem( - exists(x, P(x) /\ p) <=> (exists(x, P(x)) /\ p) - ) { - have(thesis) by Tableau - } - - /** - * Theorem -- If there is an equality on the existential quantifier's bound variable inside its body, then we can reduce - * the existential quantifier to the satisfaction of the remaining body. - */ - val equalityInExistentialQuantifier = Theorem( - exists(x, P(x) /\ (y === x)) <=> P(y) - ) { - have(exists(x, P(x) /\ (y === x)) |- P(y)) subproof { - have(P(x) |- P(x)) by Hypothesis - thenHave((P(x), y === x) |- P(y)) by RightSubstEq.withParametersSimple(List((y, x)), lambda(y, P(y))) - thenHave(P(x) /\ (y === x) |- P(y)) by Restate - thenHave(thesis) by LeftExists - } - val forward = thenHave(exists(x, P(x) /\ (y === x)) ==> P(y)) by Restate - - have(P(y) |- exists(x, P(x) /\ (y === x))) subproof { - have(P(x) /\ (y === x) |- P(x) /\ (y === x)) by Hypothesis - thenHave(P(x) /\ (y === x) |- exists(x, P(x) /\ (y === x))) by RightExists - thenHave(P(y) /\ (y === y) |- exists(x, P(x) /\ (y === x))) by InstFunSchema(Map(x -> y)) - thenHave(thesis) by Restate - } - val backward = thenHave(P(y) ==> exists(x, P(x) /\ (y === x))) by Restate - - have(thesis) by RightIff(forward, backward) - } - - /** - * Theorem --- Disjunction and existential quantification commute. - */ - val existentialDisjunctionCommutation = Theorem( - () |- exists(x, P(x) \/ Q(x)) <=> exists(x, P(x)) \/ exists(x, Q(x)) - ) { - have(thesis) by Tableau - } - - /** - * Theorem --- Universal quantification distributes over equivalence - */ - val universalEquivalenceDistribution = Theorem( - forall(z, P(z) <=> Q(z)) |- (forall(z, P(z)) <=> forall(z, Q(z))) - ) { - have(thesis) by Tableau - } - - /** - * Theorem --- Universal quantification of equivalence implies equivalence - * of existential quantification. - */ - val existentialEquivalenceDistribution = Theorem( - forall(z, P(z) <=> Q(z)) |- (exists(z, P(z)) <=> exists(z, Q(z))) - ) { - have(thesis) by Tableau - - } - - /** - * Theorem --- Universal quantification distributes over implication - */ - val universalImplicationDistribution = Theorem( - forall(z, P(z) ==> Q(z)) |- (forall(z, P(z)) ==> forall(z, Q(z))) - ) { - have(thesis) by Tableau - } - - /** - * Theorem --- Universal quantification of implication implies implication - * of existential quantification. - */ - val existentialImplicationDistribution = Theorem( - forall(z, P(z) ==> Q(z)) |- (exists(z, P(z)) ==> exists(z, Q(z))) - ) { - have(thesis) by Tableau - } - - /** - * Theorem --- Universal quantification of equivalence implies equivalence - * of unique existential quantification. - */ - val uniqueExistentialEquivalenceDistribution = Theorem( - forall(z, P(z) <=> Q(z)) |- (existsOne(z, P(z)) <=> existsOne(z, Q(z))) - ) { - val yz = have(forall(z, P(z) <=> Q(z)) |- ((y === z) <=> P(y)) <=> ((y === z) <=> Q(y))) subproof { - have(forall(z, P(z) <=> Q(z)) |- forall(z, P(z) <=> Q(z))) by Hypothesis - val quant = thenHave(forall(z, P(z) <=> Q(z)) |- P(y) <=> Q(y)) by InstantiateForall(y) - - val lhs = have((forall(z, P(z) <=> Q(z)), ((y === z) <=> P(y))) |- ((y === z) <=> Q(y))) subproof { - have((P(y) <=> Q(y), ((y === z) <=> P(y))) |- ((y === z) <=> Q(y))) by Tautology - have(thesis) by Tautology.from(lastStep, quant) - } - val rhs = have((forall(z, P(z) <=> Q(z)), ((y === z) <=> Q(y))) |- ((y === z) <=> P(y))) subproof { - have((P(y) <=> Q(y), ((y === z) <=> Q(y))) |- ((y === z) <=> P(y))) by Tautology - have(thesis) by Tautology.from(lastStep, quant) - } - - have(thesis) by Tautology.from(lhs, rhs) - } - - val fy = thenHave(forall(z, P(z) <=> Q(z)) |- forall(y, ((y === z) <=> P(y)) <=> ((y === z) <=> Q(y)))) by RightForall - - have(forall(y, P(y) <=> Q(y)) |- (forall(y, P(y)) <=> forall(y, Q(y)))) by Restate.from(universalEquivalenceDistribution) - val univy = thenHave(forall(y, ((y === z) <=> P(y)) <=> ((y === z) <=> Q(y))) |- (forall(y, ((y === z) <=> P(y))) <=> forall(y, ((y === z) <=> Q(y))))) by InstPredSchema( - Map((P -> lambda(y, (y === z) <=> P(y))), (Q -> lambda(y, (y === z) <=> Q(y)))) - ) - - have(forall(z, P(z) <=> Q(z)) |- (forall(y, ((y === z) <=> P(y))) <=> forall(y, ((y === z) <=> Q(y))))) by Cut(fy, univy) - - thenHave(forall(z, P(z) <=> Q(z)) |- forall(z, forall(y, ((y === z) <=> P(y))) <=> forall(y, ((y === z) <=> Q(y))))) by RightForall - have(forall(z, P(z) <=> Q(z)) |- exists(z, forall(y, ((y === z) <=> P(y)))) <=> exists(z, forall(y, ((y === z) <=> Q(y))))) by Cut( - lastStep, - existentialEquivalenceDistribution of (P -> lambda(z, forall(y, (y === z) <=> P(y))), Q -> lambda(z, forall(y, (y === z) <=> Q(y)))) - ) - - thenHave(thesis) by Restate - } - - /** - * Theorem --- if atleast two distinct elements exist, then there is no unique - * existence - */ - val atleastTwoExist = Theorem( - (exists(x, P(x)) /\ !existsOne(x, P(x))) <=> exists(x, exists(y, P(x) /\ P(y) /\ !(x === y))) - ) { - val fwd = have((exists(x, P(x)) /\ !existsOne(x, P(x))) ==> exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) subproof { - have((P(x), ((x === y) /\ !P(y))) |- P(x) /\ !P(y)) by Restate - thenHave((P(x), ((x === y) /\ !P(y))) |- P(y) /\ !P(y)) by Substitution.ApplyRules(x === y) // contradiction - val xy = thenHave((P(x), ((x === y) /\ !P(y))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by Weakening - - have((P(x), (!(x === y) /\ P(y))) |- (!(x === y) /\ P(y) /\ P(x))) by Restate - thenHave((P(x), (!(x === y) /\ P(y))) |- exists(y, !(x === y) /\ P(y) /\ P(x))) by RightExists - val nxy = thenHave((P(x), (!(x === y) /\ P(y))) |- exists(x, exists(y, !(x === y) /\ P(y) /\ P(x)))) by RightExists - - have((P(x), (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by Tautology.from(xy, nxy) - thenHave((P(x), exists(y, (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y)))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by LeftExists - thenHave((P(x), forall(x, exists(y, (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y))))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by LeftForall - thenHave((exists(x, P(x)), forall(x, exists(y, (!(x === y) /\ P(y)) \/ ((x === y) /\ !P(y))))) |- exists(x, exists(y, P(x) /\ P(y) /\ !(x === y)))) by LeftExists - - thenHave(thesis) by Restate - } - - val bwd = have(exists(x, exists(y, P(x) /\ P(y) /\ !(x === y))) ==> (exists(x, P(x)) /\ !existsOne(x, P(x)))) subproof { - have((P(x), P(y), !(x === y)) |- P(x)) by Restate - val ex = thenHave((P(x), P(y), !(x === y)) |- exists(x, P(x))) by RightExists - - have((P(x), P(y), !(x === y)) |- P(y) /\ !(y === x)) by Restate - thenHave((P(x), P(y), !(x === y), (x === z)) |- P(y) /\ !(y === z)) by Substitution.ApplyRules(x === z) - thenHave((P(x), P(y), !(x === y), (x === z)) |- (P(y) /\ !(y === z)) \/ (!P(y) /\ (y === z))) by Weakening - val xz = thenHave((P(x), P(y), !(x === y), (x === z)) |- exists(y, (P(y) /\ !(y === z)) \/ (!P(y) /\ (y === z)))) by RightExists - - have((P(x), P(y), !(x === y), !(x === z)) |- (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z))) by Restate - val nxz = thenHave((P(x), P(y), !(x === y), !(x === z)) |- exists(x, (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z)))) by RightExists - - have((P(x), P(y), !(x === y)) |- exists(x, (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z)))) by Tautology.from(xz, nxz) - thenHave((P(x), P(y), !(x === y)) |- forall(z, exists(x, (P(x) /\ !(x === z)) \/ (!P(x) /\ (x === z))))) by RightForall - val uex = thenHave(P(x) /\ P(y) /\ !(x === y) |- !existsOne(z, P(z))) by Restate - - have(P(x) /\ P(y) /\ !(x === y) |- exists(x, P(x)) /\ !existsOne(z, P(z))) by Tautology.from(ex, uex) - thenHave(exists(y, P(x) /\ P(y) /\ !(x === y)) |- exists(x, P(x)) /\ !existsOne(z, P(z))) by LeftExists - thenHave(exists(x, exists(y, P(x) /\ P(y) /\ !(x === y))) |- exists(x, P(x)) /\ !existsOne(z, P(z))) by LeftExists - - thenHave(thesis) by Restate - } - - have(thesis) by Tautology.from(fwd, bwd) - } - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala deleted file mode 100644 index 6330e4caa..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/Comprehensions.scala +++ /dev/null @@ -1,201 +0,0 @@ -package lisa.maths.settheory - -import lisa.SetTheoryLibrary -import lisa.SetTheoryLibrary.* -import lisa.maths.settheory.functions.functional -import lisa.prooflib.BasicStepTactic.RightForall -import lisa.prooflib.BasicStepTactic.TacticSubproof -import lisa.prooflib.SimpleDeducedSteps.* -import lisa.utils.KernelHelpers.++<< -import lisa.utils.KernelHelpers.+<< -import lisa.utils.KernelHelpers.-<< -import lisa.utils.KernelHelpers.apply -import lisa.utils.{_, given} - -// Need to objects until https://github.com/lampepfl/dotty/pull/18647 is fixed. -// See also https://github.com/lampepfl/dotty/issues/18569 - -object Comprehensions { - import lisa.fol.FOL.{*, given} - import lisa.maths.settheory.SetTheory2.{primReplacement, replacement, functionalIsFunctional, onePointRule} - import lisa.automation.Tautology - import lisa.automation.Substitution - private val x = variable - private val y = variable - private val z = variable - private val A = variable - private val B = variable - private val C = variable - private val P = predicate[2] - private val Q = predicate[1] - private val Filter = predicate[1] - private val Map = function[1] - - given lib: lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary - - // Comprehension - - /** - * A Comprehension is a local definition of a set from a base set, a filter and a map. In Set builder notation, it denotes - * {map(x) | x in A /\ filter(x)}. - * It is represented by a variable usable locally in the proof. The assumptions corresponding to the definition of that variable are automatically eliminated. - * To obtain the defining property of the comprehension, use the [[elim]] fact. - * - * @param _proof the [[Proof]] in which the comprehension is valid - * @param t The base set - * @param filter A filter on elements of the base set - * @param map A map from elements of the base set to elements of the comprehension - * @param id The identifier of the variable encoding the comprehension. - */ - class Comprehension(_proof: Proof, val t: Term, val filter: (Term ** 1) |-> Formula, val map: (Term ** 1) |-> Term, id: Identifier) extends LocalyDefinedVariable(_proof, id) { - given proof.type = proof - - protected lazy val replacer: (Term ** 2) |-> Formula = lambda((A, B), filter(A) /\ (B === map(A))) - - private val mainFact = have( - ∃(B, ∀(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y)))).substitute(P := replacer) - ) subproof { - val s = have(thesis) by Tautology.from(primReplacement of (P := replacer), functionalIsFunctional of (Filter := filter, Map := map)) - } - - /** - * forall(y, in(y, B) <=> ∃(x, in(x, A) /\ filter(x) /\ (y === map(x)) - */ - override val definition: proof.Fact = assume(using proof)(forall(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y))).substitute(P := replacer, A := t, B := this)) - - val elem_bound = definingFormula.asInstanceOf[BinderFormula].bound - - protected val instDef: proof.Fact = { - have(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) by InstantiateForall(using SetTheoryLibrary, proof)(elem_bound)(definition) - } - - /** - * `in(elem, B) <=> ∃(x, in(x, A) /\ filter(x) /\ (elem === map(x))` - * if built with term.map, `in(elem, B) <=> ∃(x, in(x, A) /\ (elem === map(x))` - * if built with term.filter, `in(elem, B) <=> (in(elem, t) /\ filter(elem))` - */ - def elim(elem: Term) = instDef of (elem_bound := elem) - - // Add elimination to proof - { - val (compS, compI) = proof.sequentAndIntOfFact(mainFact of (A := t)) - val definU = definingFormula.underlying - val exDefinU = K.BinderFormula(K.Exists, underlyingLabel, definU) - _proof.addElimination( - definingFormula, - (i, sequent) => - val resSequent = (sequent.underlying -<< definU) - List( - SC.LeftExists(resSequent +<< exDefinU, i, definU, underlyingLabel), - SC.Cut(resSequent, compI, i + 1, exDefinU) - ) - ) - } - - def toStringFull: String = s"$id{${map(elem_bound)} | ${elem_bound} ∈ $t /\\ ${filter(elem_bound)}]}" - } - - // Replacement and Set Builders - - private def innerRepl(c: Variable, replacer: (Term ** 2) |-> Formula, t: Term): BinderFormula = // forall(x, in(x, y) <=> (in(x, t) /\ φ(x))) - ∀(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y) /\ ∀(z, P(x, z) ==> (z === y)))).substitute(P := replacer, A := t, y := c) - - // Axiom(exists(y, forall(x, in(x, y) <=> (in(x, z) /\ φ(x))))) - - class Replacement(_proof: Proof, val t: Term, val replacer: (Term ** 2) |-> Formula, id: Identifier) extends LocalyDefinedVariable(_proof, id) { - given proof.type = proof - - override val definition: proof.Fact = assume(using proof)(innerRepl(this, replacer, t)) - - val elem_bound = definingFormula.asInstanceOf[BinderFormula].bound - - protected val instDef: proof.Fact = { - InstantiateForall(using SetTheoryLibrary, proof)(elem_bound)(definition)(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) - .validate(summon[sourcecode.Line], summon[sourcecode.File]) - } - - // Add elimination to proof - { - val (compS, compI) = proof.sequentAndIntOfFact(replacement of (P := replacer, z := t, y := this)) - - val definU = definingFormula.underlying - val exDefinU = K.BinderFormula(K.Exists, underlyingLabel, definU) - - _proof.addElimination( - definingFormula, - (i, sequent) => - - val resSequent = (sequent.underlying -<< definU) - List( - SC.LeftExists(resSequent +<< exDefinU, i, definU, underlyingLabel), - SC.Cut(resSequent, compI, i + 1, exDefinU) - ) - ) - } - - /** - * in(elem, y) <=> ∃(x, in(x, t) /\ replacer(x, y) /\ ∀(z, P(x, z) ==> (z === y)) - */ - def elim(elem: Term): proof.Fact = instDef of (elem_bound := elem) - - def toStringFull: String = s"$id{$elem_bound | ${definition.asInstanceOf[BinderFormula].body.asInstanceOf[AppliedConnector].args(1)}]}" - } - - extension (t: Term) { - def replace(using _proof: Proof, name: sourcecode.Name)(replacer: (Term ** 2) |-> Formula): Replacement { val proof: _proof.type } = { - if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.allSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") - val id = name.value - val c = Replacement(_proof, t, replacer, id) - c.asInstanceOf[Replacement { val proof: _proof.type }] - } - - def collect(using _proof: Proof, name: sourcecode.Name)(filter: (Term ** 1) |-> Formula, map: (Term ** 1) |-> Term): Comprehension { val proof: _proof.type } = { - if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.allSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") - val id = name.value - val c = new Comprehension(_proof, t, filter, map, id) - c.asInstanceOf[Comprehension { val proof: _proof.type }] - } - - def map(using _proof: Proof, name: sourcecode.Name)(map: (Term ** 1) |-> Term): Comprehension { val proof: _proof.type } = { - if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.allSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") - val id = name.value - inline def _map = map - inline def _t = t - val c = new Comprehension(_proof, t, lambda(x, top), map, id) { - - override val instDef: proof.Fact = { - val elim_formula = (forall(elem_bound, in(elem_bound, B) <=> ∃(x, in(x, A) /\ P(x, elem_bound))).substitute(P := lambda((A, B), B === _map(A)), A := _t, B := this)).body - - have(TacticSubproof(using proof) { - val s = have(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) by InstantiateForall(elem_bound)(definition) - thenHave(definingFormula |- elim_formula) by Restate.from - }) - - } - } - c.asInstanceOf[Comprehension { val proof: _proof.type }] - } - - def filter(using _proof: Proof, name: sourcecode.Name)(filter: (Term ** 1) |-> Formula): Comprehension { val proof: _proof.type } = { - if (_proof.lockedSymbols ++ _proof.possibleGoal.toSet.flatMap(_.freeSchematicLabels)).map(_.id.name).contains(name.value) then throw new Exception(s"Name $name is already used in the proof") - val id = name.value - inline def _filter = filter - inline def _t = t - val c = new Comprehension(_proof, t, filter, lambda(x, x), id) { - - override val instDef: proof.Fact = { - have(TacticSubproof(using proof) { - val ex = new Variable(freshId(definingFormula.allSchematicLabels.map(_.id), "x")) - have(definingFormula |- definingFormula.asInstanceOf[BinderFormula].body) by InstantiateForall(elem_bound)(definition) - have(in(elem_bound, this) <=> ∃(ex, (ex === elem_bound) /\ in(ex, _t) /\ _filter(ex))) by Tautology.from(lastStep) - thenHave(in(elem_bound, this) <=> (in(elem_bound, _t) /\ _filter(elem_bound))) by Substitution.ApplyRules(onePointRule of (y := elem_bound, Q := lambda(ex, in(ex, _t) /\ _filter(ex)))) - }) - } - - } - c.asInstanceOf[Comprehension { val proof: _proof.type }] - } - - } - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala deleted file mode 100644 index cbc135a14..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/InductiveSets.scala +++ /dev/null @@ -1,159 +0,0 @@ -package lisa.maths - -//import lisa.automation.settheory.SetTheoryTactics.* -//import lisa.maths.Quantifiers.* -//import lisa.automation.Substitution - -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.settheory.SetTheory.* - -object InductiveSets extends lisa.Main { - - // var defs - private val a = variable - private val x = variable - private val y = variable - private val z = variable - private val r = variable - private val t = variable - - // relation and function symbols - - private val P = predicate[1] - private val schemPred = predicate[1] - - /** - * Chapter 2 - * Ordinal Numbers - */ - - /** - * Inductive and transitive sets - */ - - /** - * Theorem --- There exists an intersection of all inductive sets - */ - val inductiveIntersectionExistence = Theorem( - ∃(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y)))) - ) { - val inductExt = - have(∃(x, inductive(x)) |- ∃(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y))))) by InstPredSchema(Map(P -> lambda(x, inductive(x))))(intersectionOfPredicateClassExists) - have(∃(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y))))) by Cut(inductiveSetExists, inductExt) - } - - /** - * Theorem --- The intersection of all inductive sets is unique - */ - val inductiveIntersectionUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> ∀(y, inductive(y) ==> in(t, y)))) - ) { - val prop = ∀(y, inductive(y) ==> in(t, y)) - val fprop = ∀(t, in(t, z) <=> prop) - - val existsRhs = have(∃(z, fprop) |- ∃!(z, fprop)) by InstPredSchema(Map(schemPred -> (t, prop)))(uniqueByExtension) - val existsLhs = have(∃(z, fprop)) by Restate.from(inductiveIntersectionExistence) - - have(∃!(z, fprop)) by Cut(existsLhs, existsRhs) - } - - /** - * Natural Numbers (Inductive definition) --- The intersection of all - * inductive sets is the set of natural numbers, N. - */ - val naturalsInductive = DEF() --> The(z, ∀(t, in(t, z) <=> (∀(y, inductive(y) ==> in(t, y)))))(inductiveIntersectionUniqueness) - - /** - * Theorem --- Natural numbers form an inductive set - */ - val naturalsAreInductive = Theorem( - inductive(naturalsInductive) - ) { - val defHypo = have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- ∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) by Hypothesis - - // emptySet is in N - have(inductive(x) ==> in(∅, x)) by Weakening(inductive.definition) - val inductEmpty = thenHave(∀(x, inductive(x) ==> in(∅, x))) by RightForall - - val defEmpty = - have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(∅, z) <=> (∀(x, inductive(x) ==> in(∅, x))))) by InstantiateForall(∅)(defHypo) - - have( - ∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(∅, z) <=> (∀(x, inductive(x) ==> in(∅, x)))) /\ ∀(x, inductive(x) ==> in(∅, x)) - ) by RightAnd(defEmpty, inductEmpty) - val baseCase = thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- in(∅, z)) by Tautology - - // if y in N, then succ y in N - have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) by InstantiateForall(t)(defHypo) - thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z) |- (in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) by Weakening - thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z) |- (∀(x, inductive(x) ==> in(t, x)))) by Tautology - thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z) |- (inductive(x) ==> in(t, x))) by InstantiateForall(x) - val inInductive = thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z), inductive(x)) |- in(t, x)) by Restate - - have(inductive(x) |- ∀(t, in(t, x) ==> in(successor(t), x))) by Weakening(inductive.definition) - val inInductiveDef = thenHave(inductive(x) |- in(t, x) ==> in(successor(t), x)) by InstantiateForall(t) - - have((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z), inductive(x)) |- in(t, x) /\ (in(t, x) ==> in(successor(t), x))) by RightAnd(inInductive, inInductiveDef) - thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) /\ in(t, z), inductive(x)) |- in(successor(t), x)) by Tautology - thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- inductive(x) ==> in(successor(t), x)) by Restate - val succInst = thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- ∀(x, inductive(x) ==> in(successor(t), x))) by RightForall - - val nDefSucc = - have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- (in(successor(t), z) <=> (∀(x, inductive(x) ==> in(successor(t), x))))) by InstantiateForall(successor(t))(defHypo) - have( - (∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- ∀(x, inductive(x) ==> in(successor(t), x)) /\ (in(successor(t), z) <=> (∀( - x, - inductive(x) ==> in(successor(t), x) - ))) - ) by RightAnd(succInst, nDefSucc) - thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), in(t, z)) |- in(successor(t), z)) by Tautology - thenHave((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) |- in(t, z) ==> in(successor(t), z)) by Restate - val inductiveCase = thenHave(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- ∀(t, in(t, z) ==> in(successor(t), z))) by RightForall - - have(∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))) |- in(∅, z) /\ ∀(t, in(t, z) ==> in(successor(t), z))) by RightAnd(baseCase, inductiveCase) - - val form = formulaVariable - val inductIff = thenHave( - (∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x)))), inductive(z) <=> (in(∅, z) /\ ∀(y, in(y, z) ==> in(successor(y), z)))) |- inductive(z) - ) by RightSubstIff.withParametersSimple(List((inductive(z), in(∅, z) /\ ∀(y, in(y, z) ==> in(successor(y), z)))), lambda(form, form)) - - val inductDef = have(inductive(z) <=> (in(∅, z) /\ ∀(y, in(y, z) ==> in(successor(y), z)))) by InstFunSchema(Map(x -> z))(inductive.definition) - - have((∀(t, in(t, z) <=> (∀(x, inductive(x) ==> in(t, x))))) |- inductive(z)) by Cut(inductDef, inductIff) - val inductExpansion = - thenHave((forall(t, in(t, naturalsInductive) <=> (forall(x, inductive(x) ==> in(t, x))))) |- inductive(naturalsInductive)) by InstFunSchema(Map(z -> naturalsInductive)) - - have((naturalsInductive === naturalsInductive) <=> forall(t, in(t, naturalsInductive) <=> (forall(x, inductive(x) ==> in(t, x))))) by InstantiateForall(naturalsInductive)( - naturalsInductive.definition - ) - val natDef = thenHave(forall(t, in(t, naturalsInductive) <=> forall(x, inductive(x) ==> in(t, x)))) by Restate - - have(inductive(naturalsInductive)) by Cut(natDef, inductExpansion) - } - - private val A = variable - - /** - * A set `'A` is transitive if and only if every member of `'A` is a subset of `'A`. - * ∀ 'x. 'x∈ 'A ⟹ 'x ⊂ 'A - */ - val transitiveSet = DEF(A) --> forall(x, in(x, A) ==> subset(x, A)) - - /* - private val R = predicate(2) - /** - * Show that the restriction of a functional to a set exists. - */ - val predicateRestrictionExists = makeTHM( - ∃!(r, forall(x, forall(y, in(pair(x, y), r) <=> in(x, A) /\ in(y, A) /\ R(x, y)))) - ) { - val z1 = firstInPair(z) - val z2 = secondInPair(z) - - have ( ∃!(r, forall(z, in(z, r) <=> in(z, cartesianProduct(A, A)) /\ R(z1, z2)))) by UniqueComprehension(cartesianProduct(A, A), lambda(Seq(z, x), R(z1, z2))) - showCurrentProof() - - } - */ - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala deleted file mode 100644 index 3439bad44..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory.scala +++ /dev/null @@ -1,2614 +0,0 @@ -package lisa.maths.settheory - -import lisa.automation.kernel.CommonTactics.Definition -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.Quantifiers.* - -import scala.collection.immutable.{Map => ScalaMap} - -/** - * Set Theory Library - * - * Develops Zermelo-Fraenkel Set Theory. - * Uses the following book as the main reference: - * - * Jech, Thomas. Set theory: The third millennium edition, revised and expanded. - * Springer Berlin Heidelberg, 2003. - * [[https://link.springer.com/book/10.1007/3-540-44761-X]] - */ -object SetTheory extends lisa.Main { - - // var defs - private val w = variable - private val x = variable - private val y = variable - private val z = variable - private val h = formulaVariable - private val t = variable - private val a = variable - private val b = variable - private val c = variable - private val d = variable - - // relation and function symbols - private val r = variable - private val p = variable - private val f = variable - private val g = variable - - private val P = predicate[1] - private val Q = predicate[1] - private val R = predicate[2] - private val schemPred = predicate[1] - - /** - * Chapter 1 - */ - - /** - * Axioms of Set Theory - * - * See [[SetTheoryZAxioms]] and [[SetTheoryZFAxioms]] - */ - - /** - * Theorems about basic sets - */ - - /** - * Theorem --- Russel's Paradox - * - * Consider a set `x`, that contains every set that is not a member of itself. - * The existence of `x` leads to a contradiction. This paradox forces the - * current form of the comprehension schema, i.e. `{x ∈ X | Ψ(x, X)}` - * instead of the full comprehension schema `{x | Ψ(x)}`. - */ - val russelsParadox = Theorem( - ∃(x, ∀(y, !in(y, y) <=> in(y, x))) |- () - ) { - val contra = !in(x, x) <=> in(x, x) - - have(contra |- ()) by Restate - thenHave(∀(y, !in(y, y) <=> in(y, x)) |- ()) by LeftForall - thenHave(∃(x, ∀(y, !in(y, y) <=> in(y, x))) |- ()) by LeftExists - } - - /** - * Theorem --- Uniqueness by Definition - * - * If a set is defined by its elements, existence implies uniqueness. - * - * `∃ z. ∀ t. t ∈ z ⇔ P(t) ⊢ ∃! z. ∀ t. t ∈ z ⇔ P(t)` - * - * where `P(t)` does not contain `z` as a free variable. - * - * @example {{{ - * have(∃(z, ∀(t, in(t, z) ⇔ myProperty(t))) ⊢ ∃!(z, ∀(t, in(t, z) ⇔ myProperty(t)))) by InstPredSchema(ScalaMap(schemPred -> (t, myProperty(t))))` - * }}} - * - * Instantiation will fail if `myProperty(t)` contains `z` as a free variable. - */ - val uniqueByExtension = Theorem( - ∃(z, ∀(t, in(t, z) <=> schemPred(t))) |- ∃!(z, ∀(t, in(t, z) <=> schemPred(t))) - ) { - def prop(z: Term) = in(t, z) <=> schemPred(t) - def fprop(z: Term) = ∀(t, prop(z)) - - // forward direction - have(fprop(z) |- fprop(z)) by Hypothesis - thenHave(fprop(z) /\ (z === a) |- fprop(z)) by Weakening - thenHave((fprop(z) /\ (z === a), (z === a)) |- fprop(a)) by RightSubstEq.withParametersSimple(List((z, a)), lambda(Seq(z), fprop(z))) - val forward = thenHave(fprop(z) |- (z === a) ==> fprop(a)) by Restate - - // backward direction - have(fprop(z) |- fprop(z)) by Hypothesis - val instLhs = thenHave(fprop(z) |- prop(z)) by InstantiateForall(t) - val instRhs = thenHave(fprop(a) |- prop(a)) by InstFunSchema(ScalaMap(z -> a)) - - have((fprop(z), fprop(a)) |- prop(z) /\ prop(a)) by RightAnd(instLhs, instRhs) - thenHave(fprop(z) /\ fprop(a) |- in(t, a) <=> in(t, z)) by Tautology - val extLhs = thenHave(fprop(z) /\ fprop(a) |- ∀(t, in(t, a) <=> in(t, z))) by RightForall - val extRhs = have(∀(t, in(t, a) <=> in(t, z)) <=> (a === z)) by InstFunSchema(ScalaMap(x -> a, y -> z))(extensionalityAxiom) - - have(fprop(z) /\ fprop(a) |- (∀(t, in(t, a) <=> in(t, z)) <=> (a === z)) /\ ∀(t, in(t, a) <=> in(t, z))) by RightAnd(extLhs, extRhs) - thenHave(fprop(z) /\ fprop(a) |- (a === z)) by Tautology - val backward = thenHave(fprop(z) |- fprop(a) ==> (a === z)) by Restate - - have(fprop(z) |- fprop(a) <=> (a === z)) by RightIff(forward, backward) - thenHave(fprop(z) |- ∀(a, fprop(a) <=> (a === z))) by RightForall - thenHave(fprop(z) |- ∃(z, ∀(a, fprop(a) <=> (a === z)))) by RightExists - thenHave(∃(z, fprop(z)) |- ∃(z, ∀(a, fprop(a) <=> (a === z)))) by LeftExists - thenHave(∃(z, fprop(z)) |- ∃!(z, fprop(z))) by RightExistsOne - } - - ////////////////////////////////////////////////////////////////////////////// - - /** - * Shorthand definitions - */ - - /** - * Proper Subset --- `x ⊂ y`. Shorthand for `x ⊆ y ∧ x != y`. - * - * @param x set - * @param y set - */ - def properSubset(x: Term, y: Term) = subset(x, y) /\ !(x === y) - - /** - * Singleton Set --- `{x}`. Shorthand for `{x, x}`. - * - * @param x set - */ - def singleton(x: Term) = unorderedPair(x, x) - - /** - * Ordered Pair --- `(x, y)`. Shorthand for `{{x}, {x, y}}`. - * - * @param x set - * @param y set - */ - def pair(x: Term, y: Term) = unorderedPair(unorderedPair(x, y), singleton(x)) - - /** - * Binary Set Union --- `x ∪ y = ∪ {x, y}` - * - * @param x set - * @param y set - */ - val setUnion: ConstantFunctionLabel[2] = DEF(x, y) --> union(unorderedPair(x, y)) - - /** - * Theorem --- a set is an element of `x ∪ y` iff it is an element of `x` or `y` - */ - val setUnionMembership = Theorem( - in(z, setUnion(x, y)) <=> (in(z, x) \/ in(z, y)) - ) { - have(∀(z, (z === setUnion(x, y)) <=> (z === union(unorderedPair(x, y))))) by Restate.from(setUnion.definition) - thenHave((setUnion(x, y) === setUnion(x, y)) <=> (setUnion(x, y) === union(unorderedPair(x, y)))) by InstantiateForall(setUnion(x, y)) - val unionDef = thenHave((setUnion(x, y) === union(unorderedPair(x, y)))) by Restate - - val upairax = have(in(a, unorderedPair(x, y)) <=> ((a === x) \/ (a === y))) by Restate.from(pairAxiom of (z -> a)) - - val ta = have(in(z, union(unorderedPair(x, y))) <=> ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate.from(unionAxiom of (x -> unorderedPair(x, y))) - - have(thesis) subproof { - // the proof proceeds by showing that the existence criterion reduces to the RHS of the iff in the thesis - - val fwd = have(∃(a, in(z, a) /\ in(a, unorderedPair(x, y))) ==> (in(z, x) \/ in(z, y))) subproof { - have((in(z, a), a === x) |- in(z, a)) by Hypothesis - val tax = thenHave((in(z, a), a === x) |- in(z, x)) by RightSubstEq.withParametersSimple(List((a, x)), lambda(a, in(z, a))) - - have((in(z, a), a === y) |- in(z, a)) by Hypothesis - val tay = thenHave((in(z, a), a === y) |- in(z, y)) by RightSubstEq.withParametersSimple(List((a, y)), lambda(a, in(z, a))) - - have((in(z, a), (a === x) \/ (a === y)) |- (in(z, x), in(z, y))) by LeftOr(tax, tay) - thenHave((in(z, a), in(a, unorderedPair(x, y))) |- (in(z, x), in(z, y))) by Substitution.ApplyRules(upairax) - thenHave((in(z, a) /\ in(a, unorderedPair(x, y))) |- (in(z, x), in(z, y))) by Restate - thenHave(∃(a, in(z, a) /\ in(a, unorderedPair(x, y))) |- (in(z, x), in(z, y))) by LeftExists - thenHave(thesis) by Restate - } - - val bwd = have(((in(z, x) \/ in(z, y)) ==> ∃(a, in(z, a) /\ in(a, unorderedPair(x, y))))) subproof { - have((in(z, x), (a === x)) |- in(z, x)) by Hypothesis - thenHave((in(z, x), (a === x)) |- in(z, a)) by RightSubstEq.withParametersSimple(List((a, x)), lambda(a, in(z, a))) - thenHave((in(z, x), (a === x)) |- in(z, a) /\ ((a === x) \/ (a === y))) by Tautology - andThen(Substitution.applySubst(upairax, false)) - thenHave((in(z, x), (a === x)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by RightExists - thenHave((in(z, x), (x === x)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by InstFunSchema(ScalaMap(a -> x)) - val tax = thenHave((in(z, x)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate - - have((in(z, y), (a === y)) |- in(z, y)) by Hypothesis - thenHave((in(z, y), (a === y)) |- in(z, a)) by RightSubstEq.withParametersSimple(List((a, y)), lambda(a, in(z, a))) - thenHave((in(z, y), (a === y)) |- in(z, a) /\ ((a === x) \/ (a === y))) by Tautology - andThen(Substitution.applySubst(upairax, false)) - thenHave((in(z, y), (a === y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by RightExists - thenHave((in(z, y), (y === y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by InstFunSchema(ScalaMap(a -> y)) - val tay = thenHave((in(z, y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate - - have((in(z, x) \/ in(z, y)) |- ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by LeftOr(tax, tay) - thenHave(thesis) by Restate - } - - val existsSubst = have(∃(a, in(z, a) /\ in(a, unorderedPair(x, y))) <=> (in(z, x) \/ in(z, y))) by RightIff(fwd, bwd) - - have(in(z, union(unorderedPair(x, y))) <=> ∃(a, in(z, a) /\ in(a, unorderedPair(x, y)))) by Restate.from(ta) - thenHave(thesis) by Substitution.ApplyRules(existsSubst, unionDef) - } - - } - - /** - * Successor Function --- Maps a set to its 'successor' in the sense required - * for an inductive set. - * - * `successor: x ↦ x ∪ {x}` - * - * @param x set - */ - val successor = DEF(x: Variable) --> union(unorderedPair(x, singleton(x))) - - /** - * Inductive set --- A set is inductive if it contains the empty set, and the - * [[successor]]s of each of its elements. - * - * `inductive(x) ⇔ (∅ ∈ x ⋀ ∀ y. y ∈ x ⇒ successor(y) ∈ x)` - * - * @param x set - */ - val inductive = DEF(x) --> in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)) - - /** - * Theorem --- There exists an inductive set. - * - * `∃ x. inductive(x)` - * - * Equivalent to the Axiom of Infinity ([[infinityAxiom]]). The proof shows - * that the two forms are equivalent by folding the definitions of - * [[successor]] and [[inductive]]. - */ - val inductiveSetExists = Theorem( - ∃(x, inductive(x)) - ) { - val form = formulaVariable - - have(∀(x, (x === successor(y)) <=> (x === union(unorderedPair(y, unorderedPair(y, y)))))) by InstFunSchema(ScalaMap(x -> y))(successor.definition) - thenHave(((successor(y) === successor(y)) <=> (successor(y) === union(unorderedPair(y, unorderedPair(y, y)))))) by InstantiateForall(successor(y)) - val succDef = thenHave((successor(y) === union(unorderedPair(y, unorderedPair(y, y))))) by Restate - val inductDef = have(inductive(x) <=> in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x))) by Restate.from(inductive.definition) - - have((in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by Restate - val succEq = thenHave( - (successor(y) === union(unorderedPair(y, unorderedPair(y, y)))) |- (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(successor(y), x)) - ) by RightSubstEq.withParametersSimple( - List((successor(y), union(unorderedPair(y, unorderedPair(y, y))))), - lambda(z, (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(z, x))) - ) - val iffinst = have((in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> (in(y, x) ==> in(successor(y), x))) by Cut(succDef, succEq) - - val iffquant = { - have((in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- (in(y, x) ==> in(successor(y), x))) by Weakening(iffinst) - thenHave(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- (in(y, x) ==> in(successor(y), x))) by LeftForall - thenHave(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- ∀(y, in(y, x) ==> in(successor(y), x))) by RightForall - val lhs = thenHave(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) ==> ∀(y, in(y, x) ==> in(successor(y), x))) by Restate - - have((in(y, x) ==> in(successor(y), x)) |- (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by Weakening(iffinst) - thenHave(∀(y, in(y, x) ==> in(successor(y), x)) |- (in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by LeftForall - thenHave(∀(y, in(y, x) ==> in(successor(y), x)) |- ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by RightForall - val rhs = thenHave(∀(y, in(y, x) ==> in(successor(y), x)) ==> ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) by Restate - - have(∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x))) by RightIff(lhs, rhs) - } - - have( - in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) |- in(∅, x) /\ ∀( - y, - in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x) - ) - ) by Hypothesis - thenHave( - ( - ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x)), - in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) - ) |- in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)) - ) by RightSubstIff.withParametersSimple( - List((∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)), ∀(y, in(y, x) ==> in(successor(y), x)))), - lambda(form, in(∅, x) /\ form) - ) - val substituted = thenHave( - ( - inductive(x) <=> in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)), - ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x)), - in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) - ) |- inductive(x) - ) by RightSubstIff.withParametersSimple(List((inductive(x), in(∅, x) /\ ∀(y, in(y, x) ==> in(successor(y), x)))), lambda(form, form)) - val cut1 = have( - ( - ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) <=> ∀(y, in(y, x) ==> in(successor(y), x)), - in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)) - ) |- inductive(x) - ) by Cut(inductDef, substituted) - val cut2 = have((in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) |- inductive(x)) by Cut(iffquant, cut1) - - thenHave((in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x))) |- ∃(x, inductive(x))) by RightExists - val rhs = thenHave((∃(x, in(∅, x) /\ ∀(y, in(y, x) ==> in(union(unorderedPair(y, unorderedPair(y, y))), x)))) |- ∃(x, inductive(x))) by LeftExists - - have(∃(x, inductive(x))) by Cut(infinityAxiom, rhs) - } - - ////////////////////////////////////////////////////////////////////////////// - - /** - * Properties about the empty set and power sets - */ - - /** - * Theorem --- If a set has an element, then it is not the empty set. - * - * `y ∈ x ⊢ ! x = ∅` - */ - val setWithElementNonEmpty = Theorem( - in(y, x) |- !(x === ∅) - ) { - have((x === ∅) |- !in(y, x)) by Substitution.ApplyRules(x === ∅)(emptySetAxiom of (x := y)) - } - - /** - * Theorem --- A set containing no elements is equal to the empty set. - * - * `∀ y. ! y ∈ x ⊢ x = ∅` - * - * Converse of the empty set axiom ([[emptySetAxiom]]). - */ - val setWithNoElementsIsEmpty = Theorem( - ∀(y, !in(y, x)) |- (x === ∅) - ) { - have(!in(y, ∅)) by InstFunSchema(ScalaMap(x -> y))(emptySetAxiom) - thenHave(() |- (!in(y, ∅), in(y, x))) by Weakening - val lhs = thenHave(in(y, ∅) ==> in(y, x)) by Restate - - have(!in(y, x) |- !in(y, x)) by Hypothesis - thenHave(!in(y, x) |- (!in(y, x), in(y, ∅))) by Weakening - val rhs = thenHave(!in(y, x) |- in(y, x) ==> in(y, ∅)) by Restate - - have(!in(y, x) |- in(y, x) <=> in(y, ∅)) by RightIff(lhs, rhs) - thenHave(∀(y, !in(y, x)) |- in(y, x) <=> in(y, ∅)) by LeftForall - val exLhs = thenHave(∀(y, !in(y, x)) |- ∀(y, in(y, x) <=> in(y, ∅))) by RightForall - - have(∀(z, in(z, x) <=> in(z, ∅)) <=> (x === ∅)) by InstFunSchema(ScalaMap(x -> x, y -> ∅))(extensionalityAxiom) - val exRhs = thenHave(∀(y, in(y, x) <=> in(y, ∅)) <=> (x === ∅)) by Restate - - have(∀(y, !in(y, x)) |- (∀(y, in(y, x) <=> in(y, ∅)) <=> (x === ∅)) /\ ∀(y, in(y, x) <=> in(y, ∅))) by RightAnd(exLhs, exRhs) - thenHave(∀(y, !in(y, x)) |- (x === ∅)) by Tautology - } - - /** - * Theorem --- The empty set is a subset of every set. - * - * `∅ ⊆ x` - */ - val emptySetIsASubset = Theorem( - subset(∅, x) - ) { - have(in(y, ∅) ==> in(y, x)) by Weakening(emptySetAxiom of (x := y)) - val rhs = thenHave(∀(y, in(y, ∅) ==> in(y, x))) by RightForall - have(thesis) by Tautology.from(subsetAxiom of (x := ∅, y := x), rhs) - } - - /** - * Theorem --- If a set is a subset of the empty set, it is empty. - * - * `x ⊆ ∅ <=> a = ∅` - */ - val emptySetIsItsOwnOnlySubset = Theorem( - subset(x, emptySet) <=> (x === emptySet) - ) { - val fwd = have(subset(x, emptySet) |- (x === emptySet)) subproof { - have(subset(x, emptySet) |- forall(z, in(z, x) ==> in(z, emptySet))) by Weakening(subsetAxiom of y -> emptySet) - thenHave(subset(x, emptySet) |- in(z, x) ==> in(z, emptySet)) by InstantiateForall(z) - have(subset(x, emptySet) |- !in(z, x)) by Tautology.from(lastStep, emptySetAxiom of x -> z) - thenHave(subset(x, emptySet) |- forall(z, !in(z, x))) by RightForall - - have(thesis) by Cut(lastStep, setWithNoElementsIsEmpty) - } - - val bwd = have((x === emptySet) |- subset(x, emptySet)) subproof { - have(subset(emptySet, emptySet)) by Restate.from(emptySetIsASubset of x -> emptySet) - thenHave(thesis) by Substitution.ApplyRules(x === emptySet) - } - - have(thesis) by Tautology.from(fwd, bwd) - } - - /** - * Theorem --- A power set is never empty. - * - * `! P(x) = ∅` - */ - val powerSetNonEmpty = Theorem( - !(powerSet(x) === ∅) - ) { - have(thesis) by Tautology.from(emptySetIsASubset, powerAxiom of (x := ∅, y := x), setWithElementNonEmpty of (y := ∅, x := powerSet(x))) - } - - /** - * Theorem --- Subset reflexivity - * - * Every set is a [[subset]] of itself. In other words, the [[subset]] - * predicate induces a [[reflexive]] [[relation]] on sets. - */ - val subsetReflexivity = Theorem( - subset(x, x) - ) { - val subdef = have(subset(x, x) <=> ∀(z, ⊤)) by Restate.from(subsetAxiom of (y -> x)) - thenHave(subset(x, x) <=> ⊤) by Substitution.ApplyRules(closedFormulaUniversal) - thenHave(thesis) by Restate - } - - /** - * Theorem --- If a set belongs to a [[singleton]], it must be the single element. - * - * `y ∈ {x} <=> y = x` - */ - val singletonHasNoExtraElements = Theorem( - in(y, singleton(x)) <=> (x === y) - ) { - // specialization of the pair axiom to a singleton - - have(in(y, unorderedPair(x, x)) <=> (x === y) \/ (x === y)) by InstFunSchema(ScalaMap(x -> x, y -> x, z -> y))(pairAxiom) - thenHave(in(y, singleton(x)) <=> (x === y)) by Restate - } - - /** - * Theorem --- Every set is a member of its power set. - * - * `x ∈ P(x)` - */ - val elemInItsPowerSet = Theorem( - in(x, powerSet(x)) - ) { - have(thesis) by Tautology.from(powerAxiom of (y := x), subsetReflexivity) - } - - /** - * Lemma --- The power set of the empty set is the set containing the empty set. - * - * `P(∅) = {∅}` - */ - val powerSetEmptySet = Theorem( - powerSet(∅) === singleton(∅) - ) { - val powerAx = have(in(x, powerSet(∅)) <=> subset(x, ∅)) by Weakening(powerAxiom of (y := ∅)) - - have(in(x, powerSet(∅)) <=> in(x, singleton(∅))) by Tautology.from( - powerAx, - emptySetIsItsOwnOnlySubset, - singletonHasNoExtraElements of (y := x, x := ∅) - ) - val ext = thenHave(∀(x, in(x, powerSet(∅)) <=> in(x, singleton(∅)))) by RightForall - - have(thesis) by Tautology.from(ext, extensionalityAxiom of (x := powerSet(∅), y := singleton(∅))) - } - - ////////////////////////////////////////////////////////////////////////////// - - /** - * Properties about pairs - */ - - /** - * Theorem --- First Element in Pair - * - * `x ∈ {x, y}` - * - * Unfolds the definition of [[unorderedPair]]. Easier to use in theorems than - * the complete definition. - */ - val firstElemInPair = Theorem( - in(x, unorderedPair(x, y)) - ) { - have(thesis) by Tautology.from(pairAxiom of (z := x)) - } - - /** - * Theorem --- Second Element in Pair - * - * `y ∈ {x, y}` - * - * Unfolds the definition of [[unorderedPair]]. Easier to use in theorems than - * the complete definition. - */ - val secondElemInPair = Theorem( - in(y, unorderedPair(x, y)) - ) { - val lhs = have(in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) by InstFunSchema(ScalaMap(x -> x, y -> y, z -> z))(pairAxiom) - have((z === y) |- (z === y)) by Hypothesis - val rhs = thenHave((z === y) |- (z === x) \/ (z === y)) by Restate - val factset = have((z === y) |- (in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) /\ ((z === x) \/ (z === y))) by RightAnd(lhs, rhs) - - thenHave((z === y) |- in(z, unorderedPair(x, y))) by Tautology - thenHave((y === y) |- in(y, unorderedPair(x, y))) by InstFunSchema(ScalaMap(z -> y)) - thenHave(in(y, unorderedPair(x, y))) by LeftRefl - } - - /** - * Theorem --- The [[unorderedPair]] is, in fact, unordered. - * - * `{x, y} = {y, x}` - */ - val unorderedPairSymmetry = Theorem( - unorderedPair(x, y) === unorderedPair(y, x) - ) { - have(in(z, unorderedPair(y, x)) <=> in(z, unorderedPair(x, y))) by Substitution.ApplyRules(pairAxiom)(pairAxiom of (x := y, y := x)) - thenHave(∀(z, in(z, unorderedPair(x, y)) <=> in(z, unorderedPair(y, x)))) by RightForall - thenHave(thesis) by Substitution.ApplyRules(extensionalityAxiom) - } - - val unorderedPairDeconstruction = Theorem( - (unorderedPair(a, b) === unorderedPair(c, d)) ⊢ (((a === c) ∧ (b === d)) ∨ ((a === d) ∧ (b === c))) - ) { - val s1 = have(Substitution.applySubst(unorderedPair(a, b) === unorderedPair(c, d))(pairAxiom of (x -> a, y -> b))) - val base = have(Substitution.applySubst(s1)(pairAxiom of (x -> c, y -> d))) - - have(thesis) by Tautology.from(base of (z -> a), base of (z -> b), base of (z -> c), base of (z -> d)) - } - - /** - * Theorem --- Union of a Singleton is the Original Set - * - * The unary [[union]] operation unfolds a [[singleton]] into the single - * element - * - * `∀ x. ∪ {x} === x` - */ - val unionOfSingletonIsTheOriginalSet = Theorem((union(singleton(x)) === x)) { - val X = singleton(x) - val forward = have((in(z, x) ==> in(z, union(X)))) subproof { - have(in(z, x) |- in(z, x) /\ in(x, X)) by Tautology.from(pairAxiom of (y -> x, z -> x)) - val step2 = thenHave(in(z, x) |- ∃(y, in(z, y) /\ in(y, X))) by RightExists - have(thesis) by Tautology.from(step2, unionAxiom of (x -> X)) - } - - val backward = have(in(z, union(X)) ==> in(z, x)) subproof { - have(in(z, y) |- in(z, y)) by Restate - val step2 = thenHave((y === x, in(z, y)) |- in(z, x)) by Substitution.ApplyRules(y === x) - have(in(z, y) /\ in(y, X) |- in(z, x)) by Tautology.from(pairAxiom of (y -> x, z -> y), step2) - val step4 = thenHave(∃(y, in(z, y) /\ in(y, X)) |- in(z, x)) by LeftExists - have(in(z, union(X)) ==> in(z, x)) by Tautology.from(unionAxiom of (x -> X), step4) - } - - have(in(z, union(X)) <=> in(z, x)) by RightIff(forward, backward) - thenHave(∀(z, in(z, union(X)) <=> in(z, x))) by RightForall - andThen(Substitution.applySubst(extensionalityAxiom of (x -> union(X), y -> x))) - } - - /** - * Theorem --- Two [[unorderedPair]]s are equal iff their elements are equal pairwise. - * - * `{a, b} = {c, d} <=> ( (a = c ∧ b = d) ∨ (a = d ∧ b = c) )` - */ - val unorderedPairExtensionality = Theorem( - (unorderedPair(a, b) === unorderedPair(c, d)) <=> (((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c))) - ) { - // forward direction - // up ab = up cd |- a = c and b = d OR a = d and b = c - val fwd = have((unorderedPair(a, b) === unorderedPair(c, d)) ==> (((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c)))) by Restate.from(unorderedPairDeconstruction) - - // backward direction - // a = c and b = d => up ab = up cd (and the other case) - have(unorderedPair(a, b) === unorderedPair(a, b)) by RightRefl - thenHave((a === c, b === d) |- unorderedPair(a, b) === unorderedPair(c, d)) by RightSubstEq.withParametersSimple( - List((a, c), (b, d)), - lambda(Seq(x, y), unorderedPair(a, b) === unorderedPair(x, y)) - ) - val lhs = thenHave(Set((a === c) /\ (b === d)) |- unorderedPair(a, b) === unorderedPair(c, d)) by Restate - - have(unorderedPair(a, b) === unorderedPair(b, a)) by InstFunSchema(ScalaMap(x -> a, y -> b))(unorderedPairSymmetry) - thenHave((a === d, b === c) |- (unorderedPair(a, b) === unorderedPair(c, d))) by RightSubstEq.withParametersSimple( - List((a, d), (b, c)), - lambda(Seq(x, y), unorderedPair(a, b) === unorderedPair(y, x)) - ) - val rhs = thenHave(Set((a === d) /\ (b === c)) |- (unorderedPair(a, b) === unorderedPair(c, d))) by Restate - - have((((a === d) /\ (b === c)) \/ ((a === c) /\ (b === d))) |- (unorderedPair(a, b) === unorderedPair(c, d))) by LeftOr(lhs, rhs) - val bwd = thenHave((((a === d) /\ (b === c)) \/ ((a === c) /\ (b === d))) ==> (unorderedPair(a, b) === unorderedPair(c, d))) by Restate - - have((unorderedPair(a, b) === unorderedPair(c, d)) <=> (((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c)))) by RightIff(fwd, bwd) - } - - /** - * Theorem --- A singleton set is never empty. - * - * `! {x} = ∅` - */ - val singletonNonEmpty = Theorem( - !(singleton(x) === ∅) - ) { - val reflLhs = have(in(x, singleton(x)) <=> (x === x)) by InstFunSchema(ScalaMap(y -> x))(singletonHasNoExtraElements) - - val reflRhs = have((x === x)) by RightRefl - have((x === x) /\ (in(x, singleton(x)) <=> (x === x))) by RightAnd(reflLhs, reflRhs) - val lhs = thenHave(in(x, singleton(x))) by Tautology - - val rhs = have(in(x, singleton(x)) |- !(singleton(x) === ∅)) by InstFunSchema(ScalaMap(y -> x, x -> singleton(x)))(setWithElementNonEmpty) - - have(!(singleton(x) === ∅)) by Cut(lhs, rhs) - } - - /** - * Theorem --- Two singletons are equal iff their elements are equal - * - * `{x} = {y} <=> x = y` - */ - val singletonExtensionality = Theorem( - (singleton(x) === singleton(y)) <=> (x === y) - ) { - // forward direction - // {x} === {y} |- x === y - have(∀(z, in(z, singleton(x)) <=> in(z, singleton(y))) <=> (singleton(x) === singleton(y))) by InstFunSchema(ScalaMap(x -> singleton(x), y -> singleton(y)))(extensionalityAxiom) - thenHave((singleton(x) === singleton(y)) |- ∀(z, in(z, singleton(x)) <=> in(z, singleton(y)))) by Tautology - val singiff = thenHave((singleton(x) === singleton(y)) |- in(z, singleton(x)) <=> in(z, singleton(y))) by InstantiateForall(z) - - val singX = have(in(z, singleton(x)) <=> (z === x)) by InstFunSchema(ScalaMap(y -> z))(singletonHasNoExtraElements) - have((singleton(x) === singleton(y)) |- (in(z, singleton(x)) <=> in(z, singleton(y))) /\ (in(z, singleton(x)) <=> (z === x))) by RightAnd(singiff, singX) - val yToX = thenHave((singleton(x) === singleton(y)) |- (in(z, singleton(y)) <=> (z === x))) by Tautology - - val singY = have(in(z, singleton(y)) <=> (z === y)) by InstFunSchema(ScalaMap(x -> y))(singX) - have((singleton(x) === singleton(y)) |- (in(z, singleton(y)) <=> (z === x)) /\ (in(z, singleton(y)) <=> (z === y))) by RightAnd(yToX, singY) - thenHave((singleton(x) === singleton(y)) |- ((z === x) <=> (z === y))) by Tautology - thenHave((singleton(x) === singleton(y)) |- ((x === x) <=> (x === y))) by InstFunSchema(ScalaMap(z -> x)) - - thenHave((singleton(x) === singleton(y)) |- (x === y)) by Restate - val fwd = thenHave((singleton(x) === singleton(y)) ==> (x === y)) by Tautology - - // backward direction - // x === y |- {x} === {y} - have(singleton(x) === singleton(x)) by RightRefl - thenHave((x === y) |- singleton(x) === singleton(y)) by RightSubstEq.withParametersSimple(List((x, y)), lambda(a, singleton(x) === singleton(a))) - val bwd = thenHave((x === y) ==> (singleton(x) === singleton(y))) by Restate - - have((singleton(x) === singleton(y)) <=> (x === y)) by RightIff(fwd, bwd) - } - - /** - * Theorem --- Unordered pairs of elements of a set `x` are in its power set `P(x)`. - * - * `a ∈ x ∧ b ∈ x <=> {a, b} ∈ P(x)` - */ - val unorderedPairInPowerSet = Theorem( - (in(a, x) /\ in(b, x)) <=> in(unorderedPair(a, b), powerSet(x)) - ) { - - // forward - val fwd = have((in(a, x) /\ in(b, x)) ==> in(unorderedPair(a, b), powerSet(x))) subproof { - val axExpansion = have(in(unorderedPair(a, b), powerSet(x)) <=> ∀(z, in(z, unorderedPair(a, b)) ==> in(z, x))) by Tautology.from( - powerAxiom of (x -> unorderedPair(a, b), y -> x), - subsetAxiom of (x -> unorderedPair(a, b), y -> x) - ) - - val abToz = have(in(a, x) /\ in(b, x) |- ∀(z, in(z, unorderedPair(a, b)) ==> in(z, x))) subproof { - val pairAxab = have(in(z, unorderedPair(a, b)) |- (z === a) \/ (z === b)) by Tautology.from(pairAxiom of (x -> a, y -> b)) - - have(in(a, x) /\ in(b, x) |- in(a, x)) by Restate - val za = thenHave((in(a, x) /\ in(b, x), (z === a)) |- in(z, x)) by RightSubstEq.withParametersSimple(List((z, a)), lambda(a, in(a, x))) - have(in(a, x) /\ in(b, x) |- in(b, x)) by Restate - val zb = thenHave((in(a, x) /\ in(b, x), (z === b)) |- in(z, x)) by RightSubstEq.withParametersSimple(List((z, b)), lambda(a, in(a, x))) - - val zab = have((in(a, x) /\ in(b, x), (z === a) \/ (z === b)) |- in(z, x)) by LeftOr(za, zb) - - have((in(z, unorderedPair(a, b)), in(a, x) /\ in(b, x)) |- in(z, x)) by Cut(pairAxab, zab) - thenHave(in(a, x) /\ in(b, x) |- in(z, unorderedPair(a, b)) ==> in(z, x)) by Restate - thenHave(thesis) by RightForall - } - - have(thesis) by Tautology.from(abToz, axExpansion) - } - - val bwd = have(in(unorderedPair(a, b), powerSet(x)) ==> (in(a, x) /\ in(b, x))) subproof { - have(in(unorderedPair(a, b), powerSet(x)) |- ∀(z, in(z, unorderedPair(a, b)) ==> in(z, x))) by Tautology.from( - powerAxiom of (x -> unorderedPair(a, b), y -> x), - subsetAxiom of (x -> unorderedPair(a, b), y -> x) - ) - val upz = thenHave(in(unorderedPair(a, b), powerSet(x)) |- in(z, unorderedPair(a, b)) ==> in(z, x)) by InstantiateForall(z) - - val xa = have(in(unorderedPair(a, b), powerSet(x)) |- in(a, x)) by Tautology.from(upz of (z -> a), firstElemInPair of (x -> a, y -> b)) - val xb = have(in(unorderedPair(a, b), powerSet(x)) |- in(b, x)) by Tautology.from(upz of (z -> b), secondElemInPair of (x -> a, y -> b)) - have(in(unorderedPair(a, b), powerSet(x)) |- in(b, x) /\ in(a, x)) by RightAnd(xa, xb) - thenHave(thesis) by Restate - } - - have(thesis) by RightIff(fwd, bwd) - } - - /** - * Theorem --- Pair Extensionality - * - * Two ordered pairs are equal iff their elements are equal when taken in order. - * - * `pair(a, b) = {{a}, {a, b}}` - * - * `pair(a, b) = pair(c, d) <=> a = c ∧ b = d` - */ - val pairExtensionality = Theorem( - (pair(a, b) === pair(c, d)) <=> ((a === c) /\ (b === d)) - ) { - // forward direction - // (a === c) /\ (b === d) ==> pair a b === pair c d - val fwd = have(((a === c) /\ (b === d)) ==> (pair(a, b) === pair(c, d))) subproof { - have((pair(a, b) === pair(a, b))) by RightRefl - thenHave(Set((a === c), (b === d)) |- (pair(a, b) === pair(c, d))) by RightSubstEq.withParametersSimple(List((a, c), (b, d)), lambda(Seq(x, y), pair(a, b) === pair(x, y))) - thenHave(thesis) by Restate - } - - // backward direction - // pair a b === pair c d ==> (a === c) /\ (b === d) - val bwd = have((pair(a, b) === pair(c, d)) ==> ((a === c) /\ (b === d))) subproof { - have(((pair(a, b) === pair(c, d))) |- (pair(a, b) === pair(c, d))) by Hypothesis - val lhs1 = thenHave( - ( - (pair(a, b) === pair(c, d)), - (unorderedPair(unorderedPair(a, b), singleton(a)) === unorderedPair(unorderedPair(c, d), singleton(c))) <=> (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton( - c - ))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton(a) === unorderedPair(c, d)))) - ) |- (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton(c))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton(a) === unorderedPair(c, d)))) - ) by RightSubstIff.withParametersSimple( - List( - ( - (unorderedPair(unorderedPair(a, b), singleton(a)) === unorderedPair(unorderedPair(c, d), singleton(c))), - (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton(c))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton(a) === unorderedPair(c, d)))) - ) - ), - lambda(h, h) - ) - have( - Set((pair(a, b) === pair(c, d))) |- (((unorderedPair(a, b) === unorderedPair(c, d)) /\ (singleton(a) === singleton(c))) \/ ((unorderedPair(a, b) === singleton(c)) /\ (singleton( - a - ) === unorderedPair(c, d)))) - ) by Cut(unorderedPairExtensionality of (a -> unorderedPair(a, b), b -> singleton(a), c -> unorderedPair(c, d), d -> singleton(c)), lhs1) - andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> b, c -> c, d -> d))) // {a, b} = {c, d} - andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> a, c -> c, d -> d))) // {a} = {c, d} - andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> b, c -> c, d -> c))) // {a, b} = {c} - andThen(Substitution.applySubst(unorderedPairExtensionality of (a -> a, b -> a, c -> c, d -> c))) // {a} = {c} - val expandedProp = thenHave( - ( - (pair(a, b) === pair(c, d)) - ) |- ((((a === c) /\ (b === d)) \/ ((a === d) /\ (b === c))) /\ (((a === c) /\ (a === c)) \/ ((a === c) /\ (a === c)))) \/ ((((a === c) /\ (b === c)) \/ ((a === c) /\ (b === c))) /\ (((a === c) /\ (a === d)) \/ ((a === d) /\ (a === c)))) - ) by Restate - val ac = thenHave(Set((pair(a, b) === pair(c, d))) |- (a === c)) by Tautology - - // required subproof, transitivity of equality - // b = c, a = d, a = c |- b = d - val transEqdb = have((d === a, a === c, c === b) |- d === b) subproof { - val dac = have((d === a) /\ (a === c) |- (d === c)) by Restate.from(equalityTransitivity of (x -> d, y -> a, z -> c)) - have((d === c) /\ (c === b) |- (d === b)) by Restate.from(equalityTransitivity of (x -> d, y -> c, z -> b)) - val dcb = thenHave(Set((d === c), (c === b)) |- (d === b)) by Restate - val db = have(((d === a) /\ (a === c), (c === b)) |- (d === b)) by Cut(dac, dcb) - - thenHave(thesis) by Restate - } - - val db = have(((pair(a, b) === pair(c, d))) |- (a === c) /\ (b === d)) by Tautology.from(expandedProp, ac, transEqdb) - thenHave(thesis) by Restate - } - - have(thesis) by RightIff(fwd, bwd) - } - - /** - * Theorem --- No set is an element of itself. - * - * `! x ∈ x` - * - * This is imposed by the Foundation Axiom ([[foundationAxiom]]). - */ - val selfNonInclusion = Theorem( - !in(x, x) - ) { - val X = singleton(x) - - have(!(X === ∅) ==> ∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)))) by InstFunSchema(ScalaMap(x -> X))(foundationAxiom) - val lhs = thenHave(!(X === ∅) |- ∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)))) by Restate - - have(in(y, X) |- in(y, X) <=> (x === y)) by Weakening(singletonHasNoExtraElements) - val innerRhs = thenHave(in(y, X) |- (x === y)) by Tautology - - have((in(x, X), (in(z, X) ==> !in(z, x)), in(y, X)) |- in(z, X) ==> !in(z, x)) by Hypothesis - thenHave((in(x, X), ∀(z, in(z, X) ==> !in(z, x)), in(y, X)) |- in(z, X) ==> !in(z, x)) by LeftForall - thenHave((in(x, X), ∀(z, in(z, X) ==> !in(z, x)), in(x, X)) |- in(x, X) ==> !in(x, x)) by InstFunSchema(ScalaMap(z -> x, y -> x)) - val coreRhs = thenHave(in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x)) |- !in(x, x)) by Restate - - // now we need to show that the assumption is indeed true - // this requires destruction of the existential quantifier in lhs - have(in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x)) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by Hypothesis - val innerRhs2 = thenHave((in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)), x === y) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by LeftSubstEq.withParametersSimple( - List((x, y)), - lambda(Seq(y), in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) - ) - - have((in(y, X), in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by Cut(innerRhs, innerRhs2) - thenHave(in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y)) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by Restate - val coreLhs = thenHave(∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) |- in(x, X) /\ ∀(z, in(z, X) ==> !in(z, x))) by LeftExists - - val rhs = have(∃(y, in(y, X) /\ ∀(z, in(z, X) ==> !in(z, y))) |- !in(x, x)) by Cut(coreLhs, coreRhs) - - val finRhs = have(!(X === ∅) |- !in(x, x)) by Cut(lhs, rhs) - val finLhs = have(!(X === ∅)) by Restate.from(singletonNonEmpty) - - have(!in(x, x)) by Cut(finLhs, finRhs) - } - - /** - * Theorem --- No Universal Set - * - * `∀ z. z ∈ x ⊢ ⊥` - * - * There does not exist a set of all sets. Alternatively, its existence, with - * the [[comprehensionSchema]] and Russel's paradox ([[russelsParadox]]), produces - * a contradiction. - */ - val noUniversalSet = Theorem( - ∀(z, in(z, x)) |- () - ) { - have(in(x, x) |- ()) by Restate.from(selfNonInclusion) - thenHave(∀(z, in(z, x)) |- ()) by LeftForall - } - - /** - * Theorem --- The power set of any set is not a proper subset of it. - * - * `∃ x. P(x) ⊂ x ⊢ ⊥` - */ - val powerSetNonInclusion = Theorem( - exists(x, properSubset(powerSet(x), x)) |- () - ) { - val lhs = have(subset(powerSet(x), x) |- subset(powerSet(x), x)) by Hypothesis - - val rhs = have(in(powerSet(x), powerSet(x)) <=> subset(powerSet(x), x)) by InstFunSchema(ScalaMap(x -> powerSet(x), y -> x))(powerAxiom) - - have(subset(powerSet(x), x) |- subset(powerSet(x), x) /\ (in(powerSet(x), powerSet(x)) <=> subset(powerSet(x), x))) by RightAnd(lhs, rhs) - val contraLhs = thenHave(subset(powerSet(x), x) |- in(powerSet(x), powerSet(x))) by Tautology - - val contraRhs = have(!in(powerSet(x), powerSet(x))) by InstFunSchema(ScalaMap(x -> powerSet(x)))(selfNonInclusion) - - have(subset(powerSet(x), x) |- !in(powerSet(x), powerSet(x)) /\ in(powerSet(x), powerSet(x))) by RightAnd(contraLhs, contraRhs) - thenHave(subset(powerSet(x), x) |- ()) by Restate - thenHave(subset(powerSet(x), x) |- (x === powerSet(x))) by Weakening - thenHave(properSubset(powerSet(x), x) |- ()) by Restate - thenHave(∃(x, properSubset(powerSet(x), x)) |- ()) by LeftExists - } - - val inclusionAntiSymmetric = Theorem( - !(in(x, y) /\ in(y, x)) - ) { - assume(in(x, y)) - assume(in(y, x)) - - // consider the set u = {x, y} - val u = unorderedPair(x, y) - - // u is not the empty set - have(in(x, u)) by Weakening(firstElemInPair) - have(!(u === emptySet)) by Tautology.from(lastStep, setWithElementNonEmpty of (y -> x, x -> u)) - - // by Foundation, there must be an inclusion minimal element in u - val minimal = have(exists(z, in(z, u) /\ forall(t, in(t, u) ==> !in(t, z)))) by Tautology.from(lastStep, foundationAxiom of x -> u) - // negation = forall(z, in(z, u) ==> exists(t, in(t, u) /\ in(t, z))) - - // it can only be x or y - val zxy = have(in(z, u) <=> ((z === x) \/ (z === y))) by Weakening(pairAxiom) - - // but x \cap u contains y, and y \cap u contains x - have(in(x, u) /\ in(x, y)) by Tautology.from(firstElemInPair) - thenHave((z === y) |- in(x, u) /\ in(x, z)) by Substitution.ApplyRules(z === y) - val zy = thenHave((z === y) |- exists(t, in(t, u) /\ in(t, z))) by RightExists - - have(in(y, u) /\ in(y, x)) by Tautology.from(secondElemInPair) - thenHave((z === x) |- in(y, u) /\ in(y, z)) by Substitution.ApplyRules(z === x) - val zx = thenHave((z === x) |- exists(t, in(t, u) /\ in(t, z))) by RightExists - - // this is a contradiction - have(in(z, u) ==> exists(t, in(t, u) /\ in(t, z))) by Tautology.from(zxy, zx, zy) - thenHave(forall(z, in(z, u) ==> exists(t, in(t, u) /\ in(t, z)))) by RightForall - - have(thesis) by Tautology.from(lastStep, minimal) - } - - ////////////////////////////////////////////////////////////////////////////// - - /** - * Operations on Sets - */ - - val setIntersectionUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ in(t, y)))) - ) { - have(∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ in(t, y))))) by UniqueComprehension(x, lambda(t, in(t, y))) - } - - /** - * Binary Set Intersection --- Intersection of two sets. - * - * `x ∩ y = {z ∈ x | z ∈ y}` - * - * The proofs are guaranteed and generated by [[UniqueComprehension]]. - * - * @param x set - * @param y set - */ - val setIntersection = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, x) /\ in(t, y))))(setIntersectionUniqueness) - - val setIntersectionCommutativity = Theorem( - setIntersection(x, y) === setIntersection(y, x) - ) { - sorry - } - - val setIntersectionMembership = Theorem( - in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y)) - ) { - sorry - } - - extension (x: Term) { - infix def ∩(y: Term) = setIntersection(x, y) - } - - val intersectionOfSubsets = Lemma( - subset(x, y) |- setIntersection(x, y) === x - ) { - have(forall(t, in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y)))) by InstantiateForall(setIntersection(x, y))(setIntersection.definition) - val txy = thenHave(in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y))) by InstantiateForall(t) - - have(subset(x, y) |- forall(t, in(t, x) ==> in(t, y))) by Weakening(subsetAxiom) - thenHave(subset(x, y) |- in(t, x) ==> in(t, y)) by InstantiateForall(t) - - have(subset(x, y) |- in(t, setIntersection(x, y)) <=> in(t, x)) by Tautology.from(lastStep, txy) - thenHave(subset(x, y) |- forall(t, in(t, setIntersection(x, y)) <=> in(t, x))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> setIntersection(x, y), y -> x)) - } - - val unaryIntersectionUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))) - ) { - val uniq = have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) by UniqueComprehension(union(x), lambda(t, ∀(b, in(b, x) ==> in(t, b)))) - - val lhs = have((in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b))) |- ∀(b, in(b, x) ==> in(t, b)) /\ exists(b, in(b, x))) subproof { - val unionAx = have(in(t, union(x)) |- exists(b, in(b, x) /\ in(t, b))) by Weakening(unionAxiom of (z -> t)) - - have(in(b, x) /\ in(t, b) |- in(b, x)) by Restate - thenHave(in(b, x) /\ in(t, b) |- exists(b, in(b, x))) by RightExists - val exRed = thenHave(exists(b, in(b, x) /\ in(t, b)) |- exists(b, in(b, x))) by LeftExists - - have(in(t, union(x)) |- exists(b, in(b, x))) by Cut(unionAx, exRed) - thenHave(thesis) by Tautology - } - - val rhs = have(∀(b, in(b, x) ==> in(t, b)) /\ exists(b, in(b, x)) |- (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))) subproof { - val unionAx = have(in(t, union(x)) <=> exists(z, in(z, x) /\ in(t, z))) by Restate.from(unionAxiom of (z -> t)) - - have((in(b, x), in(b, x) ==> in(t, b)) |- in(b, x) /\ (in(t, b))) by Tautology - thenHave((in(b, x), forall(b, in(b, x) ==> in(t, b))) |- in(b, x) /\ in(t, b)) by LeftForall - thenHave((in(b, x), forall(b, in(b, x) ==> in(t, b))) |- exists(b, in(b, x) /\ in(t, b))) by RightExists - val exRed = thenHave((exists(b, (in(b, x))), forall(b, in(b, x) ==> in(t, b))) |- exists(b, in(b, x) /\ in(t, b))) by LeftExists - - have(thesis) by Tautology.from(unionAx, exRed) - } - - have(() |- (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))) <=> (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b))))) by Tautology.from(lhs, rhs) - thenHave(() |- forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))) <=> (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) by RightForall - - have(() |- forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))) <=> forall(t, (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) by Cut( - lastStep, - universalEquivalenceDistribution of (P -> lambda(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))), Q -> lambda( - t, - (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))) - )) - ) - thenHave( - () |- forall(z, forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))) <=> forall(t, (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) - ) by RightForall - - have( - () |- existsOne(z, forall(t, (in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) <=> existsOne(z, forall(t, (in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))))) - ) by Cut( - lastStep, - uniqueExistentialEquivalenceDistribution of (P -> lambda(z, forall(t, in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b))))), Q -> lambda( - z, - forall(t, in(t, z) <=> (in(t, union(x)) /\ ∀(b, in(b, x) ==> in(t, b)))) - )) - ) - have(thesis) by Tautology.from(lastStep, uniq) - } - - /** - * Unary Set Intersection --- Intersection of all elements of a given set. - * - * `∩ x = {z ∈ ∪ x | ∀ y ∈ x. z ∈ y}` - * - * The proofs are guaranteed and generated by [[UniqueComprehension]]. - * - * @param x set - */ - val unaryIntersection = DEF(x) --> The(z, ∀(t, in(t, z) <=> (exists(b, in(b, x)) /\ ∀(b, in(b, x) ==> in(t, b)))))(unaryIntersectionUniqueness) - - val setDifferenceUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ !in(t, y)))) - ) { - have(∃!(z, ∀(t, in(t, z) <=> (in(t, x) /\ !in(t, y))))) by UniqueComprehension(x, lambda(t, !in(t, y))) - } - - /** - * Binary Set Difference --- Given two sets, produces the set that contains - * elements in the first but not in the second. - * - * `x - y = {z ∈ x | ! z ∈ y}` - * - * The proofs are guaranteed and generated by [[UniqueComprehension]]. - * - * @param x set - * @param y set - */ - val setDifference = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, x) /\ !in(t, y))))(setDifferenceUniqueness) - - /** - * Theorem --- Intersection of a non-empty Class is a Set - * - * There exists a set that is the intersection of all sets satisfying a given - * formula. With classes, this means that the unary intersection of a class - * defined by a predicate is a set. - * - * `∃ x. P(x) ⊢ ∃ z. t ∈ z ⇔ ∀ x. P(x) ⇒ t ∈ x` - */ - val intersectionOfPredicateClassExists = Theorem( - ∃(x, P(x)) |- ∃(z, ∀(t, in(t, z) <=> ∀(y, P(y) ==> in(t, y)))) - ) { - have(∃(z, ∀(t, in(t, z) <=> (in(t, x) /\ φ(t))))) by InstFunSchema(ScalaMap(z -> x))(comprehensionSchema) - - val conjunction = thenHave(∃(z, ∀(t, in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) by InstPredSchema(ScalaMap(φ -> lambda(t, ∀(y, P(y) ==> in(t, y))))) - - have(∀(y, P(y) ==> in(t, y)) |- ∀(y, P(y) ==> in(t, y))) by Hypothesis - thenHave(∀(y, P(y) ==> in(t, y)) /\ P(x) |- ∀(y, P(y) ==> in(t, y))) by Weakening - thenHave(∀(y, P(y) ==> in(t, y)) /\ P(x) |- P(x) ==> in(t, x)) by InstantiateForall(x) - thenHave(∀(y, P(y) ==> in(t, y)) /\ P(x) |- in(t, x) /\ ∀(y, P(y) ==> in(t, y))) by Tautology - val fwd = thenHave(P(x) |- ∀(y, P(y) ==> in(t, y)) ==> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by Restate - - have((in(t, x) /\ ∀(y, P(y) ==> in(t, y))) |- (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by Hypothesis - val bwd = thenHave((in(t, x) /\ ∀(y, P(y) ==> in(t, y))) ==> (∀(y, P(y) ==> in(t, y)))) by Restate - - val lhs = have(P(x) |- ∀(y, P(y) ==> in(t, y)) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by RightIff(fwd, bwd) - - val form = formulaVariable - have((in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) |- in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))) by Hypothesis - val rhs = thenHave( - Set((in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))), (∀(y, P(y) ==> in(t, y)) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))) |- (in(t, z) <=> (∀(y, P(y) ==> in(t, y)))) - ) by RightSubstIff.withParametersSimple(List((∀(y, P(y) ==> in(t, y)), (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))), lambda(form, in(t, z) <=> (form))) - - have((P(x), (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))) |- in(t, z) <=> (∀(y, P(y) ==> in(t, y)))) by Cut(lhs, rhs) - thenHave((P(x), ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) |- in(t, z) <=> (∀(y, P(y) ==> in(t, y)))) by LeftForall - thenHave((P(x), ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) |- ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y))))) by RightForall - thenHave((P(x), ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y)))))) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by RightExists - val cutRhs = thenHave((P(x), ∃(z, ∀(t, (in(t, z) <=> (in(t, x) /\ ∀(y, P(y) ==> in(t, y))))))) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by LeftExists - - have(P(x) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by Cut(conjunction, cutRhs) - thenHave(∃(x, P(x)) |- ∃(z, ∀(t, in(t, z) <=> (∀(y, P(y) ==> in(t, y)))))) by LeftExists - - } - - /** - * The first element of an ordered [[pair]] --- `first p = ∪ ∩ p` - * - * If `p = (a, b) = {{a}, {a, b}}`, `∩ p = {a}`, and `∪ ∩ p = a`. - * - * While the function is defined on all sets, the result on non-pairs may be - * uninteresting or garbage. Generally expected to be used via - * [[firstInPairReduction]]. - */ - val firstInPair = DEF(p) --> union(unaryIntersection(p)) - - val secondInPairSingletonUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> (in(t, union(p)) /\ ((!(union(p) === unaryIntersection(p))) ==> (!in(t, unaryIntersection(p))))))) - ) { - have(thesis) by UniqueComprehension(union(p), lambda(t, ((!(union(p) === unaryIntersection(p))) ==> (!in(t, unaryIntersection(p)))))) - } - - /** - * See [[secondInPair]]. - */ - val secondInPairSingleton = - DEF(p) --> The(z, ∀(t, in(t, z) <=> (in(t, union(p)) /\ ((!(union(p) === unaryIntersection(p))) ==> (!in(t, unaryIntersection(p)))))))(secondInPairSingletonUniqueness) - - /** - * The second element of an ordered [[pair]] --- - * - * `second p = ∪ {x ∈ ∪ p | ∪ p != ∩ p ⟹ !x ∈ ∩ p} = ∪ (secondSingleton p)` - * - * There is a more naive definition: `second p = ∪ (∪ p - (first p))`. - * If `p = (a, b) = {{a}, {a, b}}`, `∪ p = {a, b}`, and `∪ p - (first p) - * = {a, b} - {a} = {b}`, the `∪` at the top level reduces this to `b`. - * However, this fails when `a = b`, and returns the [[emptySet]]. - * - * While the function is defined on all sets, the result on non-pairs may be - * uninteresting or garbage. Generally expected to be used via - * [[secondInPairReduction]]. - * - * @see https://en.wikipedia.org/wiki/Ordered_pair#Kuratowski's_definition - */ - val secondInPair = DEF(p) --> union(secondInPairSingleton(p)) - - /** - * Theorem --- The union of an ordered pair is the corresponding unordered pair. - * - * `∪ (x, y) = ∪ {{x}, {x, y}} = {x, y}` - */ - val unionOfOrderedPair = Theorem( - () |- (union(pair(x, y)) === unorderedPair(x, y)) - ) { - val upElem = have(in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) by Restate.from(pairAxiom) - - val unionElem = have(in(z, union(pair(x, y))) <=> ((z === x) \/ (z === y))) subproof { - // expand being in \cup (x, y) - val unionax = have(in(z, union(pair(x, y))) <=> exists(b, in(b, pair(x, y)) /\ in(z, b))) by Restate.from(unionAxiom of x -> pair(x, y)) - - // what does it mean for b to be in (x, y)? - // b \in (x, y) /\ z \in b <=> z = x \/ z = y - val fwd = have(exists(b, in(b, pair(x, y)) /\ in(z, b)) |- ((z === x) \/ (z === y))) subproof { - val bxy = - have(in(b, pair(x, y)) /\ in(z, b) |- ((b === unorderedPair(x, y)) \/ (b === unorderedPair(x, x)))) by Weakening(pairAxiom of (x -> unorderedPair(x, y), y -> unorderedPair(x, x), z -> b)) - val zxy = have((b === unorderedPair(x, y)) |- in(z, b) <=> ((z === x) \/ (z === y))) by Substitution.ApplyRules(b === unorderedPair(x, y))(pairAxiom) - val zxx = have((b === unorderedPair(x, x)) |- in(z, b) <=> ((z === x) \/ (z === x))) by Substitution.ApplyRules(b === unorderedPair(x, x))(pairAxiom of y -> x) - - have(in(b, pair(x, y)) /\ in(z, b) |- ((z === x) \/ (z === y))) by Tautology.from(bxy, zxy, zxx) - thenHave(thesis) by LeftExists - } - - val bwd = have(((z === x) \/ (z === y)) |- exists(b, in(b, pair(x, y)) /\ in(z, b))) subproof { - val xyp = have(in(unorderedPair(x, y), pair(x, y))) by Restate.from(firstElemInPair of (x -> unorderedPair(x, y), y -> unorderedPair(x, x))) - val zx = have((z === x) |- in(z, unorderedPair(x, y))) by Substitution.ApplyRules(z === x)(firstElemInPair) - val zy = have((z === y) |- in(z, unorderedPair(x, y))) by Substitution.ApplyRules(z === y)(secondElemInPair) - - have(((z === x) \/ (z === y)) |- in(unorderedPair(x, y), pair(x, y)) /\ in(z, unorderedPair(x, y))) by Tautology.from(xyp, zx, zy) - thenHave(thesis) by RightExists - } - - have(thesis) by Tautology.from(fwd, bwd, unionax) - } - - have(in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y))) by Tautology.from(upElem, unionElem) - thenHave(forall(z, in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y)))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> union(pair(x, y)), y -> unorderedPair(x, y))) - } - - /** - * Theorem --- The unary intersection of an ordered pair is the singleton - * containing the first element. - * - * `∩ (x, y) = ∩ {{x}, {x, y}} = {x}` - */ - val pairUnaryIntersection = Theorem( - () |- in(z, unaryIntersection(pair(x, y))) <=> (z === x) - ) { - have(forall(t, in(t, unaryIntersection(pair(x, y))) <=> (exists(b, in(b, pair(x, y))) /\ ∀(b, in(b, pair(x, y)) ==> in(t, b))))) by InstantiateForall(unaryIntersection(pair(x, y)))( - unaryIntersection.definition of (x -> pair(x, y)) - ) - val defexp = thenHave(in(z, unaryIntersection(pair(x, y))) <=> (exists(b, in(b, pair(x, y))) /\ ∀(b, in(b, pair(x, y)) ==> in(z, b)))) by InstantiateForall(z) - - val lhs = have(in(z, unaryIntersection(pair(x, y))) |- (z === x)) subproof { - have(in(z, unaryIntersection(pair(x, y))) |- forall(b, in(b, pair(x, y)) ==> in(z, b))) by Weakening(defexp) - thenHave(in(z, unaryIntersection(pair(x, y))) |- in(unorderedPair(x, x), pair(x, y)) ==> in(z, unorderedPair(x, x))) by InstantiateForall(unorderedPair(x, x)) - have(thesis) by Tautology.from(lastStep, secondElemInPair of (x -> unorderedPair(x, y), y -> unorderedPair(x, x)), singletonHasNoExtraElements of (y -> z)) - } - - val rhs = have((z === x) |- in(z, unaryIntersection(pair(x, y)))) subproof { - val xinxy = have(in(x, unaryIntersection(pair(x, y)))) subproof { - have(in(unorderedPair(x, x), pair(x, y))) by Restate.from(secondElemInPair of (x -> unorderedPair(x, y), y -> unorderedPair(x, x))) - val exClause = thenHave(exists(b, in(b, pair(x, y)))) by RightExists - - have(in(b, pair(x, y)) |- in(b, pair(x, y))) by Hypothesis - val bp = thenHave(in(b, pair(x, y)) |- (b === singleton(x)) \/ (b === unorderedPair(x, y))) by Substitution.ApplyRules(pairAxiom of (z -> b, x -> unorderedPair(x, y), y -> singleton(x))) - - have(in(x, singleton(x))) by Restate.from(singletonHasNoExtraElements of (y -> x)) - val bxx = thenHave((b === singleton(x)) |- in(x, b)) by Substitution.ApplyRules((b === singleton(x))) - - have(in(x, unorderedPair(x, y))) by Restate.from(firstElemInPair) - val bxy = thenHave((b === unorderedPair(x, y)) |- in(x, b)) by Substitution.ApplyRules((b === unorderedPair(x, y))) - - have(in(b, pair(x, y)) ==> in(x, b)) by Tautology.from(bp, bxx, bxy) - val frClause = thenHave(forall(b, in(b, pair(x, y)) ==> in(x, b))) by RightForall - - have(thesis) by Tautology.from(defexp of (z -> x), exClause, frClause) - } - thenHave(thesis) by Substitution.ApplyRules((z === x)) - } - - have(thesis) by Tautology.from(lhs, rhs) - } - - /** - * Theorem --- The unary intersection and union of an ordered pair are equal - * iff the two elements are equal. - * - * `∪ (x, y) = {x} = {x, y} = ∩ (x, y) <=> x = y` - * - * See [[pairUnaryIntersection]] and [[unionOfOrderedPair]]. - */ - val pairUnionIntersectionEqual = Theorem( - () |- (union(pair(x, y)) === unaryIntersection(pair(x, y))) <=> (x === y) - ) { - have(in(z, unorderedPair(x, y)) <=> ((z === x) \/ (z === y))) by Restate.from(pairAxiom) - val unionPair = thenHave(in(z, union(pair(x, y))) <=> ((z === x) \/ (z === y))) by Substitution.ApplyRules(unionOfOrderedPair) - - val fwd = have((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- (x === y)) subproof { - have((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- forall(z, in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y))))) by Weakening( - extensionalityAxiom of (x -> union(pair(x, y)), y -> unaryIntersection(pair(x, y))) - ) - thenHave((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y)))) by InstantiateForall(z) - - have((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- (((z === x) \/ (z === y)) <=> (z === x))) by Tautology.from(lastStep, unionPair, pairUnaryIntersection) - thenHave((union(pair(x, y)) === unaryIntersection(pair(x, y))) |- (((y === x) \/ (y === y)) <=> (y === x))) by InstFunSchema(ScalaMap(z -> y)) - thenHave(thesis) by Restate - } - - val bwd = have((x === y) |- (union(pair(x, y)) === unaryIntersection(pair(x, y)))) subproof { - have((x === y) |- in(z, union(pair(x, y))) <=> ((z === x) \/ (z === x))) by Substitution.ApplyRules(x === y)(unionPair) - have((x === y) |- in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y)))) by Tautology.from(lastStep, pairUnaryIntersection) - thenHave((x === y) |- forall(z, in(z, union(pair(x, y))) <=> in(z, unaryIntersection(pair(x, y))))) by RightForall - - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> union(pair(x, y)), y -> unaryIntersection(pair(x, y)))) - } - - have(thesis) by Tautology.from(fwd, bwd) - } - - /** - * Theorem --- The [[firstInPair]] operation when applied to an ordered pair - * produces the first element of the pair. - * - * `first((x, y)) = x` - */ - val firstInPairReduction = Theorem( - () |- (firstInPair(pair(x, y)) === x) - ) { - // z \in \cap (x, y) <=> z = x - val elemInter = have(in(z, unaryIntersection(pair(x, y))) <=> (z === x)) by Restate.from(pairUnaryIntersection) - - // z in \cup \cap p <=> z \in x - val elemUnion = have(in(z, union(unaryIntersection(pair(x, y)))) <=> in(z, x)) subproof { - val unionax = - have(in(z, union(unaryIntersection(pair(x, y)))) <=> exists(t, in(t, unaryIntersection(pair(x, y))) /\ in(z, t))) by Restate.from(unionAxiom of (x -> unaryIntersection(pair(x, y)))) - - val lhs = have(exists(t, in(t, unaryIntersection(pair(x, y))) /\ in(z, t)) |- in(z, x)) subproof { - have(in(z, t) |- in(z, t)) by Hypothesis - thenHave((in(z, t), (t === x)) |- in(z, x)) by Substitution.ApplyRules(t === x) - have(in(t, unaryIntersection(pair(x, y))) /\ in(z, t) |- in(z, x)) by Tautology.from(lastStep, elemInter of (z -> t)) - thenHave(thesis) by LeftExists - } - - val rhs = have(in(z, x) |- exists(t, in(t, unaryIntersection(pair(x, y))) /\ in(z, t))) subproof { - have(in(x, unaryIntersection(pair(x, y)))) by Restate.from(elemInter of (z -> x)) - thenHave(in(z, x) |- in(x, unaryIntersection(pair(x, y))) /\ in(z, x)) by Tautology - thenHave(thesis) by RightExists - } - - have(thesis) by Tautology.from(lhs, rhs, unionax) - } - - thenHave(forall(z, in(z, union(unaryIntersection(pair(x, y)))) <=> in(z, x))) by RightForall - - // \cup \cap (x, y) = x - val unioneq = have(union(unaryIntersection(pair(x, y))) === x) by Tautology.from(lastStep, extensionalityAxiom of (x -> union(unaryIntersection(pair(x, y))), y -> x)) - have((firstInPair(pair(x, y)) === union(unaryIntersection(pair(x, y))))) by InstantiateForall(firstInPair(pair(x, y)))(firstInPair.definition of (p -> pair(x, y))) - have(thesis) by Substitution.ApplyRules(lastStep)(unioneq) - } - - /** - * Theorem --- The [[secondInPairSingletone]] operation when applied to an - * ordered pair produces the singleton containing the second element of the pair. - * - * `secondSingleton((x, y)) = {y}` - * - * Used for [[secondInPair]] reduction. - */ - val secondInPairSingletonReduction = Theorem( - () |- in(z, secondInPairSingleton(pair(x, y))) <=> (z === y) - ) { - have( - forall( - t, - in(t, secondInPairSingleton(pair(x, y))) <=> (in(t, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(t, unaryIntersection(pair(x, y)))))) - ) - ) by InstantiateForall(secondInPairSingleton(pair(x, y)))(secondInPairSingleton.definition of p -> pair(x, y)) - val sipsDef = thenHave( - in(z, secondInPairSingleton(pair(x, y))) <=> (in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) - ) by InstantiateForall(z) - - val predElem = have((in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) <=> (z === y)) subproof { - - // breakdown for each of the clauses in the statement - have(forall(z, in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y)))) by Tautology.from(unionOfOrderedPair, extensionalityAxiom of (x -> union(pair(x, y)), y -> unorderedPair(x, y))) - thenHave(in(z, union(pair(x, y))) <=> in(z, unorderedPair(x, y))) by InstantiateForall(z) - val zUnion = have(in(z, union(pair(x, y))) <=> ((z === x) \/ (z === y))) by Tautology.from(lastStep, pairAxiom) - val unEqInt = have((union(pair(x, y)) === unaryIntersection(pair(x, y))) <=> (x === y)) by Restate.from(pairUnionIntersectionEqual) - val zInter = have(in(z, unaryIntersection(pair(x, y))) <=> (z === x)) by Restate.from(pairUnaryIntersection) - - have( - (in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) <=> (in(z, union(pair(x, y))) /\ ((!(union( - pair(x, y) - ) === unaryIntersection(pair(x, y)))) ==> (!in(z, unaryIntersection(pair(x, y)))))) - ) by Restate - val propDest = thenHave( - (in(z, union(pair(x, y))) /\ ((!(union(pair(x, y)) === unaryIntersection(pair(x, y)))) ==> (!in( - z, - unaryIntersection(pair(x, y)) - )))) <=> (((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x)))) - ) by Substitution.ApplyRules(zUnion, zInter, unEqInt) - - have((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x)))) <=> (z === y)) subproof { - val hypo = have((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x)))) |- (((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))))) by Hypothesis - thenHave((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), (x === y)) |- (((z === y) \/ (z === y)) /\ ((!(y === y)) ==> (!(z === x))))) by Substitution.ApplyRules(x === y) - val xeqy = thenHave((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), (x === y)) |- (z === y)) by Tautology - - have((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), !(x === y)) |- (((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))))) by Weakening(hypo) - val xneqy = thenHave((((z === x) \/ (z === y)) /\ ((!(x === y)) ==> (!(z === x))), !(x === y)) |- (z === y)) by Tautology - - have(thesis) by Tautology.from(xeqy, xneqy, equalityTransitivity of (z -> y, y -> z)) - } - - have(thesis) by Tautology.from(lastStep, propDest) - } - - have(thesis) by Tautology.from(sipsDef, predElem) - } - - /** - * Theorem --- The [[secondInPair]] operation when applied to an ordered pair - * produces the second element of the pair. - * - * `second((x, y)) = y` - */ - val secondInPairReduction = Theorem( - () |- secondInPair(pair(x, y)) === y - ) { - have(secondInPair(pair(x, y)) === union(secondInPairSingleton(pair(x, y)))) by InstantiateForall(secondInPair(pair(x, y)))(secondInPair.definition of p -> pair(x, y)) - have(forall(z, in(z, secondInPair(pair(x, y))) <=> in(z, union(secondInPairSingleton(pair(x, y)))))) by Tautology.from( - lastStep, - extensionalityAxiom of (x -> secondInPair(pair(x, y)), y -> union(secondInPairSingleton(pair(x, y)))) - ) - thenHave(in(z, secondInPair(pair(x, y))) <=> in(z, union(secondInPairSingleton(pair(x, y))))) by InstantiateForall(z) - val secondElem = - have(in(z, secondInPair(pair(x, y))) <=> (exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b)))) by Tautology.from(lastStep, unionAxiom of (x -> secondInPairSingleton(pair(x, y)))) - - val elemIsY = have((exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b))) <=> in(z, y)) subproof { - val lhs = have((exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b))) |- in(z, y)) subproof { - have(in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b) |- in(z, b)) by Restate - thenHave((in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b), (b === y)) |- in(z, y)) by Substitution.ApplyRules(b === y) - have((in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b)) |- in(z, y)) by Tautology.from(lastStep, secondInPairSingletonReduction of z -> b) - - thenHave(thesis) by LeftExists - } - - val rhs = have(in(z, y) |- exists(b, in(b, secondInPairSingleton(pair(x, y))) /\ in(z, b))) subproof { - have(in(z, y) |- in(z, y)) by Hypothesis - have(in(z, y) |- in(y, secondInPairSingleton(pair(x, y))) /\ in(z, y)) by Tautology.from(lastStep, secondInPairSingletonReduction of z -> y) - thenHave(thesis) by RightExists - } - - have(thesis) by Tautology.from(lhs, rhs) - } - - have(in(z, secondInPair(pair(x, y))) <=> in(z, y)) by Tautology.from(secondElem, elemIsY) - thenHave(forall(z, in(z, secondInPair(pair(x, y))) <=> in(z, y))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of x -> secondInPair(pair(x, y))) - } - - /** - * Theorem --- Pair Reconstruction - * - * If `x` is a pair (i.e. `= (c, d)` for some `c` and `d`), then pair element - * projection on it is invertible, so `x = (fst x, snd x)`. - */ - val pairReconstruction = Lemma( - exists(c, exists(d, pair(c, d) === x)) |- x === pair(firstInPair(x), secondInPair(x)) - ) { - sorry - } - - /** - * Cartesian Products and Relations - */ - - val cartesianProductUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) - ) { - have(∃!(z, ∀(t, in(t, z) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))))))) by UniqueComprehension( - powerSet(powerSet(setUnion(x, y))), - lambda(t, ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) - ) - } - - /** - * Cartesian Product --- Given two sets `x` and `y`, their cartesian product - * is the set containing pairs with the first element in `x` and the second - * in `y`. The cartesian product can be seen as a comprehension on the set - * `PP(x ∪ y)`. - * - * `x * y = {z ∈ PP(x ∪ y) | ∃ a ∈ x, b ∈ y. z = (a, b)}` - * - * The proofs are guaranteed and generated by [[UniqueComprehension]]. - * - * @param x set - * @param y set - */ - val cartesianProduct = - DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))))))(cartesianProductUniqueness) - - /** - * Theorem --- cartesian Product ([[cartesianProd]]) of any set with the - * [[emptySet]] is empty. - */ - val productWithEmptySetEmpty = Theorem( - () |- (cartesianProduct(x, emptySet) === emptySet) /\ (cartesianProduct(emptySet, x) === emptySet) - ) { - val xFirst = have(() |- (cartesianProduct(x, emptySet) === emptySet)) subproof { - have( - forall(t, in(t, cartesianProduct(x, emptySet)) <=> (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))))) - ) by InstantiateForall(cartesianProduct(x, emptySet))(cartesianProduct.definition of (y -> emptySet)) - val impl = thenHave( - in(t, cartesianProduct(x, emptySet)) <=> (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet)))) - ) by InstantiateForall(t) - - val elemEmpty = have(in(t, emptySet) <=> (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))))) subproof { - val lhs = have(in(t, emptySet) |- (in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))))) by Weakening( - emptySet.definition of (x -> t) - ) - - have((t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet) |- in(t, emptySet)) by Weakening(emptySet.definition of (x -> b)) - thenHave(exists(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet)) |- in(t, emptySet)) by LeftExists - thenHave(exists(a, exists(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))) |- in(t, emptySet)) by LeftExists - val rhs = thenHave(in(t, powerSet(powerSet(setUnion(x, emptySet)))) /\ exists(a, exists(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, emptySet))) |- in(t, emptySet)) by Weakening - - have(thesis) by Tautology.from(lhs, rhs) - } - - have(in(t, cartesianProduct(x, emptySet)) <=> in(t, emptySet)) by Tautology.from(impl, elemEmpty) - val ext = thenHave(forall(t, in(t, cartesianProduct(x, emptySet)) <=> in(t, emptySet))) by RightForall - - have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> cartesianProduct(x, emptySet), y -> emptySet)) - } - - val xSecond = have(() |- (cartesianProduct(emptySet, x) === emptySet)) subproof { - have( - forall(t, in(t, cartesianProduct(emptySet, y)) <=> (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))))) - ) by InstantiateForall(cartesianProduct(emptySet, y))(cartesianProduct.definition of (x -> emptySet)) - val impl = thenHave( - in(t, cartesianProduct(emptySet, y)) <=> (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y)))) - ) by InstantiateForall(t) - - val elemEmpty = have(in(t, emptySet) <=> (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))))) subproof { - val lhs = have(in(t, emptySet) |- (in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))))) by Weakening( - emptySet.definition of (x -> t) - ) - - have((t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y) |- in(t, emptySet)) by Weakening(emptySet.definition of (x -> a)) - thenHave(exists(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y)) |- in(t, emptySet)) by LeftExists - thenHave(exists(a, exists(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))) |- in(t, emptySet)) by LeftExists - val rhs = thenHave(in(t, powerSet(powerSet(setUnion(emptySet, y)))) /\ exists(a, exists(b, (t === pair(a, b)) /\ in(a, emptySet) /\ in(b, y))) |- in(t, emptySet)) by Weakening - - have(thesis) by Tautology.from(lhs, rhs) - } - - have(in(t, cartesianProduct(emptySet, y)) <=> in(t, emptySet)) by Tautology.from(impl, elemEmpty) - val ext = thenHave(forall(t, in(t, cartesianProduct(emptySet, y)) <=> in(t, emptySet))) by RightForall - - have(thesis) by Tautology.from(ext of (y -> x), extensionalityAxiom of (x -> cartesianProduct(emptySet, x), y -> emptySet)) - } - - have(thesis) by RightAnd(xFirst, xSecond) - } - - /** - * Theorem --- a pair is in the product `x * y` iff its elements are in `x` and - * `y` respectively. - * - * `(a, b) ∈ x * y <=> a ∈ x ∧ b ∈ y` - */ - val pairInCartesianProduct = Theorem( - in(pair(a, b), cartesianProduct(x, y)) <=> (in(a, x) /\ in(b, y)) - ) { - have( - (cartesianProduct(x, y) === cartesianProduct(x, y)) <=> ∀( - t, - in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y)))) - ) - ) by InstantiateForall(cartesianProduct(x, y))(cartesianProduct.definition) - thenHave(∀(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y)))))) by Restate - val cartProdDef = thenHave( - in(pair(a, b), cartesianProduct(x, y)) <=> (in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)))) - ) by InstantiateForall(pair(a, b)) - - // forward - // (a, b) \in x * y ⟹ a ∈ x ∧ b ∈ y - val fwd = have(in(pair(a, b), cartesianProduct(x, y)) ==> (in(a, x) /\ in(b, y))) subproof { - have((a === c, b === d, in(c, x) /\ in(d, y)) |- in(c, x) /\ in(d, y)) by Hypothesis - thenHave((a === c, b === d, in(c, x) /\ in(d, y)) |- in(a, x) /\ in(b, y)) by RightSubstEq.withParametersSimple(List((a, c), (b, d)), lambda(Seq(a, b), in(a, x) /\ in(b, y))) - thenHave(Set((a === c) /\ (b === d), in(c, x) /\ in(d, y)) |- in(a, x) /\ in(b, y)) by Restate - andThen(Substitution.applySubst(pairExtensionality)) - thenHave((pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y) |- in(a, x) /\ in(b, y)) by Restate - thenHave(∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)) |- in(a, x) /\ in(b, y)) by LeftExists - thenHave(∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(a, x) /\ in(b, y)) by LeftExists - val cdExists = thenHave((in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(a, x) /\ in(b, y))) by Weakening - have(thesis) by Tautology.from(cdExists, cartProdDef) - } - // backward - // a \in x /\ b \in y ==> (a, b) \in x * y - val bwd = have(in(a, x) /\ in(b, y) ==> in(pair(a, b), cartesianProduct(x, y))) subproof { - val membership = have(in(a, x) /\ in(b, y) |- in(pair(a, b), powerSet(powerSet(setUnion(x, y))))) subproof { - val powerSubsetDef = have(in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) <=> ∀(z, in(z, pair(a, b)) ==> in(z, powerSet(setUnion(x, y))))) by Tautology.from( - powerAxiom of (x -> pair(a, b), y -> powerSet(setUnion(x, y))), - subsetAxiom of (x -> pair(a, b), y -> powerSet(setUnion(x, y))) - ) - - val unionToPower = have((in(a, setUnion(x, y)) /\ in(b, setUnion(x, y)), in(z, pair(a, b))) |- in(z, powerSet(setUnion(x, y)))) subproof { - val zabHypo = have(in(z, pair(a, b)) |- in(z, pair(a, b))) by Hypothesis - val cutLhs = have(in(z, pair(a, b)) |- (z === unorderedPair(a, b)) \/ (z === singleton(a))) by Tautology.from(zabHypo, pairAxiom of (x -> unorderedPair(a, b), y -> singleton(a))) - - // need to show that {a, b} and {a} = {a, a} are in P(x \cup y) - val prem = (in(a, setUnion(x, y)) /\ in(b, setUnion(x, y))) - - have(prem |- in(unorderedPair(a, b), powerSet(setUnion(x, y)))) by Weakening(unorderedPairInPowerSet of (x -> setUnion(x, y))) - val zab = - thenHave((prem, (z === unorderedPair(a, b))) |- in(z, powerSet(setUnion(x, y)))) by RightSubstEq.withParametersSimple( - List((z, unorderedPair(a, b))), - lambda(a, in(a, powerSet(setUnion(x, y)))) - ) - have(prem |- in(unorderedPair(a, a), powerSet(setUnion(x, y)))) by Weakening(unorderedPairInPowerSet of (x -> setUnion(x, y), b -> a)) - val zaa = - thenHave((prem, (z === unorderedPair(a, a))) |- in(z, powerSet(setUnion(x, y)))) by RightSubstEq.withParametersSimple( - List((z, unorderedPair(a, a))), - lambda(a, in(a, powerSet(setUnion(x, y)))) - ) - - val cutRhs = have((prem, (z === unorderedPair(a, b)) \/ (z === singleton(a))) |- in(z, powerSet(setUnion(x, y)))) by LeftOr(zab, zaa) - - have(thesis) by Cut(cutLhs, cutRhs) - } - - val abToUnion = have(in(a, x) /\ in(b, y) |- in(a, setUnion(x, y)) /\ in(b, setUnion(x, y))) subproof { - have(in(a, x) |- in(a, setUnion(x, y)) <=> (in(a, x) \/ in(a, y))) by Weakening(setUnionMembership of (z -> a)) - val aUn = thenHave(in(a, x) |- in(a, setUnion(x, y))) by Tautology - have(in(b, y) |- in(b, setUnion(x, y)) <=> (in(b, x) \/ in(b, y))) by Weakening(setUnionMembership of (z -> b)) - val bUn = thenHave(in(b, y) |- in(b, setUnion(x, y))) by Tautology - - have((in(a, x), in(b, y)) |- in(a, setUnion(x, y)) /\ in(b, setUnion(x, y))) by RightAnd(aUn, bUn) - thenHave(thesis) by Restate - } - - have((in(a, x) /\ in(b, y), in(z, pair(a, b))) |- in(z, powerSet(setUnion(x, y)))) by Cut(abToUnion, unionToPower) - thenHave((in(a, x) /\ in(b, y)) |- in(z, pair(a, b)) ==> in(z, powerSet(setUnion(x, y)))) by Restate - val abToPower = thenHave((in(a, x) /\ in(b, y)) |- ∀(z, in(z, pair(a, b)) ==> in(z, powerSet(setUnion(x, y))))) by RightForall - - have(thesis) by Tautology.from(abToPower, powerSubsetDef) - } - - val filtering = have(in(a, x) /\ in(b, y) |- ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)))) subproof { - have(in(a, x) /\ in(b, y) |- (pair(a, b) === pair(a, b)) /\ in(a, x) /\ in(b, y)) by Restate - thenHave(in(a, x) /\ in(b, y) |- ∃(d, (pair(a, d) === pair(a, b)) /\ in(a, x) /\ in(d, y))) by RightExists - thenHave(in(a, x) /\ in(b, y) |- ∃(c, ∃(d, (pair(c, d) === pair(a, b)) /\ in(c, x) /\ in(d, y)))) by RightExists - } - - val compCriterion = - have(in(a, x) /\ in(b, y) |- in(pair(a, b), powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (pair(a, b) === pair(c, d)) /\ in(c, x) /\ in(d, y)))) by RightAnd(membership, filtering) - - have(thesis) by Tautology.from(compCriterion, cartProdDef) - } - - have(thesis) by RightIff(fwd, bwd) - } - - /** - * Theorem --- If `t` is a pair containing elements from `x` and `y`, then - * it is in `PP(x ∪ y)`. - * - * `∃ c, d. t = (c, d) ∧ c ∈ x ∧ d ∈ y ⊢ t ∈ PP(x ∪ y)` - * - * Asserts that the first part of the [[cartesianProduct]] definition is redundant. - */ - val elemOfPowerPowerUnion = Theorem( - ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(t, powerSet(powerSet(setUnion(x, y)))) - ) { - val upCD = have((in(c, x), in(d, y)) |- in(unorderedPair(c, d), powerSet(setUnion(x, y)))) subproof { - - have((in(c, x), in(d, y)) |- subset(unorderedPair(c, d), setUnion(x, y))) subproof { - val zcd = have(in(z, unorderedPair(c, d)) <=> ((z === c) \/ (z === d))) by Restate.from(pairAxiom of (x -> c, y -> d)) - val zunion = have(in(z, setUnion(x, y)) <=> (in(z, x) \/ in(z, y))) by Restate.from(setUnionMembership) - - val zc = have((z === c) |- in(z, setUnion(x, y)) <=> (in(c, x) \/ in(c, y))) by Substitution.ApplyRules(z === c)(zunion) - val zd = have((z === d) |- in(z, setUnion(x, y)) <=> (in(d, x) \/ in(d, y))) by Substitution.ApplyRules(z === d)(zunion) - - have((in(c, x), in(d, y)) |- in(z, unorderedPair(c, d)) ==> in(z, setUnion(x, y))) by Tautology.from(zcd, zc, zd) - thenHave((in(c, x), in(d, y)) |- forall(z, in(z, unorderedPair(c, d)) ==> in(z, setUnion(x, y)))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> unorderedPair(c, d), y -> setUnion(x, y))) - } - - have(thesis) by Tautology.from(lastStep, powerAxiom of (y -> setUnion(x, y), x -> unorderedPair(c, d))) - } - - val upCC = have((in(c, x)) |- in(unorderedPair(c, c), powerSet(setUnion(x, y)))) subproof { - - have((in(c, x)) |- subset(unorderedPair(c, c), setUnion(x, y))) subproof { - val zcd = have(in(z, unorderedPair(c, c)) <=> (z === c)) by Restate.from(pairAxiom of (x -> c, y -> c)) - val zunion = have(in(z, setUnion(x, y)) <=> (in(z, x) \/ in(z, y))) by Restate.from(setUnionMembership) - - val zc = have((z === c) |- in(z, setUnion(x, y)) <=> (in(c, x) \/ in(c, y))) by Substitution.ApplyRules(z === c)(zunion) - - have(in(c, x) |- in(z, unorderedPair(c, c)) ==> in(z, setUnion(x, y))) by Tautology.from(zcd, zc) - thenHave(in(c, x) |- forall(z, in(z, unorderedPair(c, c)) ==> in(z, setUnion(x, y)))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> unorderedPair(c, c), y -> setUnion(x, y))) - } - - have(thesis) by Tautology.from(lastStep, powerAxiom of (y -> setUnion(x, y), x -> unorderedPair(c, c))) - - } - - have((in(c, x), in(d, y)) |- in(pair(c, d), powerSet(powerSet(setUnion(x, y))))) subproof { - - have((in(c, x), in(d, y)) |- subset(pair(c, d), powerSet(setUnion(x, y)))) subproof { - val zp = have(in(z, pair(c, d)) <=> ((z === unorderedPair(c, d)) \/ (z === unorderedPair(c, c)))) by Restate.from(pairAxiom of (x -> unorderedPair(c, d), y -> unorderedPair(c, c))) - - val zcc = have((z === unorderedPair(c, c), in(c, x)) |- in(z, powerSet(setUnion(x, y)))) by Substitution.ApplyRules(z === unorderedPair(c, c))(upCC) - val zcd = have((z === unorderedPair(c, d), in(c, x), in(d, y)) |- in(z, powerSet(setUnion(x, y)))) by Substitution.ApplyRules(z === unorderedPair(c, d))(upCD) - - have((in(c, x), in(d, y)) |- in(z, pair(c, d)) ==> in(z, powerSet(setUnion(x, y)))) by Tautology.from(zp, zcc, zcd) - thenHave((in(c, x), in(d, y)) |- forall(z, in(z, pair(c, d)) ==> in(z, powerSet(setUnion(x, y))))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> pair(c, d), y -> powerSet(setUnion(x, y)))) - } - - have(thesis) by Tautology.from(lastStep, powerAxiom of (y -> powerSet(setUnion(x, y)), x -> pair(c, d))) - - } - - thenHave((t === pair(c, d), in(c, x), in(d, y)) |- in(t, powerSet(powerSet(setUnion(x, y))))) by Substitution.ApplyRules(t === pair(c, d)) - thenHave(((t === pair(c, d)) /\ in(c, x) /\ in(d, y)) |- in(t, powerSet(powerSet(setUnion(x, y))))) by Restate - thenHave(exists(d, ((t === pair(c, d)) /\ in(c, x) /\ in(d, y))) |- in(t, powerSet(powerSet(setUnion(x, y))))) by LeftExists - thenHave(thesis) by LeftExists - } - - /** - * Theorem --- the binary set union operation is commutative. - * - * `a ∪ b = b ∪ a` - */ - val unionCommutativity = Theorem( - setUnion(a, b) === setUnion(b, a) - ) { - have(in(z, setUnion(a, b)) <=> in(z, setUnion(b, a))) by Tautology.from(setUnionMembership of (x -> a, y -> b), setUnionMembership of (x -> b, y -> a)) - thenHave(forall(z, in(z, setUnion(a, b)) <=> in(z, setUnion(b, a)))) by RightForall - - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> setUnion(a, b), y -> setUnion(b, a))) - } - - /** - * Theorem --- the first element of a union is a subset of it. - * - * `a ⊆ a ∪ b` - */ - val unionSubsetFirst = Theorem( - subset(a, setUnion(a, b)) - ) { - have(in(z, a) ==> in(z, setUnion(a, b))) by Weakening(setUnionMembership of (x -> a, y -> b)) - thenHave(forall(z, in(z, a) ==> in(z, setUnion(a, b)))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> a, y -> setUnion(a, b))) - } - - /** - * Theorem --- the second element of a union is a subset of it. - * - * `a ⊆ a ∪ b` - */ - val unionSubsetSecond = Theorem( - subset(b, setUnion(a, b)) - ) { - have(thesis) by Substitution.ApplyRules(unionCommutativity)(unionSubsetFirst of (a -> b, b -> a)) - } - - /** - * Theorem --- the union of two subsets of a set is still a subset of it. - * - * `a ⊆ c ∧ b ⊆ c ⊢ a ∪ b ⊆ c` - */ - val unionOfTwoSubsets = Theorem( - subset(a, c) /\ subset(b, c) |- subset(setUnion(a, b), c) - ) { - val unionDef = have(in(z, setUnion(a, b)) <=> (in(z, a) \/ in(z, b))) by Restate.from(setUnionMembership of (x -> a, y -> b)) - - have(subset(a, c) |- forall(z, in(z, a) ==> in(z, c))) by Weakening(subsetAxiom of (x -> a, y -> c)) - val ac = thenHave(subset(a, c) |- in(z, a) ==> in(z, c)) by InstantiateForall(z) - val bc = ac of a -> b - - have(subset(a, c) /\ subset(b, c) |- in(z, setUnion(a, b)) ==> in(z, c)) by Tautology.from(unionDef, ac, bc) - thenHave(subset(a, c) /\ subset(b, c) |- forall(z, in(z, setUnion(a, b)) ==> in(z, c))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> setUnion(a, b), y -> c)) - } - - /** - * Theorem --- the union of subsets of two sets is a subset of their union. - * - * `a ⊆ c ∧ b ⊆ d ⊢ a ∪ b ⊆ c ∪ d` - */ - val unionOfSubsetsOfDifferentSets = Theorem( - subset(a, c) /\ subset(b, d) |- subset(setUnion(a, b), setUnion(c, d)) - ) { - val unionDefab = have(in(z, setUnion(a, b)) <=> (in(z, a) \/ in(z, b))) by Restate.from(setUnionMembership of (x -> a, y -> b)) - val unionDefcd = unionDefab of (a -> c, b -> d) - - have(subset(a, c) |- forall(z, in(z, a) ==> in(z, c))) by Weakening(subsetAxiom of (x -> a, y -> c)) - val ac = thenHave(subset(a, c) |- in(z, a) ==> in(z, c)) by InstantiateForall(z) - val bc = ac of (a -> b, c -> d) - - have(subset(a, c) /\ subset(b, d) |- in(z, setUnion(a, b)) ==> (in(z, c) \/ in(z, d))) by Tautology.from(unionDefab, ac, bc) - thenHave(subset(a, c) /\ subset(b, d) |- in(z, setUnion(a, b)) ==> in(z, setUnion(c, d))) by Substitution.ApplyRules(unionDefcd) - thenHave(subset(a, c) /\ subset(b, d) |- forall(z, in(z, setUnion(a, b)) ==> in(z, setUnion(c, d)))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> setUnion(a, b), y -> setUnion(c, d))) - } - - /** - * Theorem --- the subset predicate is transitive. - * - * `a ⊆ b ∧ b ⊆ c ⊢ a ⊆ c` - */ - val subsetTransitivity = Theorem( - subset(a, b) /\ subset(b, c) |- subset(a, c) - ) { - have(subset(a, b) |- forall(z, in(z, a) ==> in(z, b))) by Weakening(subsetAxiom of (x -> a, y -> b)) - val sab = thenHave(subset(a, b) |- in(z, a) ==> in(z, b)) by InstantiateForall(z) - val sbc = sab of (a -> b, b -> c) - - have(subset(a, b) /\ subset(b, c) |- in(z, a) ==> in(z, c)) by Tautology.from(sab, sbc) - thenHave(subset(a, b) /\ subset(b, c) |- forall(z, in(z, a) ==> in(z, c))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> a, y -> c)) - } - - /** - * Theorem --- a set is an element of a Cartesian product iff it is a pair containing - * elements from the constituents of the product. - * - * `t ∈ x * y <=> ∃ a, b. t = (a, b) ∧ a ∈ x ∧ b ∈ y` - * - * Asserts a stronger definition of the [[cartesianProduct]]. See - * [[elemOfPowerPowerUnion]] for the redundancy proof. - */ - val elemOfCartesianProduct = Theorem( - in(t, cartesianProduct(x, y)) <=> ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))) - ) { - have(forall(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by InstantiateForall( - cartesianProduct(x, y) - )(cartesianProduct.definition) - val defUnfold = thenHave(in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))))) by InstantiateForall(t) - - have(∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y))) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) /\ ∃(c, ∃(d, (t === pair(c, d)) /\ in(c, x) /\ in(d, y))))) by Tautology.from( - elemOfPowerPowerUnion - ) - - have(thesis) by Tautology.from(lastStep, defUnfold) - } - - /** - * Theorem --- the union of two Cartesian products is a subset of the product of unions. - * - * `a * b ∪ c * d ⊆ (a ∪ c) * (b ∪ d)` - */ - val unionOfCartesianProducts = Theorem( - subset(setUnion(cartesianProduct(a, b), cartesianProduct(c, d)), cartesianProduct(setUnion(a, c), setUnion(b, d))) - ) { - val axb = cartesianProduct(a, b) - val cxd = cartesianProduct(c, d) - - val unionDef = have(in(z, setUnion(axb, cxd)) |- in(z, axb) \/ in(z, cxd)) by Weakening(setUnionMembership of (x -> axb, y -> cxd)) - - /* - z in a x b - <=> - exist x, y. z = (x, y); x in a; y in b - ==> x in a U c, y in b U d - ==> z in (a U c) x (b U d) - */ - val zab = have(in(z, axb) |- in(z, cartesianProduct(setUnion(a, c), setUnion(b, d)))) subproof { - have(forall(z, in(z, a) ==> in(z, setUnion(a, c)))) by Tautology.from(unionSubsetFirst of (b -> c), subsetAxiom of (x -> a, y -> setUnion(a, c))) - val xa = thenHave((in(x, a) ==> in(x, setUnion(a, c)))) by InstantiateForall(x) - - have(forall(z, in(z, b) ==> in(z, setUnion(b, d)))) by Tautology.from(unionSubsetFirst of (a -> b, b -> d), subsetAxiom of (x -> b, y -> setUnion(b, d))) - val yb = thenHave((in(y, b) ==> in(y, setUnion(b, d)))) by InstantiateForall(y) - - have(in(x, a) /\ in(y, b) |- in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))) by Tautology.from(xa, yb) - thenHave((z === pair(x, y)) /\ in(x, a) /\ in(y, b) |- (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))) by Tautology - thenHave((z === pair(x, y)) /\ in(x, a) /\ in(y, b) |- exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d)))) by RightExists - thenHave((z === pair(x, y)) /\ in(x, a) /\ in(y, b) |- exists(x, exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))))) by RightExists - thenHave(exists(y, (z === pair(x, y)) /\ in(x, a) /\ in(y, b)) |- exists(x, exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))))) by LeftExists - thenHave(exists(x, exists(y, (z === pair(x, y)) /\ in(x, a) /\ in(y, b))) |- exists(x, exists(y, (z === pair(x, y)) /\ in(x, setUnion(a, c)) /\ in(y, setUnion(b, d))))) by LeftExists - - have(thesis) by Tautology.from(lastStep, elemOfCartesianProduct of (x -> a, y -> b, t -> z), elemOfCartesianProduct of (x -> setUnion(a, c), y -> setUnion(b, d), t -> z)) - } - - val zcd = - have(in(z, cxd) |- in(z, cartesianProduct(setUnion(a, c), setUnion(b, d)))) by Substitution.ApplyRules(unionCommutativity)( - lastStep of (a -> c, b -> d, c -> a, d -> b) - ) - - have(in(z, setUnion(axb, cxd)) ==> in(z, cartesianProduct(setUnion(a, c), setUnion(b, d)))) by Tautology.from(unionDef, zab, zcd) - thenHave(forall(z, in(z, setUnion(axb, cxd)) ==> in(z, cartesianProduct(setUnion(a, c), setUnion(b, d))))) by RightForall - - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> setUnion(axb, cxd), y -> cartesianProduct(setUnion(a, c), setUnion(b, d)))) - } - - /** - * Theorem --- if a pair is in a set `r`, then elements of the pair are in `∪ ∪ r`. - * - * `(a, b) ∈ r ⊢ a, b ∈ ∪ ∪ r` - * - * Used to prove stronger definitions for [[relationDomain]] and [[relationRange]] - */ - val pairInSetImpliesPairInUnion = Theorem( - in(pair(a, b), r) |- in(a, union(union(r))) /\ in(b, union(union(r))) - ) { - // a, b in {a, b} and union union r - // {a, b} in union r - // pair a b in r - val unionUP = have(in(pair(a, b), r) |- in(unorderedPair(a, b), union(r))) subproof { - val hypo = have(in(pair(a, b), r) |- in(pair(a, b), r)) by Hypothesis - have(in(pair(a, b), r) |- in(unorderedPair(a, b), pair(a, b)) /\ in(pair(a, b), r)) by RightAnd(hypo, firstElemInPair of (x -> unorderedPair(a, b), y -> singleton(a))) - thenHave(in(pair(a, b), r) |- ∃(y, in(unorderedPair(a, b), y) /\ in(y, r))) by RightExists - andThen(Substitution.applySubst(unionAxiom of (z -> unorderedPair(a, b), x -> r))) - } - val unionA = have(in(unorderedPair(a, b), union(r)) |- in(a, union(union(r)))) subproof { - val hypo = have(in(unorderedPair(a, b), union(r)) |- in(unorderedPair(a, b), union(r))) by Hypothesis - have(in(unorderedPair(a, b), union(r)) |- in(a, unorderedPair(a, b)) /\ in(unorderedPair(a, b), union(r))) by RightAnd(hypo, firstElemInPair of (x -> a, y -> b)) - thenHave(in(unorderedPair(a, b), union(r)) |- ∃(y, in(a, y) /\ in(y, union(r)))) by RightExists - andThen(Substitution.applySubst(unionAxiom of (z -> a, x -> union(r)))) - } - val unionB = have(in(unorderedPair(a, b), union(r)) |- in(b, union(union(r)))) subproof { - val hypo = have(in(unorderedPair(a, b), union(r)) |- in(unorderedPair(a, b), union(r))) by Hypothesis - have(in(unorderedPair(a, b), union(r)) |- in(b, unorderedPair(a, b)) /\ in(unorderedPair(a, b), union(r))) by RightAnd(hypo, secondElemInPair of (x -> a, y -> b)) - thenHave(in(unorderedPair(a, b), union(r)) |- ∃(y, in(b, y) /\ in(y, union(r)))) by RightExists - andThen(Substitution.applySubst(unionAxiom of (z -> b, x -> union(r)))) - } - - have(thesis) by Tautology.from(unionUP, unionA, unionB) - } - - /** - * Binary Relation --- A binary relation `r` from `a` to `b` is a subset of - * the [[cartesianProduct]] of `a` and `b`, `a * b`. We say `x r y`, `r(x, - * y)`, or `r relates x to y` for `(x, y) ∈ r`. - */ - val relationBetween = DEF(r, a, b) --> subset(r, cartesianProduct(a, b)) - - /** - * `r` is a relation *from* `a` if there exists a set `b` such that `r` is a - * relation from `a` to `b`. - */ - val relationFrom = DEF(r, a) --> ∃(b, relationBetween(r, a, b)) - - /** - * `r` is a relation if there exist sets `a` and `b` such that `r` is a - * relation from `a` to `b`. - */ - val relation = DEF(r) --> ∃(a, ∃(b, relationBetween(r, a, b))) - - val relationDomainUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> ∃(a, in(pair(t, a), r)))) - ) { - val uniq = have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))))) by UniqueComprehension( - union(union(r)), - lambda(t, ∃(a, in(pair(t, a), r))) - ) - - // eliminating t \in UU r - // since it is implied by the second condition - val transform = have(∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) subproof { - val hypo = have(in(pair(t, a), r) |- in(pair(t, a), r)) by Hypothesis - have(in(pair(t, a), r) |- in(t, union(union(r))) /\ in(a, union(union(r)))) by Cut(hypo, pairInSetImpliesPairInUnion of (a -> t, b -> a)) - thenHave(in(pair(t, a), r) |- in(t, union(union(r)))) by Weakening - thenHave(∃(a, in(pair(t, a), r)) |- in(t, union(union(r)))) by LeftExists - val lhs = thenHave(∃(a, in(pair(t, a), r)) ==> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) by Tautology - val rhs = have((∃(a, in(pair(t, a), r)) /\ in(t, union(union(r)))) ==> ∃(a, in(pair(t, a), r))) by Restate - - val subst = have(∃(a, in(pair(t, a), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) by RightIff(lhs, rhs) - - have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) by Hypothesis - val cutRhs = thenHave( - (in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))), ∃(a, in(pair(t, a), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (∃( - a, - in(pair(t, a), r) - )) - ) by RightSubstIff.withParametersSimple(List((∃(a, in(pair(t, a), r)), in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))), lambda(h, in(t, z) <=> h)) - have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (∃(a, in(pair(t, a), r)))) by Cut(subst, cutRhs) - thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- in(t, z) <=> (∃(a, in(pair(t, a), r)))) by LeftForall - thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r))))) by RightForall - thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r)))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by RightExists - thenHave(thesis) by LeftExists - } - - // converting the exists to existsOne - val cutL = have( - ∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) - ) by Restate.from(existsOneImpliesExists of (P -> lambda(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))))) - val cutR = have(∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by Restate.from( - uniqueByExtension of (schemPred -> lambda(t, (∃(a, in(pair(t, a), r))))) - ) - - val trL = - have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by Cut(cutL, transform) - val trR = - have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(t, a), r)))))) by Cut(trL, cutR) - - have(thesis) by Cut.withParameters(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(t, a), r))))))(uniq, trR) - } - - /** - * (Binary) Relation Domain --- The set containing the first elements of every - * pair in a relation `r`. Alternatively, the set of elements which are - * related to another element by `r`. - * - * `dom(r) = {z ∈ ∪ ∪ r | ∃ t. (z, t) ∈ r}` - * - * The proofs are guaranteed and generated by [[UniqueComprehension]]. - * - * The first part is proved redundant by [[pairInSetImpliesPairInUnion]]. - * We have, - * - * `dom(r) = {z | ∃ t. (z, t) ∈ r}` - * - * @param r relation (set) - */ - val relationDomain = DEF(r) --> The(z, ∀(t, in(t, z) <=> ∃(a, in(pair(t, a), r))))(relationDomainUniqueness) - - val relationRangeUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> ∃(a, in(pair(a, t), r)))) - ) { - val uniq = have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))))) by UniqueComprehension( - union(union(r)), - lambda(t, ∃(a, in(pair(a, t), r))) - ) - - // eliminating t \in UU r - // since it is implied by the second condition - val transform = have(∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) subproof { - val hypo = have(in(pair(a, t), r) |- in(pair(a, t), r)) by Hypothesis - have(in(pair(a, t), r) |- in(t, union(union(r))) /\ in(a, union(union(r)))) by Cut(hypo, pairInSetImpliesPairInUnion of (a -> a, b -> t)) - thenHave(in(pair(a, t), r) |- in(t, union(union(r)))) by Weakening - thenHave(∃(a, in(pair(a, t), r)) |- in(t, union(union(r)))) by LeftExists - val lhs = thenHave(∃(a, in(pair(a, t), r)) ==> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) by Tautology - val rhs = have((∃(a, in(pair(a, t), r)) /\ in(t, union(union(r)))) ==> ∃(a, in(pair(a, t), r))) by Restate - - val subst = have(∃(a, in(pair(a, t), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) by RightIff(lhs, rhs) - - have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) by Hypothesis - val cutRhs = thenHave( - (in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))), ∃(a, in(pair(a, t), r)) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (∃( - a, - in(pair(a, t), r) - )) - ) by RightSubstIff.withParametersSimple(List((∃(a, in(pair(a, t), r)), in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))), lambda(h, in(t, z) <=> h)) - have((in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (∃(a, in(pair(a, t), r)))) by Cut(subst, cutRhs) - thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- in(t, z) <=> (∃(a, in(pair(a, t), r)))) by LeftForall - thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r))))) by RightForall - thenHave(∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r)))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by RightExists - thenHave(thesis) by LeftExists - } - - // converting the exists to existsOne - val cutL = have( - ∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) - ) by Restate.from(existsOneImpliesExists of (P -> lambda(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))))) - val cutR = have(∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by Restate.from( - uniqueByExtension of (schemPred -> lambda(t, (∃(a, in(pair(a, t), r))))) - ) - - val trL = - have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by Cut(cutL, transform) - val trR = - have(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))) |- ∃!(z, ∀(t, in(t, z) <=> (∃(a, in(pair(a, t), r)))))) by Cut(trL, cutR) - - have(thesis) by Cut.withParameters(∃!(z, ∀(t, in(t, z) <=> (in(t, union(union(r))) /\ ∃(a, in(pair(a, t), r))))))(uniq, trR) - } - - /** - * Theorem --- the relation domain of the empty set is the empty set. - */ - val domainOfEmptySetIsEmpty = Theorem( - relationDomain(∅) === ∅ - ) { - have(∀(t, in(t, relationDomain(∅)) <=> ∃(a, in(pair(t, a), ∅)))) by InstantiateForall(relationDomain(∅))( - relationDomain.definition of (r := ∅) - ) - val domainDef = thenHave(in(t, relationDomain(∅)) <=> ∃(a, in(pair(t, a), ∅))) by InstantiateForall(t) // todo: check if can be simplified with shortDefinition - val contraPosDomainDef = have(!in(t, relationDomain(∅)) <=> ∀(a, !in(pair(t, a), ∅))) by Tautology.from(domainDef) - have(!in(pair(t, a), ∅)) by Tautology.from(emptySetAxiom of (x := pair(t, a))) - val nothingInEmpty = thenHave(∀(a, !in(pair(t, a), ∅))) by RightForall - - have(!in(t, relationDomain(∅))) by Tautology.from(nothingInEmpty, contraPosDomainDef) - thenHave(∀(t, !in(t, relationDomain(∅)))) by RightForall - - have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (x := relationDomain(∅))) - } - - /** - * (Binary) Relation Range --- The set containing the second elements of every - * pair in a relation `r`. Alternatively, the set of elements which another - * element relates to by `r`. - * - * `range(r) = {z ∈ ∪ ∪ r | ∃ t. (t, z) ∈ r} - * - * The proofs are guaranteed and generated by [[UniqueComprehension]]. - * - * The first part is proved redundant by [[pairInSetImpliesPairInUnion]]. - * We have, - * - * `range(r) = {z | ∃ t. (t, z) ∈ r}` - * - * @param r relation (set) - */ - val relationRange = DEF(r) --> The(z, ∀(t, in(t, z) <=> ∃(a, in(pair(a, t), r))))(relationRangeUniqueness) - - /** - * Theorem --- If `r` is a relation, then `r` is a relation between its domain and its range. - */ - val relationImpliesRelationBetweenDomainAndRange = Theorem( - relation(r) |- relationBetween(r, relationDomain(r), relationRange(r)) - ) { - // Lay out the definitions to apply them later - have(∀(t, in(t, relationDomain(r)) <=> ∃(b, in(pair(t, b), r)))) by Definition(relationDomain, relationDomainUniqueness)(r) - val relationDomainDef = thenHave(in(t, relationDomain(r)) <=> ∃(b, in(pair(t, b), r))) by InstantiateForall(t) - - have(∀(t, in(t, relationRange(r)) <=> ∃(a, in(pair(a, t), r)))) by Definition(relationRange, relationRangeUniqueness)(r) - val relationRangeDef = thenHave(in(t, relationRange(r)) <=> ∃(a, in(pair(a, t), r))) by InstantiateForall(t) - - // Start the proof - have(relation(r) |- ∃(x, ∃(y, relationBetween(r, x, y)))) by Tautology.from(relation.definition) - - have(relationBetween(r, x, y) |- subset(r, cartesianProduct(x, y))) by Tautology.from(relationBetween.definition of (a -> x, b -> y)) - have(relationBetween(r, x, y) |- ∀(t, in(t, r) ==> in(t, cartesianProduct(x, y)))) by Tautology.from( - lastStep, - subset.definition of (x -> r, y -> cartesianProduct(x, y)) - ) - thenHave(relationBetween(r, x, y) |- in(t, r) ==> in(t, cartesianProduct(x, y))) by InstantiateForall(t) - thenHave((relationBetween(r, x, y), in(t, r)) |- in(t, cartesianProduct(x, y))) by Restate - - // Apply the definition of the cartesian product - val relationDef = have((relationBetween(r, x, y), in(t, r)) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) by Tautology.from( - lastStep, - elemOfCartesianProduct - ) - - // Show that x ⊇ relationDomain(r) and y ⊇ relationRange(r) - val memberships = have((in(t, r), (t === pair(a, b))) |- in(a, relationDomain(r)) /\ in(b, relationRange(r))) subproof { - have(in(t, r) |- in(t, r)) by Hypothesis - val membership = thenHave((in(t, r), (t === pair(a, b))) |- in(pair(a, b), r)) by Substitution.ApplyRules(t === pair(a, b)) - - assume(in(t, r)) - assume(t === pair(a, b)) - have(∃(b, in(pair(a, b), r))) by RightExists(membership) - val left = have(in(a, relationDomain(r))) by Tautology.from(lastStep, relationDomainDef of (t -> a)) - - have(∃(a, in(pair(a, b), r))) by RightExists(membership) - val right = have(in(b, relationRange(r))) by Tautology.from(lastStep, relationRangeDef of (t -> b)) - - have(thesis) by RightAnd(left, right) - } - - // We can now reconstruct the definition of relationBetween(r, relationDomain(r), relationRange(r)) - have((t === pair(a, b)) |- (t === pair(a, b))) by Hypothesis - val toCut = have((in(t, r), (t === pair(a, b))) |- (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))) by RightAnd(lastStep, memberships) - - have((t === pair(a, b)) /\ in(a, x) /\ in(b, y) |- (t === pair(a, b))) by Tautology - have((in(t, r), (t === pair(a, b)) /\ in(a, x) /\ in(b, y)) |- (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))) by Cut(lastStep, toCut) - - // Re-add the existential quantifiers - thenHave((in(t, r), (t === pair(a, b)) /\ in(a, x) /\ in(b, y)) |- ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r)))) by RightExists - thenHave((in(t, r), (t === pair(a, b)) /\ in(a, x) /\ in(b, y)) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by RightExists - thenHave((in(t, r), ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y))) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by LeftExists - thenHave((in(t, r), ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by LeftExists - - // Cut and rewrap the definition - have((in(t, r), relationBetween(r, x, y)) |- ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, relationDomain(r)) /\ in(b, relationRange(r))))) by Cut( - relationDef, - lastStep - ) - have((in(t, r), relationBetween(r, x, y)) |- in(t, cartesianProduct(relationDomain(r), relationRange(r)))) by Tautology.from( - lastStep, - elemOfCartesianProduct of (x -> relationDomain(r), y -> relationRange(r)) - ) - thenHave(relationBetween(r, x, y) |- in(t, r) ==> in(t, cartesianProduct(relationDomain(r), relationRange(r)))) by Restate - thenHave(relationBetween(r, x, y) |- ∀(t, in(t, r) ==> in(t, cartesianProduct(relationDomain(r), relationRange(r))))) by RightForall - have(relationBetween(r, x, y) |- subset(r, cartesianProduct(relationDomain(r), relationRange(r)))) by Tautology.from( - lastStep, - subset.definition of (x -> r, y -> cartesianProduct(relationDomain(r), relationRange(r))) - ) - have(relationBetween(r, x, y) |- relationBetween(r, relationDomain(r), relationRange(r))) by Tautology.from( - lastStep, - relationBetween.definition of (a -> relationDomain(r), b -> relationRange(r)) - ) - - // Add the existential quantifier to finish the proofs - thenHave(∃(y, relationBetween(r, x, y)) |- relationBetween(r, relationDomain(r), relationRange(r))) by LeftExists - thenHave(∃(x, ∃(y, relationBetween(r, x, y))) |- relationBetween(r, relationDomain(r), relationRange(r))) by LeftExists - - have(thesis) by Tautology.from(lastStep, relation.definition) - } - - /** - * Theorem --- The Cartesian Product of two sets is a relation. - */ - val cartesianProductIsRelation = Theorem( - relation(cartesianProduct(x, y)) - ) { - // trivially follows from the fact that a relation is a subset of the cartesian product - have(relationBetween(cartesianProduct(x, y), x, y)) by Tautology.from( - subsetReflexivity of (x := cartesianProduct(x, y)), - relationBetween.definition of (r := cartesianProduct(x, y), a := x, b := y) - ) - thenHave(∃(b, relationBetween(cartesianProduct(x, y), x, b))) by RightExists - val relationSpec = thenHave(∃(a, ∃(b, relationBetween(cartesianProduct(x, y), a, b)))) by RightExists - - have(thesis) by Tautology.from(relationSpec, relation.definition of (r := cartesianProduct(x, y))) - } - - /** - * (Binary) Relation Field --- The union of the domain and range of a - * relation, or the set of all elements related by `r`. - * - * @param r relation (set) - */ - val relationField = DEF(r) --> (setUnion(relationDomain(r), relationRange(r))) - - /** - * Theorem --- the union of two relations is a relation, with domains and codomains - * unions of the constituents. - * - * Effectively, - * - * `f ⊆ a * b; g ⊆ c * d ⊢ (f ∪ g) ⊆ (a ∪ c) * (b ∪ d)` - */ - val unionOfTwoRelationsWithField = Theorem( - relationBetween(f, a, b) /\ relationBetween(g, c, d) |- relationBetween(setUnion(f, g), setUnion(a, c), setUnion(b, d)) - ) { - val fab = have(relationBetween(f, a, b) <=> subset(f, cartesianProduct(a, b))) by Restate.from(relationBetween.definition of r -> f) - val gcd = fab of (f -> g, a -> c, b -> d) - val fug = fab of (f -> setUnion(f, g), a -> setUnion(a, c), b -> setUnion(b, d)) - - have(subset(f, cartesianProduct(a, b)) /\ subset(g, cartesianProduct(c, d)) |- subset(setUnion(f, g), cartesianProduct(setUnion(a, c), setUnion(b, d)))) by Tautology.from( - unionOfCartesianProducts, - unionOfSubsetsOfDifferentSets of (a -> f, b -> g, c -> cartesianProduct(a, b), d -> cartesianProduct(c, d)), - subsetTransitivity of (a -> setUnion(f, g), b -> setUnion(cartesianProduct(a, b), cartesianProduct(c, d)), c -> cartesianProduct(setUnion(a, c), setUnion(b, d))) - ) - - have(thesis) by Tautology.from(lastStep, fab, gcd, fug) - } - - /** - * Theorem --- the union of two relations is a relation. (weaker form) - * - * Weakening of [[unionOfTwoRelationsWithField]] to unknown fields. - */ - val unionOfTwoRelations = Theorem( - relation(f) /\ relation(g) |- relation(setUnion(f, g)) - ) { - val relf = have(relation(f) <=> exists(x, exists(y, relationBetween(f, x, y)))) by Restate.from(relation.definition of r -> f) - val relg = relf of f -> g - val relfug = relf of f -> setUnion(f, g) - - have((relationBetween(f, a, b), relationBetween(g, c, d)) |- relationBetween(setUnion(f, g), setUnion(a, c), setUnion(b, d))) by Restate.from(unionOfTwoRelationsWithField) - thenHave((relationBetween(f, a, b), relationBetween(g, c, d)) |- exists(y, relationBetween(setUnion(f, g), setUnion(a, c), y))) by RightExists - thenHave((relationBetween(f, a, b), relationBetween(g, c, d)) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by RightExists - thenHave((relationBetween(f, a, b), exists(d, relationBetween(g, c, d))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists - thenHave((relationBetween(f, a, b), exists(c, exists(d, relationBetween(g, c, d)))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists - thenHave((exists(b, relationBetween(f, a, b)), exists(c, exists(d, relationBetween(g, c, d)))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists - thenHave((exists(a, exists(b, relationBetween(f, a, b))), exists(c, exists(d, relationBetween(g, c, d)))) |- exists(x, exists(y, relationBetween(setUnion(f, g), x, y)))) by LeftExists - - thenHave((relation(f), relation(g)) |- relation(setUnion(f, g))) by Substitution.ApplyRules(relf, relg, relfug) - } - - /** - * Theorem --- Pair in Relation - * - * If a pair `(x, y)` exists in a relation `r` from `a` to `b`, - * then `x` and `y` are elements of `a` and `b` respectively. - */ - val pairInRelation = Lemma( - relationBetween(r, a, b) /\ in(pair(x, y), r) |- in(x, a) /\ in(y, b) - ) { - assume(relationBetween(r, a, b)) - assume(in(pair(x, y), r)) - have(forall(t, in(t, r) ==> in(t, cartesianProduct(a, b)))) by Tautology.from(relationBetween.definition, subsetAxiom of (x -> r, y -> cartesianProduct(a, b))) - thenHave(in(pair(x, y), r) ==> in(pair(x, y), cartesianProduct(a, b))) by InstantiateForall(pair(x, y)) - have(thesis) by Tautology.from(lastStep, pairInCartesianProduct of (x -> a, y -> b, a -> x, b -> y)) - } - - // TODO: any subset of a functional is functional - // TODO: a functional over something restricted to x is still functional - - /** - * Properties of relations - */ - - /** - * Reflexive Relation --- `∀ x. x R x` - */ - val reflexive = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, in(y, x) ==> in(pair(y, y), r)) - - /** - * Symmetric Relation --- `∀ x y. x R y ⇔ y R x` - */ - val symmetric = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, in(pair(y, z), r) <=> in(pair(z, y), r))) - - /** - * Transitive Relation --- `∀ x y z. x R y ∧ y R z ⇒ x R z` - */ - val transitive = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(w, ∀(y, ∀(z, (in(pair(w, y), r) /\ in(pair(y, z), r)) ==> in(pair(w, z), r)))) - - /** - * Equivalence Relation --- A relation is an equivalence relation if it is - * [[reflexive]], [[symmetric]], and [[transitive]]. - */ - val equivalence = DEF(r, x) --> reflexive(r, x) /\ symmetric(r, x) /\ transitive(r, x) - - /** - * Anti-reflexive Relation --- `∀ x. ! x R x` - */ - val antiReflexive = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, in(y, x) ==> !in(pair(y, y), r)) - - /** - * Irreflexive Relation --- Alias for [[antiReflexive]]. - */ - val irreflexive = antiReflexive - - /** - * Anti-symmetric Relation --- `∀ x y. x R y ∧ y R x ⇒ y = x` - */ - val antiSymmetric = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(pair(y, z), r) /\ in(pair(z, y), r)) ==> (y === z))) - - /** - * Asymmetric Relation --- `∀ x y. x R y ⇔ ! y R x` - */ - val asymmetric = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, in(pair(y, z), r) ==> !in(pair(z, y), r))) - - /** - * Connected Relation --- `∀ x y. (x R y) ∨ (y R x) ∨ (y = x)` - */ - val connected = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(y, x) /\ in(z, x)) ==> (in(pair(y, z), r) \/ in(pair(z, y), r) \/ (y === z)))) - - /** - * Total Relation --- Alias for [[connected]]. - */ - val total = connected - - /** - * Strongly Connected Relation --- - * `∀ x y z. y R x ∧ z R x ⇒ y R z ∨ z R y` - */ - val stronglyConnected = DEF(r, x) --> relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(y, x) /\ in(z, x)) ==> (in(pair(y, z), r) \/ in(pair(z, y), r)))) - - /** - * Theorem --- the empty set is a relation, the empty relation, between any two sets. - */ - val emptySetRelation = Theorem( - () |- relationBetween(emptySet, a, b) - ) { - have(thesis) by Tautology.from(emptySetIsASubset of (x -> cartesianProduct(a, b)), relationBetween.definition of (r -> emptySet)) - } - - /** - * Theorem --- the empty relation is a relation on the empty set. - */ - val emptySetRelationOnItself = Theorem( - () |- relationBetween(emptySet, emptySet, emptySet) - ) { - have(thesis) by Restate.from(emptySetRelation of (a -> emptySet, b -> emptySet)) - } - - /** - * Theorem --- empty relation on the empty set is reflexive. - */ - val emptyRelationReflexiveOnItself = Theorem( - () |- reflexive(emptySet, emptySet) - ) { - have(() |- in(y, emptySet) ==> in(pair(y, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> y)) - val refCond = thenHave(() |- forall(y, in(y, emptySet) ==> in(pair(y, y), emptySet))) by RightForall - - have(thesis) by Tautology.from(reflexive.definition of (r -> emptySet, x -> emptySet), emptySetRelationOnItself, refCond) - } - - /** - * Theorem --- the empty relation is symmetric. - */ - val emptyRelationSymmetric = Theorem( - () |- symmetric(emptySet, a) - ) { - have(() |- in(pair(y, z), emptySet) <=> in(pair(z, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(y, z)), emptySetAxiom of (x -> pair(z, y))) - thenHave(() |- forall(z, in(pair(y, z), emptySet) <=> in(pair(z, y), emptySet))) by RightForall - val symCond = thenHave(() |- forall(y, forall(z, in(pair(y, z), emptySet) <=> in(pair(z, y), emptySet)))) by RightForall - - have(thesis) by Tautology.from(symmetric.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), symCond) - } - - /** - * Theorem --- the empty relation is irreflexive. - */ - val emptyRelationIrreflexive = Theorem( - () |- irreflexive(emptySet, a) - ) { - have(() |- in(y, a) ==> !in(pair(y, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(y, y))) - val irrefCond = thenHave(() |- forall(y, in(y, a) ==> !in(pair(y, y), emptySet))) by RightForall - - have(thesis) by Tautology.from(irreflexive.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), irrefCond) - } - - /** - * Theorem --- the empty relation is transitive. - */ - val emptyRelationTransitive = Theorem( - () |- transitive(emptySet, a) - ) { - have(() |- (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(w, y))) - thenHave(() |- forall(z, (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet))) by RightForall - thenHave(() |- forall(y, forall(z, (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet)))) by RightForall - val trsCond = thenHave(() |- forall(w, forall(y, forall(z, (in(pair(w, y), emptySet) /\ in(pair(y, z), emptySet)) ==> in(pair(w, z), emptySet))))) by RightForall - - have(thesis) by Tautology.from(transitive.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), trsCond) - } - - /** - * Theorem --- the empty relation is an equivalence relation on the empty set. - */ - val emptyRelationEquivalence = Theorem( - () |- equivalence(emptySet, emptySet) - ) { - have(thesis) by Tautology.from( - equivalence.definition of (r -> emptySet, x -> emptySet), - emptyRelationReflexiveOnItself, - emptyRelationSymmetric of (a -> emptySet), - emptyRelationTransitive of (a -> emptySet) - ) - } - - /** - * Theorem --- the empty relation is anti-symmetric. - */ - val emptyRelationAntiSymmetric = Theorem( - () |- antiSymmetric(emptySet, a) - ) { - have(() |- (in(pair(y, z), emptySet) /\ in(pair(z, y), emptySet)) ==> (y === z)) by Tautology.from(emptySetAxiom of (x -> pair(y, z))) - thenHave(() |- forall(z, (in(pair(y, z), emptySet) /\ in(pair(z, y), emptySet)) ==> (y === z))) by RightForall - val ansymCond = thenHave(() |- forall(y, forall(z, (in(pair(y, z), emptySet) /\ in(pair(z, y), emptySet)) ==> (y === z)))) by RightForall - - have(thesis) by Tautology.from(antiSymmetric.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), ansymCond) - } - - /** - * Theorem --- the empty relation is asymmetric. - */ - val emptyRelationAsymmetric = Theorem( - () |- asymmetric(emptySet, a) - ) { - have(() |- in(pair(y, z), emptySet) ==> !in(pair(z, y), emptySet)) by Tautology.from(emptySetAxiom of (x -> pair(y, z))) - thenHave(() |- forall(z, in(pair(y, z), emptySet) ==> !in(pair(z, y), emptySet))) by RightForall - val asymCond = thenHave(() |- forall(y, forall(z, in(pair(y, z), emptySet) ==> !in(pair(z, y), emptySet)))) by RightForall - - have(thesis) by Tautology.from(asymmetric.definition of (r -> emptySet, x -> a), emptySetRelation of (b -> a), asymCond) - } - - /** - * Theorem --- the empty relation is total on the empty set. - */ - val emptyRelationTotalOnItself = Theorem( - () |- total(emptySet, emptySet) - ) { - have((in(y, emptySet) /\ in(z, emptySet)) ==> (in(pair(y, z), emptySet) \/ in(pair(z, y), emptySet) \/ (y === z))) by Tautology.from(emptySetAxiom of x -> y) - thenHave(forall(z, (in(y, emptySet) /\ in(z, emptySet)) ==> (in(pair(y, z), emptySet) \/ in(pair(z, y), emptySet) \/ (y === z)))) by RightForall - thenHave(forall(y, forall(z, (in(y, emptySet) /\ in(z, emptySet)) ==> (in(pair(y, z), emptySet) \/ in(pair(z, y), emptySet) \/ (y === z))))) by RightForall - - have(thesis) by Tautology.from(lastStep, total.definition of (r -> emptySet, x -> emptySet), emptySetRelationOnItself) - } - - // smaller needed lemmas - // f from x to y => range f <= y - // f from x to y => dom f = x - // x <= y, y <= x |- x = y - - /** - * Theorem --- Symmetry of Equality and Subset - * - * [[equality]] implies a [[subset]] ordering, and [[subset]] ordering in both - * directions implies [[equality]]. - */ - val subsetEqualitySymmetry = Theorem( - (x === y) <=> (subset(x, y) /\ subset(y, x)) - ) { - have(subset(x, y) /\ subset(y, x) <=> subset(x, y) /\ subset(y, x)) by Restate - thenHave(subset(x, y) /\ subset(y, x) <=> forall(t, in(t, x) ==> in(t, y)) /\ subset(y, x)) by Substitution.ApplyRules(subsetAxiom) - thenHave(subset(x, y) /\ subset(y, x) <=> forall(t, in(t, x) ==> in(t, y)) /\ forall(t, in(t, y) ==> in(t, x))) by Substitution.ApplyRules(subsetAxiom) - andThen(Substitution.applySubst(universalConjunctionCommutation of (P -> lambda(t, in(t, x) ==> in(t, y)), Q -> lambda(t, in(t, y) ==> in(t, x))))) - andThen(Substitution.applySubst(extensionalityAxiom)) - thenHave(thesis) by Restate - } - - /** - * Theorem --- a set containing only pairs is a relation, and vice versa. - * - * `(\forall t \in z. \exists a, b. t = (a, b)) <=> relation(z)` - * - * The domain and codomain of this relation can be obtained constructively by applying - * the [[replacementSchema]] with the [[firstInPair]] and [[secondInPair]] projection - * functions. - * - * Here, it is sufficient to deal with them abstractly through the definitions of - * [[relationDomain]] and [[relationRange]]. - */ - val setOfPairsIsRelation = Theorem( - forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) <=> relation(z) - ) { - // if the set contains only pairs, it is a relation - val fwd = have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) ==> relation(z)) subproof { - val dom = relationDomain(z) - val ran = relationRange(z) - - val inst = have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- (in(t, z) ==> exists(a, exists(b, (t === pair(a, b)))))) by InstantiateForall - - // unfold defs - have(forall(t, in(t, dom) <=> exists(a, in(pair(t, a), z)))) by InstantiateForall(dom)(relationDomain.definition of r -> z) - val inDom = thenHave((in(a, dom) <=> exists(b, in(pair(a, b), z)))) by InstantiateForall(a) - have(forall(t, in(t, ran) <=> exists(a, in(pair(a, t), z)))) by InstantiateForall(ran)(relationRange.definition of r -> z) - val inRan = thenHave((in(b, ran) <=> exists(a, in(pair(a, b), z)))) by InstantiateForall(b) - - have((in(t, z)) |- in(t, z)) by Restate - val abz = thenHave((in(t, z), (t === pair(a, b))) |- in(pair(a, b), z)) by Substitution.ApplyRules(t === pair(a, b)) - - val exa = have((in(t, z), (t === pair(a, b))) |- exists(a, in(pair(a, b), z))) by RightExists(abz) - val exb = have((in(t, z), (t === pair(a, b))) |- exists(b, in(pair(a, b), z))) by RightExists(abz) - - have((in(t, z), (t === pair(a, b))) |- (t === pair(a, b)) /\ in(a, dom) /\ in(b, ran)) by Tautology.from(exa, exb, inDom, inRan) - thenHave((in(t, z), (t === pair(a, b))) |- exists(b, (t === pair(a, b)) /\ in(a, dom) /\ in(b, ran))) by RightExists - thenHave((in(t, z), (t === pair(a, b))) |- exists(a, exists(b, (t === pair(a, b)) /\ in(a, dom) /\ in(b, ran)))) by RightExists - - have((in(t, z), (t === pair(a, b))) |- in(t, cartesianProduct(dom, ran))) by Tautology.from(lastStep, elemOfCartesianProduct of (x -> dom, y -> ran)) - thenHave((in(t, z), exists(b, t === pair(a, b))) |- in(t, cartesianProduct(dom, ran))) by LeftExists - thenHave((in(t, z), exists(a, exists(b, t === pair(a, b)))) |- in(t, cartesianProduct(dom, ran))) by LeftExists - - have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- in(t, z) ==> in(t, cartesianProduct(dom, ran))) by Tautology.from(lastStep, inst) - thenHave(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- forall(t, in(t, z) ==> in(t, cartesianProduct(dom, ran)))) by RightForall - - have(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- relationBetween(z, dom, ran)) by Tautology.from( - lastStep, - subsetAxiom of (x -> z, y -> cartesianProduct(dom, ran)), - relationBetween.definition of (r -> z, a -> dom, b -> ran) - ) - thenHave(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- exists(b, relationBetween(z, dom, b))) by RightExists - thenHave(forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) |- exists(a, exists(b, relationBetween(z, a, b)))) by RightExists - - have(thesis) by Tautology.from(lastStep, relation.definition of r -> z) - } - - // if the set is a relation, it contains only pairs - val bwd = have(relation(z) ==> forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b)))))) subproof { - have(subset(z, cartesianProduct(c, d)) |- forall(t, in(t, z) ==> in(t, cartesianProduct(c, d)))) by Weakening(subsetAxiom of (x -> z, y -> cartesianProduct(c, d))) - val tz = thenHave(subset(z, cartesianProduct(c, d)) |- (in(t, z) ==> in(t, cartesianProduct(c, d)))) by InstantiateForall(t) - - have(in(t, cartesianProduct(c, d)) |- exists(a, exists(b, (t === pair(a, b))))) subproof { - have(((t === pair(a, b)) /\ in(a, c) /\ in(a, b)) ==> (t === pair(a, b))) by Restate - thenHave(forall(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b)) ==> (t === pair(a, b)))) by RightForall - have(exists(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b))) ==> exists(b, (t === pair(a, b)))) by Cut( - lastStep, - existentialImplicationDistribution of (P -> lambda(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b))), Q -> lambda(b, (t === pair(a, b)))) - ) - thenHave(forall(a, exists(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b))) ==> exists(b, (t === pair(a, b))))) by RightForall - val elemCart = have(exists(a, exists(b, ((t === pair(a, b)) /\ in(a, c) /\ in(a, b)))) ==> exists(a, exists(b, (t === pair(a, b))))) by Cut( - lastStep, - existentialImplicationDistribution of (P -> lambda(a, exists(b, (t === pair(a, b)) /\ in(a, c) /\ in(a, b))), Q -> lambda(a, exists(b, t === pair(a, b)))) - ) - - // TODO: Tautology bug - have(thesis) by Tautology.from(lastStep, elemOfCartesianProduct of (x -> c, y -> d, z -> t)) - } - - have(relationBetween(z, c, d) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by Tautology.from(lastStep, tz, relationBetween.definition of (r -> z, a -> c, b -> d)) - thenHave(exists(d, relationBetween(z, c, d)) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by LeftExists - thenHave(exists(c, exists(d, relationBetween(z, c, d))) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by LeftExists - - have(relation(z) |- in(t, z) ==> exists(a, exists(b, (t === pair(a, b))))) by Tautology.from(lastStep, relation.definition of r -> z) - thenHave(relation(z) |- forall(t, in(t, z) ==> exists(a, exists(b, (t === pair(a, b)))))) by RightForall - thenHave(thesis) by Restate - } - - have(thesis) by Tautology.from(fwd, bwd) - } - - /** - * Theorem --- the union of a set of relations is a relation itself. - * - * `\forall t \in z. relation(t) |- relation(union(z))` - * - * This implication also holds in the other direction, but that is - * not as useful. - */ - val unionOfRelationSet = Theorem( - forall(t, in(t, z) ==> relation(t)) |- relation(union(z)) - ) { - // union of a set of relations contains only pairs - have(forall(t, in(t, z) ==> relation(t)) |- forall(t, in(t, union(z)) ==> exists(a, exists(b, (t === pair(a, b)))))) subproof { - assume(forall(t, in(t, z) ==> relation(t))) - have(in(x, z) ==> relation(x)) by InstantiateForall - have(in(x, z) |- forall(t, in(t, x) ==> exists(a, exists(b, (t === pair(a, b)))))) by Tautology.from(lastStep, setOfPairsIsRelation of z -> x) - thenHave((in(x, z) /\ in(t, x)) |- exists(a, exists(b, (t === pair(a, b))))) by InstantiateForall(t) - thenHave(exists(x, in(x, z) /\ in(t, x)) |- exists(a, exists(b, (t === pair(a, b))))) by LeftExists - - have(in(t, union(z)) ==> exists(a, exists(b, (t === pair(a, b))))) by Tautology.from(lastStep, unionAxiom of (x -> z, z -> t)) - thenHave(thesis) by RightForall - } - - // a set of pairs is a relation - have(thesis) by Tautology.from(lastStep, setOfPairsIsRelation of z -> union(z)) - } - - /** - * Theorem --- Domain of Relational Union - * - * If the unary union of a set is relational, then its domain is defined - * precisely by the union of the domains of its elements. - * - * relation(\cup z) |- \forall t. t \in dom(U z) <=> \exists y \in z. t \in - * dom(y) - * - * This holds, particularly, as the elements of z must be relations - * themselves, which follows from the assumption. - */ - val domainOfRelationalUnion = Theorem( - relation(union(z)) |- forall(t, in(t, relationDomain(union(z))) <=> exists(y, in(y, z) /\ in(t, relationDomain(y)))) - ) { - val uz = union(z) - - have(forall(t, in(t, relationDomain(uz)) <=> exists(a, in(pair(t, a), uz)))) by InstantiateForall(relationDomain(uz))(relationDomain.definition of r -> uz) - val inDom = thenHave(in(t, relationDomain(uz)) <=> exists(a, in(pair(t, a), uz))) by InstantiateForall(t) - - assume(relation(uz)) // proof assumption - - have(exists(a, in(pair(t, a), uz)) <=> exists(y, in(y, z) /\ in(t, relationDomain(y)))) subproof { - // we prove the directions separately - val fwd = have(exists(a, in(pair(t, a), uz)) |- exists(y, in(y, z) /\ in(t, relationDomain(y)))) subproof { - have(in(pair(t, a), uz) |- exists(y, in(y, z) /\ in(t, relationDomain(y)))) subproof { - assume(in(pair(t, a), uz)) - // since \cup z is a union - // \exists y such that (t, a) \in y - // and so t \in dom y - val exy = have(exists(y, in(pair(t, a), y) /\ in(y, z))) by Tautology.from(unionAxiom of (z -> pair(t, a), x -> z)) - - have(exists(y, in(pair(t, a), y) /\ in(y, z)) |- exists(y, in(t, relationDomain(y)) /\ in(y, z))) subproof { - have(forall(z, (z === relationDomain(y)) <=> forall(t, in(t, z) <=> exists(a, in(pair(t, a), y))))) by Weakening(relationDomain.definition of r -> y) - thenHave(forall(t, in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y)))) by InstantiateForall(relationDomain(y)) - val inDomY = thenHave(in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y))) by InstantiateForall(t) - have(in(pair(t, a), y) |- in(pair(t, a), y)) by Hypothesis - thenHave(in(pair(t, a), y) |- exists(a, in(pair(t, a), y))) by RightExists - have(in(pair(t, a), y) /\ in(y, z) |- in(t, relationDomain(y)) /\ in(y, z)) by Tautology.from(lastStep, inDomY) - thenHave(in(pair(t, a), y) /\ in(y, z) |- exists(y, in(t, relationDomain(y)) /\ in(y, z))) by RightExists - thenHave(thesis) by LeftExists - } - - have(thesis) by Cut(exy, lastStep) - } - - thenHave(thesis) by LeftExists - } - val bwd = have(exists(y, in(y, z) /\ in(t, relationDomain(y))) |- exists(a, in(pair(t, a), uz))) subproof { - have(in(y, z) /\ in(t, relationDomain(y)) |- exists(a, in(pair(t, a), uz))) subproof { - assume(in(y, z) /\ in(t, relationDomain(y))) - have(forall(z, (z === relationDomain(y)) <=> forall(t, in(t, z) <=> exists(a, in(pair(t, a), y))))) by Weakening(relationDomain.definition of r -> y) - thenHave(forall(t, in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y)))) by InstantiateForall(relationDomain(y)) - thenHave(in(t, relationDomain(y)) <=> exists(a, in(pair(t, a), y))) by InstantiateForall(t) - val exA = thenHave(exists(a, in(pair(t, a), y))) by Tautology - - have(exists(a, in(pair(t, a), y)) |- exists(a, in(pair(t, a), uz))) subproof { - have(in(pair(t, a), y) |- in(pair(t, a), y) /\ in(y, z)) by Restate - thenHave(in(pair(t, a), y) |- exists(y, in(pair(t, a), y) /\ in(y, z))) by RightExists - have(in(pair(t, a), y) |- in(pair(t, a), uz)) by Tautology.from(lastStep, unionAxiom of (z -> pair(t, a), x -> z)) - thenHave(in(pair(t, a), y) |- exists(a, in(pair(t, a), uz))) by RightExists - thenHave(thesis) by LeftExists - } - - have(exists(a, in(pair(t, a), uz))) by Cut(exA, lastStep) - } - thenHave(thesis) by LeftExists - } - - have(thesis) by Tautology.from(fwd, bwd) - } - - have(in(t, relationDomain(union(z))) <=> exists(y, in(y, z) /\ in(t, relationDomain(y)))) by Tautology.from(inDom, lastStep) - thenHave(thesis) by RightForall - } - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala deleted file mode 100644 index 2957a9ec4..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/SetTheory2.scala +++ /dev/null @@ -1,108 +0,0 @@ -package lisa.maths.settheory - -/** - * Revamp of the set theory library from scratch, since most of the current one is severely outdated. - * We can make a better presentation and organisation of theorems, better automation, uniform comments/latex tags, etc. - */ -object SetTheory2 extends lisa.Main { - import lisa.maths.settheory.SetTheory.* - // import Comprehensions.* - - private val s = variable - private val x = variable - private val x_1 = variable - private val y = variable - private val z = variable - private val f = function[1] - private val t = variable - private val A = variable - private val B = variable - private val C = variable - private val P = predicate[2] - private val Q = predicate[1] - private val Filter = predicate[1] - private val Map = function[1] - - val primReplacement = Theorem( - ∀(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))) |- - ∃(B, forall(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y)))) - ) { - have(thesis) by Restate.from(replacementSchema of (A := A, x := x, P := P)) - } - - val manyForall = Lemma( - ∀(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))).substitute(P := lambda((A, B), P(A, B) /\ ∀(C, P(A, C) ==> (B === C)))) <=> top - ) { - have(thesis) by Tableau - } - - val functionalIsFunctional = Theorem( - ∀(x, in(x, A) ==> ∀(y, ∀(z, (P(x, y) /\ P(x, z)) ==> (y === z)))).substitute(P := lambda((A, B), Filter(A) /\ (B === Map(A)))) <=> top - ) { - - have(y === Map(x) |- (y === Map(x))) by Restate - thenHave((y === Map(x), z === Map(x)) |- y === z) by Substitution.ApplyRules(Map(x) === z) - thenHave(in(x, A) |- ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z))) by Weakening - thenHave(in(x, A) |- ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z)))) by RightForall - thenHave(in(x, A) |- ∀(y, ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z))))) by RightForall - thenHave(in(x, A) ==> ∀(y, ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z))))) by Restate - thenHave(∀(x, in(x, A) ==> ∀(y, ∀(z, ((Filter(x) /\ (y === Map(x)) /\ (z === Map(x))) ==> (y === z)))))) by RightForall - thenHave(thesis) by Restate - - } - - /** - * Theorem --- the refined replacement axiom. Easier to use as a rule than primReplacement. - */ - val replacement = Theorem( - ∃(B, ∀(y, in(y, B) <=> ∃(x, in(x, A) /\ P(x, y) /\ ∀(z, P(x, z) ==> (z === y))))) - ) { - have(thesis) by Tautology.from(manyForall, primReplacement of (P := lambda((A, B), P(A, B) /\ ∀(C, P(A, C) ==> (B === C))))) - } - - val onePointRule = Theorem( - ∃(x, (x === y) /\ Q(x)) <=> Q(y) - ) { - val s1 = have(∃(x, (x === y) /\ Q(x)) ==> Q(y)) subproof { - assume(∃(x, (x === y) /\ Q(x))) - val ex = witness(lastStep) - val s1 = have(Q(ex)) by Tautology.from(ex.definition) - val s2 = have(ex === y) by Tautology.from(ex.definition) - have(Q(y)) by Substitution.ApplyRules(s2)(s1) - } - val s2 = have(Q(y) ==> ∃(x, (x === y) /\ Q(x))) subproof { - assume(Q(y)) - thenHave((y === y) /\ Q(y)) by Restate - thenHave(∃(x, (x === y) /\ Q(x))) by RightExists - thenHave(thesis) by Restate.from - } - have(thesis) by Tautology.from(s1, s2) - } - - /** - * Theorem - `∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) <=> (x === f(∅))` - */ - val singletonMap = Lemma( - ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) <=> (x === f(∅)) - ) { - val s1 = have(∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) ==> (x === f(∅))) subproof { - have(x === f(∅) |- x === f(∅)) by Restate - thenHave((x_1 === ∅, x === f(x_1)) |- x === f(∅)) by Substitution.ApplyRules(x_1 === ∅) - thenHave((x_1 === ∅) /\ (x === f(x_1)) |- x === f(∅)) by Restate - thenHave((in(x_1, singleton(∅))) /\ ((x === f(x_1))) |- x === f(∅)) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) - thenHave(∃(x_1, in(x_1, singleton(∅)) /\ ((x === f(x_1)))) |- x === f(∅)) by LeftExists - - } - - val s2 = have((x === f(∅)) ==> ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) subproof { - have(x === f(∅) |- (∅ === ∅) /\ (x === f(∅))) by Restate - thenHave(x === f(∅) |- in(∅, singleton(∅)) /\ (x === f(∅))) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) - thenHave(x === f(∅) |- ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) by RightExists - thenHave(thesis) by Restate.from - - } - - have(thesis) by Tautology.from(s1, s2) - } - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala deleted file mode 100644 index 23468423d..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/FunctionProperties.scala +++ /dev/null @@ -1,428 +0,0 @@ -package lisa.maths.settheory.functions - -import lisa.automation.kernel.CommonTactics.ExistenceAndUniqueness - -/** - * Classes/properties of functions. - * - * Describes properties of functions such as being injective ([[FunctionPoperties.injective]]), being invertible - * ([[FunctionPoperties.invertibleFunction]]), or being constant ([[FunctionProperties.constantFunction]]). - */ -object FunctionProperties extends lisa.Main { - import lisa.maths.settheory.SetTheory.* - import lisa.maths.Quantifiers.* - import lisa.maths.settheory.functions.Functionals.* - - // var defs - private val x = variable - private val y = variable - private val z = variable - private val t = variable - private val a = variable - private val b = variable - private val p = variable - private val A = variable - private val B = variable - - // relation and function symbols - private val r = variable - private val f = variable - private val g = variable - - private val P = predicate[1] - private val Q = predicate[1] - - /** - * Surjective (function) --- a function `f: x → y` is surjective iff it - * maps to every `b ∈ y` from atleast one `a ∈ x`. - * - * `surjective(f, x, y) = f ∈ x → y ∧ ∀ b ∈ y. (∃ a ∈ x. f(a) = b)` - */ - val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) - - /** - * Alias for [[surjective]] - */ - val onto = surjective - - /** - * Injective (function) --- a function `f: x → y` is injective iff it maps - * to every `b ∈ y` from atmost one `a ∈ x`. - * - * `injective(f, x, y) = f ∈ x → y ∧ ∀ b ∈ y. (∃ a ∈ x. f(a) = b) ⟹ (∃! a ∈ x. f(a) = b)` - */ - val injective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> (∃(a, in(a, x) /\ in(pair(a, b), f)) ==> ∃!(a, in(a, x) /\ in(pair(a, b), f)))) - - /** - * Alias for [[injective]] - */ - val oneone = injective - - /** - * Bijective function --- a function `f: x → y` is bijective iff it is - * [[injective]] and [[surjective]]. - */ - val bijective = DEF(f, x, y) --> injective(f, x, y) /\ surjective(f, x, y) - - /** - * Invertible Function --- a function from `x` to `y` is invertible iff it is - * [[bijective]]. See also, [[inverseFunction]] - */ - val invertibleFunction = DEF(f, x, y) --> bijective(f, x, y) - - /** - * Inverse Function --- the inverse of a function `f: x → y`, denoted - * `f^-1`, is a function from `y` to `x` such that `∀ a ∈ x, b ∈ y. - * f(f^-1(b)) = b ∧ f^-1(f(b)) = b`. - */ - val inverseFunctionOf = DEF(g, f, x, y) --> functionFrom(g, y, x) /\ functionFrom(f, x, y) /\ ∀(a, (in(a, y) ==> (a === app(f, app(g, a)))) /\ (in(a, x) ==> (a === app(g, app(f, a))))) - - // val inverseFunctionExistsIfInvertible = Theorem( - // invertibleFunction(f, x, y) <=> ∃(g, inverseFunctionOf(g, f, x, y)) - // ) { - // ??? - // } - - // val inverseFunctionIsUniqueIfItExists = Theorem( - // ∃(g, inverseFunctionOf(g, f, x, y)) |- ∃!(g, inverseFunctionOf(g, f, x, y)) - // ) { - // ??? - // } - - // val inverseFunctionUniqueness = Theorem( - // ∃!(g, invertibleFunction(f) ==> inverseFunctionOf(g, f, x, y)) - // ) { - // ??? - // } - - // val inverseFunction = DEF (f, x, y) --> The(g, invertibleFunction(f) ==> inverseFunctionOf(g, f, x, y))(inverseFunctionUniqueness) - - /** - * Theorem --- if a function is [[surjective]], its range is equal to its codomain. - */ - val surjectiveImpliesRangeIsCodomain = Theorem( - surjective(f, x, y) |- (y === functionRange(f)) - ) { - have(surjective(f, x, y) |- ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f)))) by Tautology.from(surjective.definition) - val surjDef = thenHave(surjective(f, x, y) |- in(b, y) ==> ∃(a, in(pair(a, b), f))) by InstantiateForall(b) - have(∀(t, in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f))))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) - val rangeDef = thenHave(in(b, functionRange(f)) <=> (∃(a, in(pair(a, b), f)))) by InstantiateForall(b) - - have(surjective(f, x, y) |- in(b, y) ==> in(b, functionRange(f))) by Tautology.from(surjDef, rangeDef) - thenHave(surjective(f, x, y) |- ∀(b, in(b, y) ==> in(b, functionRange(f)))) by RightForall - val surjsub = andThen(Substitution.applySubst(subsetAxiom of (x -> y, y -> functionRange(f)))) - - have((surjective(f, x, y), functionFrom(f, x, y)) |- subset(y, functionRange(f)) /\ subset(functionRange(f), y)) by RightAnd(surjsub, functionImpliesRangeSubsetOfCodomain) - val funceq = andThen(Substitution.applySubst(subsetEqualitySymmetry of (x -> y, y -> functionRange(f)))) - - val surjfunc = have(surjective(f, x, y) |- functionFrom(f, x, y)) by Tautology.from(surjective.definition) - - have(thesis) by Cut(surjfunc, funceq) - } - - /** - * Theorem --- Cantor's Theorem - * - * There is no [[surjective]] mapping ([[functionFrom]]) a set to its [[powerSet]]. - * - * In terms of cardinality, it asserts that a set is strictly smaller than - * its power set. - */ - val cantorTheorem = Theorem( - surjective(f, x, powerSet(x)) |- () - ) { - // define y = {z \in x | ! z \in f(z)} - val ydef = ∀(t, in(t, y) <=> (in(t, x) /\ !in(t, app(f, t)))) - - // y \subseteq x - // y \in P(x) - have(ydef |- ydef) by Hypothesis - thenHave(ydef |- in(t, y) <=> (in(t, x) /\ !in(t, app(f, t)))) by InstantiateForall(t) - thenHave(ydef |- in(t, y) ==> in(t, x)) by Weakening - thenHave(ydef |- ∀(t, in(t, y) ==> in(t, x))) by RightForall - andThen(Substitution.applySubst(subsetAxiom of (x -> y, y -> x))) - andThen(Substitution.applySubst(powerAxiom of (x -> y, y -> x))) - val yInPower = thenHave(ydef |- in(y, powerSet(x))) by Restate - - // y \in range(f) - have(surjective(f, x, powerSet(x)) |- (powerSet(x) === functionRange(f))) by Restate.from(surjectiveImpliesRangeIsCodomain of (y -> powerSet(x))) - andThen(Substitution.applySubst(extensionalityAxiom of (x -> powerSet(x), y -> functionRange(f)))) - val surjRange = thenHave(surjective(f, x, powerSet(x)) |- in(y, powerSet(x)) <=> in(y, functionRange(f))) by InstantiateForall(y) - val yInRange = have((ydef, surjective(f, x, powerSet(x))) |- in(y, functionRange(f))) by Tautology.from(yInPower, surjRange) - - // \exists z. z \in x /\ f(z) = y - val surjToFunFrom = have(surjective(f, x, powerSet(x)) |- functionFrom(f, x, powerSet(x))) by Tautology.from(surjective.definition of (y -> powerSet(x))) - val existsZdom = have((ydef, surjective(f, x, powerSet(x))) |- ∃(z, in(z, functionDomain(f)) /\ (app(f, z) === y))) by Tautology.from( - yInRange, - surjective.definition of (y -> powerSet(x)), - inRangeImpliesPullbackExists of (z -> y), - functionFromImpliesFunctional of (y -> powerSet(x)) - ) - val xeqdom = thenHave((ydef, surjective(f, x, powerSet(x)), (functionDomain(f) === x)) |- ∃(z, in(z, x) /\ (app(f, z) === y))) by RightSubstEq.withParametersSimple( - List((x, functionDomain(f))), - lambda(x, ∃(z, in(z, x) /\ (app(f, z) === y))) - ) - val existsZ = have((ydef, surjective(f, x, powerSet(x))) |- ∃(z, in(z, x) /\ (app(f, z) === y))) by Tautology.from( - surjective.definition of (y -> powerSet(x)), - functionFromImpliesDomainEq of (y -> powerSet(x)), - xeqdom - ) - - // z \in Y <=> z \in x /\ ! z \in f(z) - // y = f(z) so z \in f(z) <=> ! z \in f(z) - have(ydef |- ydef) by Hypothesis - thenHave(ydef |- in(z, y) <=> (in(z, x) /\ !in(z, app(f, z)))) by InstantiateForall(z) - thenHave((ydef, in(z, x), (app(f, z) === y)) |- in(z, y) <=> (in(z, x) /\ !in(z, app(f, z)))) by Weakening - thenHave((ydef, in(z, x), (app(f, z) === y)) |- in(z, app(f, z)) <=> (in(z, x) /\ !in(z, app(f, z)))) by RightSubstEq.withParametersSimple( - List((y, app(f, z))), - lambda(y, in(z, y) <=> (in(z, x) /\ !in(z, app(f, z)))) - ) - thenHave((ydef, in(z, x) /\ (app(f, z) === y)) |- ()) by Tautology - val existsToContra = thenHave((ydef, ∃(z, in(z, x) /\ (app(f, z) === y))) |- ()) by LeftExists - - have((ydef, surjective(f, x, powerSet(x))) |- ()) by Cut(existsZ, existsToContra) - val yToContra = thenHave((∃(y, ydef), surjective(f, x, powerSet(x))) |- ()) by LeftExists - val yexists = have(∃(y, ydef)) by Restate.from(comprehensionSchema of (z -> x, φ -> lambda(t, !in(t, app(f, t))))) - - have(thesis) by Cut(yexists, yToContra) - } - - /** - * Constant function --- for every element in its domain, the value is the same. - */ - val constantFunction = DEF(x, t) --> cartesianProduct(x, singleton(t)) - - /** - * Theorem --- the value of a constant function is the same for all elements in its domain. - * - * `a ∈ x |- app(constantFunction(x, t), a) = t` - */ - val constantFunctionApplication = Theorem( - in(a, x) |- app(constantFunction(x, t), a) === t - ) { - assume(in(a, x)) - have(functionFrom(constantFunction(x, t), x, singleton(t))) by Weakening(constantFunctionFunctionFrom) - - have(in(app(constantFunction(x, t), a), singleton(t))) by Tautology.from( - functionFromApplication of (f := constantFunction(x, t), y := singleton(t)), - lastStep - ) - - have(thesis) by Tautology.from( - singletonHasNoExtraElements of (y := app(constantFunction(x, t), a), x := t), - lastStep - ) - } - - /** - * Theorem --- the domain of a constant function is the set it is defined on. - * - * `dom(constantFunction(x, t)) = x` - */ - val constantFunctionDomain = Theorem( - functionDomain(constantFunction(x, t)) === x - ) { - // since we define constant function using the cartesian product, this requires a bit more effort - val constFunDef = have((constantFunction(x, t) === cartesianProduct(x, singleton(t)))) by Weakening(constantFunction.definition of constantFunction(x, t)) - - have(∀(p, in(p, functionDomain(constantFunction(x, t))) <=> ∃(a, in(pair(p, a), constantFunction(x, t))))) by InstantiateForall(functionDomain(constantFunction(x, t)))( - functionDomain.definition of (r := constantFunction(x, t)) - ) - val domainDef = thenHave(in(p, functionDomain(constantFunction(x, t))) <=> ∃(a, in(pair(p, a), constantFunction(x, t)))) by InstantiateForall(p) - - val rhs = have(∃(a, in(pair(p, a), constantFunction(x, t))) ==> in(p, x)) subproof { - val assumption = assume(∃(a, in(pair(p, a), constantFunction(x, t)))) - val aw = witness(assumption) - have(in(pair(p, aw), constantFunction(x, t))) by Restate - thenHave(in(pair(p, aw), cartesianProduct(x, singleton(t)))) by Substitution.ApplyRules(constFunDef) - - have(thesis) by Tautology.from(lastStep, pairInCartesianProduct of (a := p, b := aw, y := singleton(t))) - } - - val lhs = have(in(p, x) ==> ∃(a, in(pair(p, a), constantFunction(x, t)))) subproof { - assume(in(p, x)) - val tIn = have(in(t, singleton(t))) by Tautology.from(singletonHasNoExtraElements of (y := t, x := t)) - - have(in(pair(p, t), cartesianProduct(x, singleton(t)))) by Tautology.from( - pairInCartesianProduct of (a := p, b := t, y := singleton(t)), - tIn - ) - thenHave(∃(a, in(pair(p, a), cartesianProduct(x, singleton(t))))) by RightExists - thenHave(∃(a, in(pair(p, a), constantFunction(x, t)))) by Substitution.ApplyRules(constFunDef) - } - - have(in(p, x) <=> in(p, functionDomain(constantFunction(x, t)))) by Tautology.from(domainDef, rhs, lhs) - val ext = thenHave(∀(p, in(p, x) <=> in(p, functionDomain(constantFunction(x, t))))) by RightForall - - have(thesis) by Tautology.from(ext, extensionalityAxiom of (y := functionDomain(constantFunction(x, t)))) - } - - /** - * Theorem --- a constant function is functional. - */ - val constantFunctionIsFunctional = Theorem( - functional(constantFunction(x, t)) - ) { - val constFunDef = have((constantFunction(x, t) === cartesianProduct(x, singleton(t)))) by Weakening(constantFunction.definition of constantFunction(x, t)) - - val isRelation = have(relation(constantFunction(x, t))) subproof { - have(relation(cartesianProduct(x, singleton(t)))) by Weakening(cartesianProductIsRelation of (y := singleton(t))) - thenHave(thesis) by Substitution.ApplyRules(constFunDef) - } - - val uniqueY = have(∀(a, ∃(y, in(pair(a, y), constantFunction(x, t))) ==> ∃!(y, in(pair(a, y), constantFunction(x, t))))) subproof { - have(∃(y, in(pair(a, y), constantFunction(x, t))) ==> ∃!(y, in(pair(a, y), constantFunction(x, t)))) subproof { - val existence = assume(∃(y, in(pair(a, y), constantFunction(x, t)))) - - val uniqueness = have((in(pair(a, y), constantFunction(x, t)), in(pair(a, p), constantFunction(x, t))) |- (y === p)) subproof { - val assumption1 = assume(in(pair(a, y), constantFunction(x, t))) - val assumption2 = assume(in(pair(a, p), constantFunction(x, t))) - - have(in(pair(a, y), cartesianProduct(x, singleton(t)))) by Substitution.ApplyRules(constFunDef)(assumption1) - val eq1 = have(y === t) by Tautology.from( - pairInCartesianProduct of (b := y, y := singleton(t)), - lastStep, - singletonHasNoExtraElements of (x := t) - ) - - have(in(pair(a, p), cartesianProduct(x, singleton(t)))) by Substitution.ApplyRules(constFunDef)(assumption2) - val eq2 = have(p === t) by Tautology.from( - pairInCartesianProduct of (b := p, y := singleton(t)), - lastStep, - singletonHasNoExtraElements of (x := t, y := p) - ) - - have(y === p) by Tautology.from(eq1, eq2, equalityTransitivity of (x := y, y := t, z := p)) - } - - have(∃!(y, in(pair(a, y), constantFunction(x, t)))) by ExistenceAndUniqueness(in(pair(a, y), constantFunction(x, t)))(existence, uniqueness) - } - thenHave(thesis) by RightForall - } - - have(thesis) by Tautology.from(isRelation, uniqueY, functional.definition of (f := constantFunction(x, t))) - } - - /** - * Theorem --- a constant function is a function from `x` to the singleton of `t`. - * - * `constantFunction(x, t) ∈ x → {t}` - */ - val constantFunctionFunctionFrom = Theorem( - functionFrom(constantFunction(x, t), x, singleton(t)) - ) { - val constFunDef = have((constantFunction(x, t) === cartesianProduct(x, singleton(t)))) by Weakening(constantFunction.definition of constantFunction(x, t)) - - have(∀(a, in(a, setOfFunctions(x, singleton(t))) <=> (in(a, powerSet(cartesianProduct(x, singleton(t)))) /\ functionalOver(a, x)))) by InstantiateForall(setOfFunctions(x, singleton(t)))( - setOfFunctions.definition of (y := singleton(t)) - ) - val setOfFunctionsDef = thenHave( - in(constantFunction(x, t), setOfFunctions(x, singleton(t))) <=> (in(constantFunction(x, t), powerSet(cartesianProduct(x, singleton(t)))) /\ functionalOver(constantFunction(x, t), x)) - ) by InstantiateForall(constantFunction(x, t)) - - have(in(cartesianProduct(x, singleton(t)), powerSet(cartesianProduct(x, singleton(t))))) by Weakening(elemInItsPowerSet of (x := cartesianProduct(x, singleton(t)))) - val inPowerSet = thenHave(in(constantFunction(x, t), powerSet(cartesianProduct(x, singleton(t))))) by Substitution.ApplyRules(constFunDef) - - val funcOver = have(functionalOver(constantFunction(x, t), x)) by Tautology.from( - constantFunctionIsFunctional, - constantFunctionDomain, - functionalOver.definition of (f := constantFunction(x, t)) - ) - - have(thesis) by Tautology.from( - inPowerSet, - funcOver, - setOfFunctionsDef, - functionFrom.definition of (f := constantFunction(x, t), y := singleton(t)) - ) - } - - /** - * Theorem --- Sigma with a constant function is the cartesian product - * - * `Σ(A, constantFunction(A, t)) = A × t` - */ - val sigmaIsCartesianProductWhenBIsConstant = Theorem( - Sigma(A, constantFunction(A, t)) === cartesianProduct(A, t) - ) { - have(∀(p, in(p, Sigma(A, constantFunction(A, t))) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))))))) by InstantiateForall( - Sigma(A, constantFunction(A, t)) - )( - Sigma.definition of (B -> constantFunction(A, t)) - ) - val sigmaDef = thenHave(in(p, Sigma(A, constantFunction(A, t))) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))))) by InstantiateForall(p) - - have((in(a, A) /\ in(b, app(constantFunction(A, t), a))) <=> (in(a, A) /\ in(b, t))) subproof { - val constApp = have(in(a, A) <=> (in(a, A) /\ (app(constantFunction(A, t), a) === t))) by Tautology.from( - constantFunctionApplication of (x := A) - ) - - val lhs = have(in(a, A) /\ in(b, app(constantFunction(A, t), a)) |- (in(a, A) /\ in(b, t))) subproof { - val inA = assume(in(a, A)) - val subst = have(app(constantFunction(A, t), a) === t) by Tautology.from(constApp, inA) - - assume(in(b, app(constantFunction(A, t), a))) - thenHave(in(a, A) /\ in(b, app(constantFunction(A, t), a))) by Tautology - thenHave(thesis) by Substitution.ApplyRules(subst) - } - - val rhs = have(in(a, A) /\ in(b, t) |- (in(a, A) /\ in(b, app(constantFunction(A, t), a)))) subproof { - val inA = assume(in(a, A)) - val subst = have(app(constantFunction(A, t), a) === t) by Tautology.from(constApp, inA) - - assume(in(b, t)) - thenHave(in(a, A) /\ in(b, t)) by Tautology - thenHave(thesis) by Substitution.ApplyRules(subst) - } - - have(thesis) by Tautology.from(lhs, rhs) - } - have(((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))) <=> ((p === pair(a, b)) /\ in(a, A) /\ in(b, t))) by Tautology.from(lastStep) - thenHave(∀(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))) <=> ((p === pair(a, b)) /\ in(a, A) /\ in(b, t)))) by RightForall - have(∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))) <=> ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, t)))) by Cut( - lastStep, - existentialEquivalenceDistribution of (P := lambda(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))), Q := lambda(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, t))) - ) - thenHave(∀(a, ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))) <=> ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, t))))) by RightForall - val constApp = have(∃(a, ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a))))) <=> ∃(a, ∃(b, ((p === pair(a, b)) /\ in(a, A) /\ in(b, t))))) by Cut( - lastStep, - existentialEquivalenceDistribution of (P := lambda(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(constantFunction(A, t), a)))), Q := lambda( - a, - ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, t)) - )) - ) - val simplSigmaDef = have(in(p, Sigma(A, constantFunction(A, t))) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, t))))) by Substitution.ApplyRules(constApp)(sigmaDef) - - have(in(p, Sigma(A, constantFunction(A, t))) <=> in(p, cartesianProduct(A, t))) by Tautology.from(simplSigmaDef, elemOfCartesianProduct of (x := A, y := t, t := p)) - val ext = thenHave(∀(p, in(p, Sigma(A, constantFunction(A, t))) <=> in(p, cartesianProduct(A, t)))) by RightForall - - have(thesis) by Tautology.from(ext, extensionalityAxiom of (x := Sigma(A, constantFunction(A, t)), y := cartesianProduct(A, t))) - } - - /** - * Theorem --- Pi with a constant function is the set of functions. - * - * `Π(A, constantFunction(A, t)) = A |=> t` - */ - val piIsSetOfFunctionsWhenBIsConstant = Theorem( - Pi(A, constantFunction(A, t)) === (A |=> t) - ) { - have(∀(g, in(g, setOfFunctions(A, t)) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A)))) by InstantiateForall(setOfFunctions(A, t))( - setOfFunctions.definition of (x -> A, y -> t) - ) - val setOfFunctionsDef = thenHave(in(g, setOfFunctions(A, t)) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A))) by InstantiateForall(g) - - have(∀(g, in(g, Pi(A, constantFunction(A, t))) <=> (in(g, powerSet(Sigma(A, constantFunction(A, t)))) /\ functionalOver(g, A)))) by InstantiateForall(Pi(A, constantFunction(A, t)))( - Pi.definition of (x -> A, f -> constantFunction(A, t)) - ) - thenHave(∀(g, in(g, Pi(A, constantFunction(A, t))) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A)))) by Substitution.ApplyRules(sigmaIsCartesianProductWhenBIsConstant) - thenHave(in(g, Pi(A, constantFunction(A, t))) <=> (in(g, powerSet(cartesianProduct(A, t))) /\ functionalOver(g, A))) by InstantiateForall(g) - thenHave(in(g, Pi(A, constantFunction(A, t))) <=> in(g, setOfFunctions(A, t))) by Substitution.ApplyRules(setOfFunctionsDef) - val ext = thenHave(∀(g, in(g, Pi(A, constantFunction(A, t))) <=> in(g, setOfFunctions(A, t)))) by RightForall - - have(thesis) by Tautology.from( - ext, - extensionalityAxiom of (x := Pi(A, constantFunction(A, t)), y := setOfFunctions(A, t)) - ) - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala deleted file mode 100644 index 71efaef16..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/Functionals.scala +++ /dev/null @@ -1,1365 +0,0 @@ -package lisa.maths.settheory.functions -import lisa.automation.kernel.CommonTactics.Definition -import lisa.automation.settheory.SetTheoryTactics._ -import lisa.maths.Quantifiers._ -import lisa.maths.settheory.SetTheory._ - -/** - * Functional sets - * - * Defines functionals, their application, and spaces of functions. - */ -object Functionals extends lisa.Main { - // var defs - private val w = variable - private val x = variable - private val y = variable - private val z = variable - private val p = variable - private val h = formulaVariable - private val t = variable - private val a = variable - private val b = variable - private val c = variable - private val d = variable - private val A = variable - private val B = variable - - // relation and function symbols - private val r = variable - private val f = variable - private val g = variable - - private val P = predicate[1] - private val Q = predicate[1] - - /** - * Functional --- A binary [[relation]] is functional if it maps every element in its domain - * to a unique element (in its range). - * - * `functional(f) ⇔ relation(f) ∧ ∀ x. (∃ y. (x, y) ∈ f) ⟹ (∃! y. (x, y) ∈ f)` - * - * We may alternatively denote `(z, y) ∈ f` as `y = f(z)`. - * - * @param f relation (set) - */ - val functional = DEF(f) --> relation(f) /\ ∀(x, ∃(y, in(pair(x, y), f)) ==> ∃!(y, in(pair(x, y), f))) - - /** - * Alias for [[relationDomain]]. - */ - val functionDomain = relationDomain - - /** - * Alias for [[relationRange]]. - */ - val functionRange = relationRange - - /** - * Functional Over a Set --- A binary [[relation]] is functional over a set `x` if it is - * [[functional]] and has`x` as its domain ([[functionDomain]]). - * - * @param f relation (set) - * @param x set - */ - val functionalOver = DEF(f, x) --> functional(f) /\ (functionDomain(f) === x) - - /** - * Theorem --- The empty set is a functional relation. - * - * `functional(∅)` - */ - val emptySetFunctional = Theorem( - functional(∅) - ) { - val isRelation = have(relation(∅)) subproof { - // ∅ ⊆ A x B, so it must be a (empty) relation - have(relationBetween(∅, a, b)) by Tautology.from(emptySetIsASubset of (x := cartesianProduct(a, b)), relationBetween.definition of (r := ∅)) - thenHave(∃(b, relationBetween(∅, a, b))) by RightExists - val relationSpec = thenHave(∃(a, ∃(b, relationBetween(∅, a, b)))) by RightExists - - have(thesis) by Tautology.from(relationSpec, relation.definition of (r := ∅)) - } - - val uniqueY = have(∀(x, ∃(y, in(pair(x, y), ∅)) ==> ∃!(y, in(pair(x, y), ∅)))) subproof { - // contradiction directly from the emptySetAxiom: there is nothing in the empty set - have(in(pair(x, y), ∅) |- ∃!(y, in(pair(x, y), ∅))) by Tautology.from(emptySetAxiom of (x := pair(x, y))) - thenHave(∃(y, in(pair(x, y), ∅)) |- ∃!(y, in(pair(x, y), ∅))) by LeftExists - thenHave(∃(y, in(pair(x, y), ∅)) ==> ∃!(y, in(pair(x, y), ∅))) by Restate - thenHave(thesis) by RightForall - } - - have(thesis) by Tautology.from(isRelation, uniqueY, functional.definition of (f := ∅)) - } - - /** - * Lemma --- If `f` is a function, then `t ∈ f` implies `t = (x, y)` such that `x ∈ functionDomain(f)`. - */ - val functionalMembership = Lemma( - functional(f) |- ∀(t, in(t, f) ==> ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) - ) { - assume(functional(f)) - - have((functional(f), in(t, f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) subproof { - val isRelation = have(relation(f)) by Tautology.from(functional.definition) - - // Use the definitions - have(relationBetween(f, functionDomain(f), functionRange(f)) |- ∀(x, in(x, f) ==> in(x, cartesianProduct(functionDomain(f), functionRange(f))))) by Tautology.from( - relationBetween.definition of (r -> f, a -> functionDomain(f), b -> functionRange(f)), - subset.definition of (x -> f, y -> cartesianProduct(functionDomain(f), functionRange(f))) - ) - thenHave(relationBetween(f, functionDomain(f), functionRange(f)) |- in(t, f) ==> in(t, cartesianProduct(functionDomain(f), functionRange(f)))) by InstantiateForall(t) - thenHave((relationBetween(f, functionDomain(f), functionRange(f)), in(t, f)) |- in(t, cartesianProduct(functionDomain(f), functionRange(f)))) by Restate - - val almostThere = - have((relationBetween(f, functionDomain(f), functionRange(f)), in(t, f)) |- ∃(x, ∃(y, (t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f))))) by Tautology.from( - lastStep, - elemOfCartesianProduct of (x -> functionDomain(f), y -> functionRange(f)) - ) - - // Remove the extraneous term in the conjunction - have((t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)) |- in(x, functionDomain(f)) /\ (t === pair(x, y))) by Tautology - thenHave((t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)) |- ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y)))) by RightExists - thenHave((t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by RightExists - thenHave(∃(y, (t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f))) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by LeftExists - thenHave(∃(x, ∃(y, (t === pair(x, y)) /\ in(x, functionDomain(f)) /\ in(y, functionRange(f)))) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by LeftExists - - have((relationBetween(f, functionDomain(f), functionRange(f)), in(t, f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Cut(almostThere, lastStep) - have((relation(f), in(t, f)) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Cut(relationImpliesRelationBetweenDomainAndRange of (r -> f), lastStep) - have(in(t, f) |- ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Cut(isRelation, lastStep) - } - thenHave(in(t, f) ==> ∃(x, ∃(y, in(x, functionDomain(f)) /\ (t === pair(x, y))))) by Restate - thenHave(thesis) by RightForall - } - - /** - * Theorem --- a function cannot have two pairs representing different values - * for a given element. - * - * `functional(f) /\ (x, y) \in f /\ (x, z) \in f /\ !(y = z) |- \bot` - */ - val violatingPairInFunction = Theorem( - functional(f) /\ in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z) |- () - ) { - assume(functional(f), in(pair(x, y), f), in(pair(x, z), f), !(y === z)) - - have(forall(x, exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f)))) by Tautology.from(functional.definition) - val exExOne = thenHave(exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f))) by InstantiateForall(x) - - have(in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z)) by Restate - thenHave(exists(z, in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z))) by RightExists - thenHave(exists(y, exists(z, in(pair(x, y), f) /\ in(pair(x, z), f) /\ !(y === z)))) by RightExists - - have(thesis) by Tautology.from(lastStep, exExOne, atleastTwoExist of (P -> lambda(y, in(pair(x, y), f)))) - } - - /** - * Theorem --- a set containing a single pair is a function. - * - * Given `s = {(x, y)}`, - * - * `functional(s) /\ dom(s) = {x} /\ ran(s) = {y}` - */ - val pairSingletonIsFunctional = Theorem( - { - val s = singleton(pair(x, y)) - functional(s) /\ (functionDomain(s) === singleton(x)) /\ (functionRange(s) === singleton(y)) - } - ) { - val s = singleton(pair(x, y)) - - val elemOfS = have(in(z, s) <=> (z === pair(x, y))) by Restate.from(singletonHasNoExtraElements of (y -> z, x -> pair(x, y))) - - // (x, y) in {x} * {y} - val xyInCart = have(in(pair(x, y), cartesianProduct(singleton(x), singleton(y)))) subproof { - have((pair(x, y) === pair(x, y)) /\ in(x, singleton(x)) /\ in(y, singleton(y))) by Tautology.from(singletonHasNoExtraElements of (y -> x), singletonHasNoExtraElements of (x -> y)) - thenHave(exists(b, (pair(x, y) === pair(x, b)) /\ in(x, singleton(x)) /\ in(b, singleton(y)))) by RightExists - thenHave(exists(a, exists(b, (pair(x, y) === pair(a, b)) /\ in(a, singleton(x)) /\ in(b, singleton(y))))) by RightExists - have(thesis) by Tautology.from(lastStep, elemOfCartesianProduct of (t -> pair(x, y), x -> singleton(x), y -> singleton(y))) - } - - val relS = have(relation(s)) subproof { - have((z === pair(x, y)) |- in(z, cartesianProduct(singleton(x), singleton(y)))) by Substitution.ApplyRules(z === pair(x, y))(xyInCart) - have(in(z, s) ==> in(z, cartesianProduct(singleton(x), singleton(y)))) by Tautology.from(lastStep, elemOfS) - thenHave(forall(z, in(z, s) ==> in(z, cartesianProduct(singleton(x), singleton(y))))) by RightForall - have(relationBetween(s, singleton(x), singleton(y))) by Tautology.from( - lastStep, - subsetAxiom of (x -> s, y -> cartesianProduct(singleton(x), singleton(y))), - relationBetween.definition of (r -> s, a -> singleton(x), b -> singleton(y)) - ) - thenHave(exists(b, relationBetween(s, singleton(x), b))) by RightExists - thenHave(exists(a, exists(b, relationBetween(s, a, b)))) by RightExists - have(thesis) by Tautology.from(lastStep, relation.definition of r -> s) - } - - val uniq = have(forall(a, exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s)))) subproof { - have((pair(a, z) === pair(x, y)) <=> in(pair(a, z), s)) by Restate.from(elemOfS of z -> pair(a, z)) - val eq = thenHave(((a === x) /\ (z === y)) <=> in(pair(a, z), s)) by Substitution.ApplyRules(pairExtensionality) - thenHave((a === x) |- (z === y) <=> in(pair(a, z), s)) by Tautology - thenHave((a === x) |- forall(z, (z === y) <=> in(pair(a, z), s))) by RightForall - thenHave((a === x) |- exists(b, forall(z, (z === b) <=> in(pair(a, z), s)))) by RightExists - thenHave((a === x) |- existsOne(b, in(pair(a, b), s))) by RightExistsOne - val pos = thenHave((a === x) |- exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s))) by Weakening - - val ax = have(in(pair(a, z), s) |- (a === x)) by Weakening(eq) - thenHave(!(a === x) |- !in(pair(a, z), s)) by Restate - thenHave(!(a === x) |- forall(z, !in(pair(a, z), s))) by RightForall - thenHave(!(a === x) |- !exists(z, in(pair(a, z), s))) by Restate - val neg = thenHave(!(a === x) |- exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s))) by Weakening - - have(exists(b, in(pair(a, b), s)) ==> existsOne(b, in(pair(a, b), s))) by Tautology.from(pos, neg) - thenHave(thesis) by RightForall - } - - val dom = have(functionDomain(s) === singleton(x)) subproof { - have(forall(t, in(t, functionDomain(s)) <=> exists(a, in(pair(t, a), s)))) by InstantiateForall(functionDomain(s))(functionDomain.definition of r -> s) - val inDom = thenHave(in(t, functionDomain(s)) <=> exists(a, in(pair(t, a), s))) by InstantiateForall(t) - - have(in(pair(t, a), s) <=> ((t === x) /\ (a === y))) by Substitution.ApplyRules(pairExtensionality)(elemOfS of z -> pair(t, a)) - thenHave(forall(a, in(pair(t, a), s) <=> ((t === x) /\ (a === y)))) by RightForall - val exToEq = have(exists(a, in(pair(t, a), s)) <=> exists(a, ((t === x) /\ (a === y)))) by Cut( - lastStep, - existentialEquivalenceDistribution of (P -> lambda(a, in(pair(t, a), s)), Q -> lambda(a, ((t === x) /\ (a === y)))) - ) - - val exRedundant = have(exists(a, ((t === x) /\ (a === y))) <=> (t === x)) subproof { - have((t === x) /\ (a === y) |- (t === x)) by Restate - val fwd = thenHave(exists(a, (t === x) /\ (a === y)) |- (t === x)) by LeftExists - - have((t === x) |- (t === x) /\ (y === y)) by Restate - val bwd = thenHave((t === x) |- exists(a, (t === x) /\ (a === y))) by RightExists - - have(thesis) by Tautology.from(fwd, bwd) - } - - have((t === x) <=> in(t, singleton(x))) by Restate.from(singletonHasNoExtraElements of y -> t) - have(in(t, functionDomain(s)) <=> in(t, singleton(x))) by Tautology.from(lastStep, exRedundant, exToEq, inDom) - thenHave(forall(t, in(t, functionDomain(s)) <=> in(t, singleton(x)))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> functionDomain(s), y -> singleton(x))) - } - - val ran = have(functionRange(s) === singleton(y)) subproof { - have(forall(t, in(t, functionRange(s)) <=> exists(a, in(pair(a, t), s)))) by InstantiateForall(functionRange(s))(functionRange.definition of r -> s) - val inDom = thenHave(in(t, functionRange(s)) <=> exists(a, in(pair(a, t), s))) by InstantiateForall(t) - - have(in(pair(a, t), s) <=> ((a === x) /\ (t === y))) by Substitution.ApplyRules(pairExtensionality)(elemOfS of z -> pair(a, t)) - thenHave(forall(a, in(pair(a, t), s) <=> ((a === x) /\ (t === y)))) by RightForall - val exToEq = have(exists(a, in(pair(a, t), s)) <=> exists(a, ((a === x) /\ (t === y)))) by Cut( - lastStep, - existentialEquivalenceDistribution of (P -> lambda(a, in(pair(a, t), s)), Q -> lambda(a, ((a === x) /\ (t === y)))) - ) - - val exRedundant = have(exists(a, ((a === x) /\ (t === y))) <=> (t === y)) subproof { - have((a === x) /\ (t === y) |- (t === y)) by Restate - val fwd = thenHave(exists(a, (a === x) /\ (t === y)) |- (t === y)) by LeftExists - - have((t === y) |- (x === x) /\ (t === y)) by Restate - val bwd = thenHave((t === y) |- exists(a, (a === x) /\ (t === y))) by RightExists - - have(thesis) by Tautology.from(fwd, bwd) - } - - have((t === y) <=> in(t, singleton(y))) by Restate.from(singletonHasNoExtraElements of (y -> t, x -> y)) - have(in(t, functionRange(s)) <=> in(t, singleton(y))) by Tautology.from(lastStep, exRedundant, exToEq, inDom) - thenHave(forall(t, in(t, functionRange(s)) <=> in(t, singleton(y)))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> functionRange(s), y -> singleton(y))) - } - - have(functional(s)) by Tautology.from(relS, uniq, functional.definition of f -> s) - - have(thesis) by Tautology.from(ran, dom, lastStep) - - } - - val setOfFunctionsUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) - ) { - have(thesis) by UniqueComprehension(powerSet(cartesianProduct(x, y)), lambda(t, functionalOver(t, x))) - } - - /** - * Set of functions --- All functions from `x` to `y`, denoted `x → y` or - * `→(x, y)`. - * - * Since functions from `x` to `y` contain pairs of the form `(a, b) | a ∈ - * x, b ∈ y`, it is a filtering on the power set of their product, i.e. `x - * → y ⊆ PP(x * y)`. - */ - val setOfFunctions = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x))))(setOfFunctionsUniqueness) - val |=> = setOfFunctions - - extension (x: Term) { - // Infix notation for a set of functions: x |=> y - def |=>(y: Term): Term = setOfFunctions(x, y) - } - - // class SetOfFunctions(val x: Term, val y: Term) extends AppliedFunctional(setOfFunctions, Seq(x, y)) with LisaObject[SetOfFunctions] { - // override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): SetOfFunctions = SetOfFunctions(x.substituteUnsafe(map), y.substituteUnsafe(map)) - - // override def toString(): String = x.toStringSeparated() + " |=> " + y.toStringSeparated() - // override def toStringSeparated(): String = toString() - // } - // object SetOfFunctions { - // def unapply(sof: SetOfFunctions): Option[(Term, Term)] = sof match - // case AppliedFunctional(label, Seq(x, y)) if label == setOfFunctions => Some((x, y)) - // case _ => None - // } - // extension (x: Term) { - // // Infix notation for a set of functions: x |=> y - // def |=>(y: Term): Term = SetOfFunctions(x, y) - // } - - /** - * Function From (x to y) --- denoted `f ∈ x → y` or `f: x → y`. - */ - val functionFrom = DEF(f, x, y) --> in(f, x |=> y) - - /** - * Theorem --- A function between two sets is [[functional]] - */ - val functionFromImpliesFunctional = Theorem( - functionFrom(f, x, y) |- functional(f) - ) { - have(∀(t, in(t, x |=> y) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) by InstantiateForall(x |=> y)(setOfFunctions.definition) - val funSetDef = thenHave(in(f, x |=> y) <=> (in(f, powerSet(cartesianProduct(x, y))) /\ functionalOver(f, x))) by InstantiateForall(f) - - val funOver = have(functionFrom(f, x, y) |- functional(f)) by Tautology.from(funSetDef, functionFrom.definition, functionalOver.definition) - } - - /** - * Theorem --- if `f` is a [[functionFrom]] `x` to `y`, i.e. `f ∈ x → y`, - * then `x` is precisely its domain as a relation. - */ - val functionFromImpliesDomainEq = Theorem( - functionFrom(f, x, y) |- (functionDomain(f) === x) - ) { - have(∀(t, in(t, x |=> y) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) by InstantiateForall(x |=> y)(setOfFunctions.definition) - val funSetDef = thenHave(in(f, x |=> y) <=> (in(f, powerSet(cartesianProduct(x, y))) /\ functionalOver(f, x))) by InstantiateForall(f) - - have(thesis) by Tautology.from(functionFrom.definition, funSetDef, functionalOver.definition) - } - - /** - * Theorem --- the range of a function is a subset of its codomain. - */ - val functionImpliesRangeSubsetOfCodomain = Theorem( - functionFrom(f, x, y) |- subset(functionRange(f), y) - ) { - have(∀(t, in(t, x |=> y) <=> (in(t, powerSet(cartesianProduct(x, y))) /\ functionalOver(t, x)))) by InstantiateForall(x |=> y)(setOfFunctions.definition) - val funSetDef = thenHave(in(f, x |=> y) <=> (in(f, powerSet(cartesianProduct(x, y))) /\ functionalOver(f, x))) by InstantiateForall(f) - - have(functionFrom(f, x, y) |- ∀(z, in(z, f) ==> in(z, cartesianProduct(x, y)))) by Tautology.from( - functionFrom.definition, - funSetDef, - powerAxiom of (x -> f, y -> cartesianProduct(x, y)), - subsetAxiom of (x -> f, y -> cartesianProduct(x, y)) - ) - thenHave(functionFrom(f, x, y) |- in(pair(a, t), f) ==> in(pair(a, t), cartesianProduct(x, y))) by InstantiateForall(pair(a, t)) - thenHave((functionFrom(f, x, y), in(pair(a, t), f)) |- in(pair(a, t), cartesianProduct(x, y))) by Restate - andThen(Substitution.applySubst(pairInCartesianProduct of (b -> t))) - thenHave((functionFrom(f, x, y), in(pair(a, t), f)) |- in(t, y)) by Weakening - val funFromty = thenHave((functionFrom(f, x, y), ∃(a, in(pair(a, t), f))) |- in(t, y)) by LeftExists - - have(∀(t, in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f))))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) - thenHave(in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f)))) by InstantiateForall(t) - val ranat = thenHave(in(t, functionRange(f)) |- ∃(a, in(pair(a, t), f))) by Weakening - - have((functionFrom(f, x, y), in(t, functionRange(f))) |- in(t, y)) by Cut(ranat, funFromty) - thenHave((functionFrom(f, x, y)) |- in(t, functionRange(f)) ==> in(t, y)) by Restate - thenHave((functionFrom(f, x, y)) |- ∀(t, in(t, functionRange(f)) ==> in(t, y))) by RightForall - andThen(Substitution.applySubst(subsetAxiom of (x -> functionRange(f)))) - } - - val functionApplicationUniqueness = Theorem( - ∃!(z, ((functional(f) /\ in(x, functionDomain(f))) ==> in(pair(x, z), f)) /\ ((!functional(f) \/ !in(x, functionDomain(f))) ==> (z === ∅))) - ) { - val prem = functional(f) /\ in(x, functionDomain(f)) - - // we prove thesis by two cases, first by assuming prem, and then by assuming !prem - - // positive direction - have(functional(f) |- ∀(x, ∃(y, in(pair(x, y), f)) ==> ∃!(y, in(pair(x, y), f)))) by Tautology.from(functional.definition) - val funcDef = thenHave(functional(f) |- ∃(y, in(pair(x, y), f)) ==> ∃!(y, in(pair(x, y), f))) by InstantiateForall(x) - - have((functionDomain(f) === functionDomain(f)) <=> ∀(t, in(t, functionDomain(f)) <=> (∃(y, in(pair(t, y), f))))) by InstantiateForall(functionDomain(f))( - functionDomain.definition of (r -> f) - ) - thenHave(∀(t, in(t, functionDomain(f)) <=> (∃(y, in(pair(t, y), f))))) by Restate - thenHave(in(x, functionDomain(f)) <=> (∃(y, in(pair(x, y), f)))) by InstantiateForall(x) - val domDef = thenHave(in(x, functionDomain(f)) |- ∃(y, in(pair(x, y), f))) by Weakening - - val uniqPrem = have(functional(f) /\ in(x, functionDomain(f)) |- ∃!(z, in(pair(x, z), f))) by Tautology.from(funcDef, domDef) - - val positive = have(prem |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { - val lhs = have(prem /\ ((z === y) <=> in(pair(x, y), f)) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ ⊤))) subproof { - val iff = have(prem |- (in(pair(x, y), f)) <=> (prem ==> in(pair(x, y), f))) by Restate - have(prem /\ ((z === y) <=> in(pair(x, y), f)) |- ((z === y) <=> in(pair(x, y), f))) by Restate - val subst = thenHave((prem /\ ((z === y) <=> in(pair(x, y), f)), (in(pair(x, y), f)) <=> (prem ==> in(pair(x, y), f))) |- ((z === y) <=> (prem ==> in(pair(x, y), f)))) by RightSubstIff - .withParametersSimple( - List(((in(pair(x, y), f)), (prem ==> in(pair(x, y), f)))), - lambda(h, ((z === y) <=> h)) - ) - - have((prem /\ ((z === y) <=> in(pair(x, y), f)), prem) |- ((z === y) <=> (prem ==> in(pair(x, y), f)))) by Cut(iff, subst) - thenHave(thesis) by Restate - } - - val topIntro = have((prem, ((z === y) <=> in(pair(x, y), f))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) subproof { - val topIff = have(prem |- (!prem ==> (y === ∅)) <=> ⊤) by Restate - val topSubst = have( - (prem /\ ((z === y) <=> in(pair(x, y), f)), ((!prem ==> (y === ∅)) <=> ⊤)) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))) - ) by RightSubstIff.withParametersSimple(List(((!prem ==> (y === ∅)), ⊤)), lambda(h, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ h))))(lhs) - - have((prem /\ ((z === y) <=> in(pair(x, y), f)), prem) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Cut(topIff, topSubst) - thenHave((prem, ((z === y) <=> in(pair(x, y), f))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Restate - } - - val quantification = have((prem, ∃!(z, in(pair(x, z), f))) |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { - have((prem, ∀(y, ((z === y) <=> in(pair(x, y), f)))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by LeftForall(topIntro) - thenHave((prem, ∀(y, ((z === y) <=> in(pair(x, y), f)))) |- ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))))) by RightForall - thenHave((prem, ∀(y, ((z === y) <=> in(pair(x, y), f)))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))))) by RightExists - thenHave( - (prem, ∃(z, ∀(y, ((z === y) <=> in(pair(x, y), f))))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))))) - ) by LeftExists - thenHave(thesis) by Restate - } - - have(thesis) by Cut(uniqPrem, quantification) - } - - // negative - have((∅ === y) <=> (∅ === y)) by Restate - thenHave(∀(y, (∅ === y) <=> (∅ === y))) by RightForall - thenHave(∃(z, ∀(y, (z === y) <=> (∅ === y)))) by RightExists - val emptyPrem = thenHave(∃!(z, (z === ∅))) by Restate - - val negative = have(!prem |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { - val lhs = have(!prem /\ ((z === y) <=> (y === ∅)) |- ((z === y) <=> ((!prem ==> (y === ∅)) /\ ⊤))) subproof { - val iff = have(!prem |- ((y === ∅)) <=> (!prem ==> (y === ∅))) by Restate - have(!prem /\ ((z === y) <=> (y === ∅)) |- ((z === y) <=> (y === ∅))) by Restate - val subst = thenHave( - (!prem /\ ((z === y) <=> (y === ∅)), ((y === ∅)) <=> (!prem ==> (y === ∅))) |- ((z === y) <=> (!prem ==> (y === ∅))) - ) by RightSubstIff.withParametersSimple(List((((y === ∅)), (!prem ==> (y === ∅)))), lambda(h, ((z === y) <=> h))) - - have((!prem /\ ((z === y) <=> (y === ∅)), !prem) |- ((z === y) <=> (!prem ==> (y === ∅)))) by Cut(iff, subst) - thenHave(thesis) by Restate - } - - val topIntro = have((!prem, ((z === y) <=> (y === ∅))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) subproof { - val topIff = have(!prem |- (prem ==> in(pair(x, y), f)) <=> ⊤) by Restate - val topSubst = have( - (!prem /\ ((z === y) <=> (y === ∅)), ((prem ==> in(pair(x, y), f)) <=> ⊤)) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))) - ) by RightSubstIff.withParametersSimple(List(((prem ==> in(pair(x, y), f)), ⊤)), lambda(h, ((z === y) <=> ((!prem ==> (y === ∅)) /\ h))))(lhs) - - have((!prem /\ ((z === y) <=> (y === ∅)), !prem) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Cut(topIff, topSubst) - thenHave((!prem, ((z === y) <=> (y === ∅))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by Restate - } - - val quantification = - have((!prem, ∃!(z, (z === ∅))) |- ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅))))) subproof { - have((!prem, ∀(y, ((z === y) <=> (y === ∅)))) |- ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by LeftForall(topIntro) - thenHave((!prem, ∀(y, ((z === y) <=> (y === ∅)))) |- ∀(y, (z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))) by RightForall - thenHave((!prem, ∀(y, ((z === y) <=> (y === ∅)))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅))))))) by RightExists - thenHave( - (!prem, ∃(z, ∀(y, ((z === y) <=> (y === ∅))))) |- ∃(z, ∀(y, ((z === y) <=> ((prem ==> in(pair(x, y), f)) /\ (!prem ==> (y === ∅)))))) - ) by LeftExists - thenHave(thesis) by Restate - } - - have(thesis) by Cut(emptyPrem, quantification) - } - - val negRhs = thenHave(() |- (prem, ∃!(z, ((prem ==> in(pair(x, z), f)) /\ (!prem ==> (z === ∅)))))) by Restate - - have(thesis) by Cut.withParameters(prem)(negRhs, positive) - } - - /** - * Function application --- denoted `f(x)`. The unique element `z` such that - * `(x, z) ∈ f` if it exists and `f` is functional, [[emptySet]] otherwise. - */ - val app = - DEF(f, x) --> The(z, ((functional(f) /\ in(x, functionDomain(f))) ==> in(pair(x, z), f)) /\ ((!functional(f) \/ !in(x, functionDomain(f))) ==> (z === ∅)))(functionApplicationUniqueness) - - /** - * Theorem -- Function application typing - * - * `f ∈ x → y, a ∈ x |- f(a) ∈ y` - */ - val functionFromApplication = Theorem( - in(f, x |=> y) /\ in(a, x) |- in(app(f, a), y) - ) { - val thm = have(functionFrom(f, x, y) /\ in(a, x) |- in(app(f, a), y)) subproof { - val funcFrom = assume(functionFrom(f, x, y)) - val inDomain = assume(in(a, x)) - - val isFunctional = have(functional(f)) by Tautology.from(funcFrom, functionFromImpliesFunctional) - val relDomainEq = have(functionDomain(f) === x) by Tautology.from(funcFrom, functionFromImpliesDomainEq) - val inRelDomain = have(in(a, functionDomain(f))) by Substitution.ApplyRules(relDomainEq)(inDomain) - - val appDef = have( - ((functional(f) /\ in(a, functionDomain(f))) ==> in(pair(a, app(f, a)), f)) - /\ ((!functional(f) \/ !in(a, functionDomain(f))) ==> (app(f, a) === ∅)) - ) by InstantiateForall(app(f, a))( - app.definition of (x := a) - ) - - have(in(pair(a, app(f, a)), f)) by Tautology.from( - isFunctional, - inRelDomain, - appDef - ) - val pairInF = thenHave(∃(z, in(pair(z, app(f, a)), f))) by RightExists - - have(∀(t, in(t, functionRange(f)) <=> ∃(z, in(pair(z, t), f)))) by InstantiateForall(functionRange(f))( - functionRange.definition of (r := f) - ) - val rangeDef = thenHave(in(app(f, a), functionRange(f)) <=> ∃(z, in(pair(z, app(f, a)), f))) by InstantiateForall(app(f, a)) - - val appInRange = have(in(app(f, a), functionRange(f))) by Tautology.from(rangeDef, pairInF) - - have(subset(functionRange(f), y)) by Weakening(functionImpliesRangeSubsetOfCodomain) - thenHave(∀(z, in(z, functionRange(f)) ==> in(z, y))) by Substitution.ApplyRules(subsetAxiom of (x := functionRange(f))) - thenHave(in(app(f, a), functionRange(f)) ==> in(app(f, a), y)) by InstantiateForall(app(f, a)) - - have(thesis) by Tautology.from(appInRange, lastStep) - } - - have(thesis) by Tautology.from(thm, functionFrom.definition) - - } - - val pairInFunctionIsApp = Theorem( - functional(f) /\ in(a, functionDomain(f)) |- in(pair(a, b), f) <=> (app(f, a) === b) - ) { - val appDef = have( - (app(f, a) === b) <=> (((functional(f) /\ in(a, functionDomain(f))) ==> in(pair(a, b), f)) /\ ((!functional(f) \/ !in(a, functionDomain(f))) ==> (b === ∅))) - ) by InstantiateForall(b)(app.definition of x -> a) - - assume(functional(f) /\ in(a, functionDomain(f))) - - val fwd = have(in(pair(a, b), f) |- app(f, a) === b) by Tautology.from(appDef) - val bwd = have(app(f, a) === b |- in(pair(a, b), f)) by Tautology.from(appDef) - have(thesis) by Tautology.from(fwd, bwd) - } - - val functionalOverApplication = Theorem( - functionalOver(f, x) /\ in(a, x) |- in(pair(a, b), f) <=> (app(f, a) === b) - ) { - assume(functionalOver(f, x)) - assume(in(a, x)) - - val domEQ = have(functionDomain(f) === x) by Tautology.from(functionalOver.definition) - have(in(a, x)) by Restate - thenHave(in(a, functionDomain(f))) by Substitution.ApplyRules(domEQ) - - have(thesis) by Tautology.from(lastStep, functionalOver.definition, pairInFunctionIsApp) - } - - /** - * Lemma --- If something is in the application of function `f` to `x`, then - * `f` is functional, `x` is in the domain of `f`, and the pair `(x, f(x))` is - * in `f`. Essentially an inversion lemma for [[app]]. - */ - val inAppIsFunction = Lemma( - in(y, app(f, x)) |- functional(f) /\ in(x, functionDomain(f)) /\ in(pair(x, app(f, x)), f) - ) { - assume(in(y, app(f, x))) - - val appIsNotEmpty = have(!(app(f, x) === ∅)) by Tautology.from( - setWithElementNonEmpty of (x := app(f, x)) - ) - - val appDef = have( - ((functional(f) /\ in(x, functionDomain(f))) ==> in(pair(x, app(f, x)), f)) - /\ ((!functional(f) \/ !in(x, functionDomain(f))) ==> (app(f, x) === ∅)) - ) by InstantiateForall(app(f, x))( - app.definition - ) - - val isFunctional = have(functional(f) /\ in(x, functionDomain(f))) by Tautology.from(appDef, appIsNotEmpty) - - val pairIn = have(in(pair(x, app(f, x)), f)) by Tautology.from( - appDef, - isFunctional - ) - - have(thesis) by Tautology.from(isFunctional, pairIn) - } - - val elemOfFunctional = Theorem( - functional(f) |- in(t, f) <=> exists(c, exists(d, in(c, functionDomain(f)) /\ in(d, functionRange(f)) /\ (t === pair(c, d)) /\ (app(f, c) === d))) - ) { - // since f is a relation - // t \in f <=> \exists c \in dom f, d \in ran f. t = (c, d) - // we need to show that the app part of the conclusion is redundant by definition of app - // have(functional(f) |- in(t, f) <=> exists(c, exists(d, in(c, functionDomain(f)) /\ in(d, functionRange(f)) /\ (t === pair(c, d))))) by Tautology.from(functional.definition, ???) - sorry - } - - val elemOfFunctionalOver = Theorem( - functionalOver(f, a) |- in(t, f) <=> exists(c, exists(d, in(c, a) /\ in(d, functionRange(f)) /\ (t === pair(c, d)) /\ (app(f, c) === d))) - ) { - sorry - } - - val elemOfFunctionFrom = Theorem( - functionFrom(f, a, b) |- in(t, f) <=> exists(c, exists(d, in(c, a) /\ in(d, b) /\ (t === pair(c, d)) /\ (app(f, c) === d))) - ) { - sorry - } - - val functionsEqualIfEqualOnDomain = Theorem( - functionalOver(f, a) /\ functionalOver(g, a) |- forall(z, in(z, a) ==> (app(f, z) === app(g, z))) <=> (f === g) - ) { - assume(functionalOver(f, a)) - assume(functionalOver(g, a)) - - sorry - } - - val functionsSubsetIfEqualOnSubsetDomain = Theorem( - functionalOver(f, a) /\ functionalOver(g, b) /\ subset(a, b) /\ forall(z, in(z, a) ==> (app(f, z) === app(g, z))) |- subset(f, g) - ) { - assume(functionalOver(f, a)) - assume(functionalOver(g, b)) - - sorry - } - - val restrictedFunctionUniqueness = Theorem( - ∃!(g, ∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z))))))) - ) { - have(∃!(g, ∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z)))))))) by UniqueComprehension( - f, - lambda(t, ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z))))) - ) - } - - /** - * Function Restriction --- The restriction of a function f to a domain x, - * also written as f_x. - * - * `restrictedFunction(f, x) = {(y, f(y)) | y ∈ x}` - * - * @param f function (set) - * @param x set to restrict to - */ - val restrictedFunction = DEF(f, x) --> The(g, ∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z)))))))(restrictedFunctionUniqueness) - - /** - * Pair membership in a restricted function -- A pair `(t, a)` is in `f_x` iff `(t, a) ∈ f` and `t ∈ x`. - * - * This is a direct but painful corollary of the definition. - */ - val restrictedFunctionPairMembership = Lemma( - in(pair(t, a), restrictedFunction(f, x)) <=> (in(pair(t, a), f) /\ in(t, x)) - ) { - val g = restrictedFunction(f, x) - - have(∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, x) /\ (t === pair(y, z))))))) by Definition( - restrictedFunction, - restrictedFunctionUniqueness - )(f, x) - val pairMembership = thenHave( - in(pair(t, a), g) <=> (in(pair(t, a), f) /\ ∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))))) - ) by InstantiateForall(pair(t, a)) - - have((pair(t, a) === pair(y, z)) <=> ((t === y) /\ (a === z))) by Restate.from(pairExtensionality of (a -> t, b -> a, c -> y, d -> z)) - thenHave((in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y) /\ (a === z))) by Tautology - thenHave(∀(z, (in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y) /\ (a === z)))) by RightForall - - val existentialEquiv1 = have(∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))) <=> ∃(z, in(y, x) /\ (t === y) /\ (a === z))) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P -> lambda(z, in(y, x) /\ (pair(t, a) === pair(y, z))), - Q -> lambda(z, in(y, x) /\ (t === y) /\ (a === z)) - ) - ) - - have(∃(z, in(y, x) /\ (t === y) /\ (a === z)) <=> (in(y, x) /\ (t === y))) by Restate.from( - equalityInExistentialQuantifier of ( - P -> lambda(z, in(y, x) /\ (t === y)), - y -> a - ) - ) - - have(∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y))) by Tautology.from(existentialEquiv1, lastStep) - thenHave(∀(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))) <=> (in(y, x) /\ (t === y)))) by RightForall - - val existentialEquiv2 = have(∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z)))) <=> ∃(y, in(y, x) /\ (t === y))) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P -> lambda(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z)))), - Q -> lambda(y, in(y, x) /\ (t === y)) - ) - ) - - have(∃(y, in(y, x) /\ (t === y)) <=> in(t, x)) by Restate.from( - equalityInExistentialQuantifier of ( - P -> lambda(y, in(y, x)), - y -> t - ) - ) - - have(∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z)))) <=> in(t, x)) by Tautology.from(existentialEquiv2, lastStep) - thenHave((in(pair(t, a), f) /\ ∃(y, ∃(z, in(y, x) /\ (pair(t, a) === pair(y, z))))) <=> (in(pair(t, a), f) /\ in(t, x))) by Tautology - - have(thesis) by Tautology.from(lastStep, pairMembership) - } - - /** - * Restricted function domain -- For a function `f`, the domain of `f_x` is `x ∩ functionDomain(f)`. - */ - val restrictedFunctionDomain = Theorem( - functionDomain(restrictedFunction(f, x)) === (x ∩ functionDomain(f)) - ) { - val D = variable - val dom = x ∩ functionDomain(f) - val g = restrictedFunction(f, x) - - // Characterize x ∩ functionDomain(f) - val domCharacterization = have(∀(t, in(t, dom) <=> (∃(a, in(pair(t, a), f)) /\ in(t, x)))) subproof { - // Use the definition of the intersection - have(∀(t, in(t, dom) <=> (in(t, x) /\ in(t, functionDomain(f))))) by Definition( - setIntersection, - setIntersectionUniqueness - )(x, functionDomain(f)) - val intersectionDef = thenHave(in(t, dom) <=> (in(t, x) /\ in(t, functionDomain(f)))) by InstantiateForall(t) - - // Use the definition of the relation domain - have(∀(t, in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f)))) by Definition( - functionDomain, - relationDomainUniqueness - )(f) - thenHave(in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f))) by InstantiateForall(t) - - // Conclude - have(in(t, dom) <=> (∃(a, in(pair(t, a), f)) /\ in(t, x))) by Tautology.from(intersectionDef, lastStep) - thenHave(thesis) by RightForall - } - - // Characterize the domain of g - have(∀(D, (functionDomain(g) === D) <=> ∀(t, in(t, D) <=> ∃(a, in(pair(t, a), g))))) by Tautology.from( - functionDomain.definition of (r -> g), - relationDomainUniqueness - ) - val characterization = thenHave((functionDomain(g) === dom) <=> ∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), g)))) by InstantiateForall(dom) - - // Use the membership of a pair in the restricted function to derive a simpler characterization - have(∀(a, in(pair(t, a), g) <=> (in(pair(t, a), f) /\ in(t, x)))) by RightForall(restrictedFunctionPairMembership) - have(∃(a, in(pair(t, a), g)) <=> ∃(a, in(pair(t, a), f) /\ in(t, x))) by Tautology.from( - lastStep, - existentialEquivalenceDistribution of ( - P -> lambda(a, in(pair(t, a), g)), - Q -> lambda(a, in(pair(t, a), f) /\ in(t, x)) - ) - ) - - // Extract in(t, x) from the existential quantifier - val p = formulaVariable // local shadowing to correctly use the theorem - have(∃(a, in(pair(t, a), g)) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x)) by Tautology.from( - lastStep, - existentialConjunctionWithClosedFormula of ( - P -> lambda(a, in(pair(t, a), f)), - p -> in(t, x) - ) - ) - - thenHave((in(t, dom) <=> ∃(a, in(pair(t, a), g))) <=> (in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x))) by Tautology - thenHave(∀(t, (in(t, dom) <=> ∃(a, in(pair(t, a), g))) <=> (in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x)))) by RightForall - - have(∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), g))) <=> ∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x))) by Cut( - lastStep, - universalEquivalenceDistribution of ( - P -> lambda(t, in(t, dom) <=> ∃(a, in(pair(t, a), g))), - Q -> lambda(t, in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x)) - ) - ) - - val simplerCharacterization = have((functionDomain(g) === dom) <=> ∀(t, in(t, dom) <=> ∃(a, in(pair(t, a), f)) /\ in(t, x))) by Tautology.from(characterization, lastStep) - - have(thesis) by Tautology.from(domCharacterization, simplerCharacterization) - } - - val restrictedFunctionIsFunctionalOver = Lemma( - functional(f) |- functionalOver(restrictedFunction(f, x), setIntersection(x, functionDomain(f))) - ) { - // restriction is a function - - // its domain is indeed x \cap dom f - - // it is functionalOver its domain - - sorry - } - - val restrictedFunctionApplication = Lemma( - in(y, x) |- app(f, y) === app(restrictedFunction(f, x), y) - ) { - sorry - } - - /** - * Restricted function cancellation --- Restricting a function to its relation domain does nothing. - */ - val restrictedFunctionCancellation = Theorem( - functional(f) |- restrictedFunction(f, functionDomain(f)) === f - ) { - val g = restrictedFunction(f, functionDomain(f)) - - assume(functional(f)) - - have(∀(t, in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f)))) by Definition(functionDomain, relationDomainUniqueness)(f) - thenHave(in(y, functionDomain(f)) <=> ∃(a, in(pair(y, a), f))) by InstantiateForall(y) - - have(∀(t, in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z))))))) by Definition( - restrictedFunction, - restrictedFunctionUniqueness - )(f, functionDomain(f)) - val equiv = thenHave(in(t, g) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))))) by InstantiateForall(t) - - // Prove that the second part of the conjunction is extraneous - val hypo = have(in(t, f) |- in(t, f)) by Hypothesis - have(in(t, f) |- ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z))))) by InstantiateForall(t)(functionalMembership) - have(in(t, f) |- in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z))))) by RightAnd(hypo, lastStep) - val forward = thenHave(in(t, f) ==> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))))) by Restate - - val backward = have(in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))) ==> in(t, f)) by Tautology - - have(in(t, f) <=> (in(t, f) /\ ∃(y, ∃(z, in(y, functionDomain(f)) /\ (t === pair(y, z)))))) by RightIff(forward, backward) - - // Conclude by extensionnality - have(in(t, g) <=> in(t, f)) by Tautology.from(equiv, lastStep) - thenHave(∀(t, in(t, g) <=> in(t, f))) by RightForall - - have(g === f) by Tautology.from(extensionalityAxiom of (x -> g, y -> f), lastStep) - } - - val restrictedFunctionAbsorption = Theorem( - restrictedFunction(restrictedFunction(f, x), y) === restrictedFunction(f, setIntersection(x, y)) - ) { - sorry - } - - /** - * Theorem --- if `f` is [[functional]] over `x`, then `x` is precisely its - * domain as a relation. - */ - val functionalOverImpliesDomain = Theorem( - functionalOver(f, x) |- (functionDomain(f) === x) - ) { - have(thesis) by Tautology.from(functionalOver.definition) - } - - /** - * Theorem --- if a set is in the range of a function, then there exists atleast - * one element in the domain mapping to it. - */ - val inRangeImpliesPullbackExists = Theorem( - functional(f) /\ in(z, functionRange(f)) |- ∃(t, in(t, functionDomain(f)) /\ (app(f, t) === z)) - ) { - val appIff = have( - (z === app(f, t)) <=> ((functional(f) /\ in(t, functionDomain(f))) ==> in(pair(t, z), f)) /\ ((!functional(f) \/ !in(t, functionDomain(f))) ==> (z === ∅)) - ) by InstantiateForall(z)(app.definition of (x -> t)) - - have(∀(t, in(t, functionRange(f)) <=> ∃(a, in(pair(a, t), f)))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) - thenHave(in(z, functionRange(f)) <=> ∃(a, in(pair(a, z), f))) by InstantiateForall(z) - val elementInDomainExists = thenHave(in(z, functionRange(f)) |- ∃(t, in(pair(t, z), f))) by Weakening - - val toApp = have( - (functional(f), in(t, functionDomain(f)), in(pair(t, z), f)) |- ((functional(f) /\ in(t, functionDomain(f))) ==> in(pair(t, z), f)) /\ ((!functional(f) \/ !in( - t, - functionDomain(f) - )) ==> (z === ∅)) - ) by Restate - val zAppdom = have((functional(f), in(t, functionDomain(f)), in(pair(t, z), f)) |- (z === app(f, t))) by Tautology.from(toApp, appIff) - - val pairInDomain = have(in(pair(t, z), f) |- in(t, functionDomain(f))) subproof { - have(∀(t, in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f)))) by InstantiateForall(functionDomain(f))(functionDomain.definition of (r -> f)) - val domDef = thenHave(in(t, functionDomain(f)) <=> ∃(a, in(pair(t, a), f))) by InstantiateForall(t) - - have(in(pair(t, z), f) |- in(pair(t, z), f)) by Hypothesis - val pairEx = thenHave(in(pair(t, z), f) |- ∃(a, in(pair(t, a), f))) by RightExists - - have(thesis) by Tautology.from(domDef, pairEx) - } - - val zApp2 = have((functional(f), in(pair(t, z), f)) |- (z === app(f, t))) by Cut(pairInDomain, zAppdom) - have((functional(f), in(pair(t, z), f)) |- in(t, functionDomain(f)) /\ (z === app(f, t))) by RightAnd(pairInDomain, zApp2) - thenHave((functional(f), in(pair(t, z), f)) |- ∃(t, in(t, functionDomain(f)) /\ (z === app(f, t)))) by RightExists - val zAppIfExists = thenHave((functional(f), ∃(t, in(pair(t, z), f))) |- ∃(t, in(t, functionDomain(f)) /\ (z === app(f, t)))) by LeftExists - - have((functional(f), in(z, functionRange(f))) |- ∃(t, in(t, functionDomain(f)) /\ (z === app(f, t)))) by Cut(elementInDomainExists, zAppIfExists) - thenHave(thesis) by Restate - } - - /** - * Theorem --- Union of two functions is a function if they agree on the - * intersection of their domains. - * - * `functional(f) ∧ functional(g) ∧ ∀ x, y. x ∈ dom(f) ∧ x ∈ dom(g) ⟹ (x, y) ∈ f <=> (x, y) ∈ g ⊢ functional(f ∪ g)` - */ - val unionOfFunctionsIsAFunction = Theorem( - functional(f) /\ functional(g) /\ forall(x, forall(y, (in(x, functionDomain(f)) /\ in(x, functionDomain(g))) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- functional(setUnion(f, g)) - ) { - // some renaming for convenience - val domF = functionDomain(f) - val domG = functionDomain(g) - - val h = setUnion(f, g) - val domH = setUnion(domF, domG) - - // is a relation - val isRelation = have(functional(f) /\ functional(g) |- relation(h)) by Tautology.from(functional.definition, functional.definition of f -> g, unionOfTwoRelations) - - // has the uniqueness property - val isFunctional = have( - functional(f) /\ functional(g) /\ forall(x, forall(y, (in(x, functionDomain(f)) /\ in(x, functionDomain(g))) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- forall( - x, - exists(y, in(pair(x, y), h)) ==> existsOne(y, in(pair(x, y), h)) - ) - ) subproof { - // x in domH <=> x in domF \/ x in domG - val domHDef = have(in(x, domH) <=> (in(x, domF) \/ in(x, domG))) by Restate.from(setUnionMembership of (z -> x, x -> domF, y -> domG)) - - // x in domF/G <=> exists y. xy in F/G - have(forall(t, in(t, domF) <=> exists(y, in(pair(t, y), f)))) by InstantiateForall(domF)(functionDomain.definition of r -> f) - val xInDomF = thenHave(in(x, domF) <=> exists(y, in(pair(x, y), f))) by InstantiateForall(x) - val xInDomG = xInDomF of f -> g - - val xInDomFOne = have((functional(f), in(x, domF)) |- existsOne(y, in(pair(x, y), f))) subproof { - have(functional(f) |- forall(x, exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f)))) by Weakening(functional.definition) - thenHave(functional(f) |- exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f))) by InstantiateForall(x) - - have(thesis) by Tautology.from(lastStep, xInDomF) - } - - // x in domH <=> exists y. xy in H OR domH = functionDomain(h) - val domHIsDomain = have(in(x, domH) <=> exists(y, in(pair(x, y), h))) subproof { - have(exists(y, in(pair(x, y), h)) <=> (exists(y, in(pair(x, y), f)) \/ exists(y, in(pair(x, y), g)))) subproof { - have(in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g))) by Restate.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) - thenHave(forall(y, in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g)))) by RightForall - have(exists(y, in(pair(x, y), h)) <=> exists(y, in(pair(x, y), f) \/ in(pair(x, y), g))) by Tautology.from( - lastStep, - existentialEquivalenceDistribution of (P -> lambda(y, in(pair(x, y), h)), Q -> lambda(y, in(pair(x, y), f) \/ in(pair(x, y), g))) - ) - // have(exists(y, in(pair(x, y), h)) <=> (exists(y, in(pair(x, y), f)) \/ exists(y, in(pair(x, y), g)))) by Tautology.from(lastStep, existentialDisjunctionCommutation of (P -> lambda(y, in(pair(x, y), f)), Q -> lambda(y, in(pair(x, y), g)))) // TODO: Possible Tautology Bug - thenHave(exists(y, in(pair(x, y), h)) <=> (exists(y, in(pair(x, y), f)) \/ exists(y, in(pair(x, y), g)))) by Substitution.ApplyRules( - existentialDisjunctionCommutation of (P -> lambda(y, in(pair(x, y), f)), Q -> lambda(y, in(pair(x, y), g))) // BUG: ? - ) - } - - have(thesis) by Tautology.from(lastStep, domHDef, xInDomF, xInDomG) - } - - // x in domF and x not in domG - have(functional(f) |- forall(x, exists(y, in(pair(x, y), f)) ==> existsOne(y, in(pair(x, y), f)))) by Weakening(functional.definition) - val exToExOne = thenHave((functional(f), exists(y, in(pair(x, y), f))) |- existsOne(y, in(pair(x, y), f))) by InstantiateForall(x) - - have(forall(y, !in(pair(x, y), g)) |- existsOne(y, in(pair(x, y), f)) <=> existsOne(y, in(pair(x, y), h))) subproof { - val fwd = have(in(pair(x, y), f) |- in(pair(x, y), h)) by Tautology.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) - val notzg = have(forall(y, !in(pair(x, y), g)) |- !in(pair(x, y), g)) by InstantiateForall - have(in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g))) by Restate.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) - - have(forall(y, !in(pair(x, y), g)) |- in(pair(x, y), h) <=> (in(pair(x, y), f))) by Tautology.from(lastStep, notzg, fwd) - thenHave(forall(y, !in(pair(x, y), g)) |- forall(y, in(pair(x, y), h) <=> (in(pair(x, y), f)))) by RightForall - - have(forall(y, !in(pair(x, y), g)) |- existsOne(y, in(pair(x, y), h)) <=> existsOne(y, in(pair(x, y), f))) by Tautology.from( - lastStep, - uniqueExistentialEquivalenceDistribution of (P -> lambda(z, in(pair(x, z), h)), Q -> lambda(z, in(pair(x, z), f))) - ) - } - - val notInG = have((functional(f), in(x, domF), !in(x, domG)) |- existsOne(y, in(pair(x, y), h))) by Tautology.from(lastStep, xInDomFOne, xInDomG) - - // x not in domF and x in domG - val notInF = - have((functional(g), !in(x, domF), in(x, domG)) |- existsOne(y, in(pair(x, y), h))) by Substitution.ApplyRules(unionCommutativity)(notInG of (f -> g, g -> f)) - - // x in domF and in domG - have( - forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- forall( - x, - forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))) - ) - ) by Hypothesis - thenHave( - forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))) |- (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)) - ) by InstantiateForall(x, y) - thenHave((forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (in(pair(x, y), f) <=> in(pair(x, y), g))) by Restate - val FToFG = thenHave( - (forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (in(pair(x, y), f) <=> (in(pair(x, y), g) \/ in(pair(x, y), f))) - ) by Tautology - - have(in(pair(x, y), h) <=> (in(pair(x, y), f) \/ in(pair(x, y), g))) by Restate.from(setUnionMembership of (z -> pair(x, y), x -> f, y -> g)) - - have((forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (in(pair(x, y), f) <=> in(pair(x, y), h))) by Tautology.from( - lastStep, - FToFG - ) - thenHave( - (forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- forall(y, in(pair(x, y), f) <=> in(pair(x, y), h)) - ) by RightForall - have( - (forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (existsOne(y, in(pair(x, y), f)) <=> existsOne( - y, - in(pair(x, y), h) - )) - ) by Tautology.from(lastStep, uniqueExistentialEquivalenceDistribution of (P -> lambda(z, in(pair(x, z), h)), Q -> lambda(z, in(pair(x, z), f)))) - val inFAndG = have( - (functional(f), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g)))), in(x, domF), in(x, domG)) |- (existsOne(y, in(pair(x, y), h))) - ) by Tautology.from(lastStep, xInDomFOne) - - have( - (functional(f), functional(g), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))))) |- in(x, domH) ==> existsOne(y, in(pair(x, y), h)) - ) by Tautology.from(inFAndG, notInF, notInG, domHDef) - thenHave( - (functional(f), functional(g), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))))) |- exists(y, in(pair(x, y), h)) ==> existsOne( - y, - in(pair(x, y), h) - ) - ) by Substitution.ApplyRules(domHIsDomain) - thenHave( - (functional(f), functional(g), forall(x, forall(y, (in(x, domF) /\ in(x, domG)) ==> (in(pair(x, y), f) <=> in(pair(x, y), g))))) |- forall( - x, - exists(y, in(pair(x, y), h)) ==> existsOne(y, in(pair(x, y), h)) - ) - ) by RightForall - } - - have(thesis) by Tautology.from(functional.definition of f -> h, isRelation, isFunctional) - } - - val unionOfFunctionsWithDisjointDomains = Theorem( - functionalOver(f, a) /\ functionalOver(g, b) /\ (setIntersection(a, b) === emptySet) |- functionalOver(setUnion(f, g), setUnion(a, b)) - ) { - // union is functional - - // domain of union is union of domains - - sorry - } - - /** - * Theorem --- Union of a Set of Functions is a Function - * - * Given a set `z` of functions (weakly or [[reflexive]]ly) totally ordered by - * the [[subset]] relation on the elements' domains ([[functionDomain]]), `∪ - * z` is [[functional]] (in particular, with domain as the union of the - * elements' domains). - */ - val unionOfFunctionSet = Theorem( - forall(t, in(t, z) ==> functional(t)) /\ forall(x, forall(y, (in(x, z) /\ in(y, z)) ==> (subset(x, y) \/ subset(y, x)))) |- functional(union(z)) - ) { - // add assumptions - assume(forall(t, in(t, z) ==> functional(t)), forall(x, forall(y, (in(x, z) /\ in(y, z)) ==> (subset(x, y) \/ subset(y, x))))) - - // assume, towards a contradiction - assume(!functional(union(z))) - - val u = union(z) - - // begin proof ---------------- - - // u is a relation - have(in(t, z) ==> functional(t)) by InstantiateForall - have(in(t, z) ==> relation(t)) by Tautology.from(lastStep, functional.definition of f -> t) - thenHave(forall(t, in(t, z) ==> relation(t))) by RightForall - val relU = have(relation(u)) by Tautology.from(lastStep, unionOfRelationSet) - - // if u is not functional, there exists a violating pair in it - val notFun = have(exists(x, exists(y, in(pair(x, y), u)) /\ !existsOne(y, in(pair(x, y), u)))) by Tautology.from(relU, functional.definition of f -> u) - - // the violating pairs must each come from a function in z - val exfg = have((in(pair(x, y), u), in(pair(x, w), u), !(y === w)) |- exists(f, in(f, z) /\ in(pair(x, y), f)) /\ exists(g, in(g, z) /\ in(pair(x, w), g))) by Tautology.from( - unionAxiom of (x -> z, z -> pair(x, y)), - unionAxiom of (x -> z, z -> pair(x, w)) - ) - - have((exists(f, in(f, z) /\ in(pair(x, y), f)), exists(g, in(g, z) /\ in(pair(x, w), g)), !(y === w)) |- ()) subproof { - have(forall(x, forall(y, (in(x, z) /\ in(y, z)) ==> (subset(x, y) \/ subset(y, x))))) by Restate - val subfg = thenHave((in(f, z) /\ in(g, z)) ==> (subset(f, g) \/ subset(g, f))) by InstantiateForall(f, g) - - have(forall(t, in(t, z) ==> functional(t))) by Restate - val funF = thenHave(in(f, z) ==> functional(f)) by InstantiateForall(f) - val funG = funF of f -> g - - val fg = have((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(f, g)) |- ()) subproof { - have(subset(f, g) |- forall(t, in(t, f) ==> in(t, g))) by Weakening(subsetAxiom of (x -> f, y -> g)) - thenHave(subset(f, g) |- in(pair(x, y), f) ==> in(pair(x, y), g)) by InstantiateForall(pair(x, y)) - thenHave((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(f, g)) |- in(pair(x, y), g) /\ in(pair(x, w), g) /\ !(y === w)) by Tautology - have(thesis) by Tautology.from(lastStep, funG, violatingPairInFunction of (f -> g, z -> w)) - } - - val gf = have((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(g, f)) |- ()) subproof { - have(subset(g, f) |- forall(t, in(t, g) ==> in(t, f))) by Weakening(subsetAxiom of (x -> g, y -> f)) - thenHave(subset(g, f) |- in(pair(x, w), g) ==> in(pair(x, w), f)) by InstantiateForall(pair(x, w)) - thenHave((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w), subset(g, f)) |- in(pair(x, w), f) /\ in(pair(x, y), f) /\ !(y === w)) by Tautology - have(thesis) by Tautology.from(lastStep, funF, violatingPairInFunction of (f -> f, z -> w)) - } - - have((in(f, z) /\ in(pair(x, y), f), in(g, z) /\ in(pair(x, w), g), !(y === w)) |- ()) by Tautology.from(subfg, fg, gf) - thenHave((exists(f, in(f, z) /\ in(pair(x, y), f)), (in(g, z) /\ in(pair(x, w), g)), !(y === w)) |- ()) by LeftExists - thenHave(thesis) by LeftExists - } - - have((in(pair(x, y), u) /\ in(pair(x, w), u) /\ !(y === w)) |- ()) by Tautology.from(lastStep, exfg) - thenHave(exists(w, in(pair(x, y), u) /\ in(pair(x, w), u) /\ !(y === w)) |- ()) by LeftExists - thenHave(exists(y, exists(w, in(pair(x, y), u) /\ in(pair(x, w), u) /\ !(y === w))) |- ()) by LeftExists - - have(exists(y, in(pair(x, y), u)) /\ !existsOne(y, in(pair(x, y), u)) |- ()) by Tautology.from(lastStep, atleastTwoExist of P -> lambda(y, in(pair(x, y), u))) - thenHave(exists(x, exists(y, in(pair(x, y), u)) /\ !existsOne(y, in(pair(x, y), u))) |- ()) by LeftExists - - // contradiction - have(thesis) by Tautology.from(lastStep, notFun) - } - - /** - * Theorem --- Domain of Functional Union - * - * If the unary union of a set is functional, then its domain is defined - * precisely by the union of the domains of its elements. - * - * functional(\cup z) |- \forall t. t \in dom(U z) <=> \exists y \in z. t - * \in dom(y) - * - * This holds, particularly, as the elements of z must be functions - * themselves, which follows from the assumption. - */ - val domainOfFunctionalUnion = Theorem( - functional(union(z)) |- forall(t, in(t, functionDomain(union(z))) <=> exists(y, in(y, z) /\ in(t, functionDomain(y)))) - ) { - assume(functional(union(z))) - have(relation(union(z))) by Tautology.from(functional.definition of f -> union(z)) - have(thesis) by Tautology.from(lastStep, domainOfRelationalUnion) - } - - val sigmaUniqueness = Theorem( - ∃!(z, ∀(t, in(t, z) <=> ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) - ) { - // we first show that the sigma is a subset of a larger set: Σ(A, B) ⊆ A × ⋃(range(B)) - val inclusion = have(∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))) ==> in(t, cartesianProduct(A, union(relationRange(B))))) subproof { - assume(∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))) - val aw = witness(lastStep) - have(∃(b, (t === pair(aw, b)) /\ in(aw, A) /\ in(b, app(B, aw)))) by Restate - val bw = witness(lastStep) - val isPair = have(t === pair(aw, bw)) by Restate - val inA = have(in(aw, A)) by Restate - val inBApp = have(in(bw, app(B, aw))) by Restate - - val inUnion = have(in(bw, union(relationRange(B)))) subproof { - val inRange = have(in(app(B, aw), relationRange(B))) subproof { - have(in(pair(aw, app(B, aw)), B)) by Tautology.from(inAppIsFunction of (f := B, x := aw, y := bw), inBApp) - val rangeCondition = thenHave(∃(a, in(pair(a, app(B, aw)), B))) by RightExists - - have(∀(t, in(t, relationRange(B)) <=> ∃(a, in(pair(a, t), B)))) by InstantiateForall(relationRange(B))( - relationRange.definition of (r -> B) - ) - val rangeDef = thenHave(in(app(B, aw), relationRange(B)) <=> ∃(a, in(pair(a, app(B, aw)), B))) by InstantiateForall(app(B, aw)) - - have(thesis) by Tautology.from(rangeDef, rangeCondition) - } - - // using the definition of a union with app(B, aw) as the intermediate set - have(in(app(B, aw), relationRange(B)) /\ in(bw, app(B, aw))) by Tautology.from(inRange, inBApp) - thenHave(∃(y, in(y, relationRange(B)) /\ in(bw, y))) by RightExists.withParameters(in(y, relationRange(B)) /\ in(bw, y), y, app(B, aw)) - thenHave(thesis) by Substitution.ApplyRules(unionAxiom of (x := relationRange(B), z := bw)) - } - - have(in(aw, A) /\ in(bw, union(relationRange(B)))) by Tautology.from(inA, inUnion) - have(in(pair(aw, bw), cartesianProduct(A, union(relationRange(B))))) by Tautology.from( - lastStep, - pairInCartesianProduct of (a := aw, b := bw, x := A, y := union(relationRange(B))) - ) - thenHave(in(t, cartesianProduct(A, union(relationRange(B))))) by Substitution.ApplyRules(isPair) - } - - // given belonging in a larger set, we can use unique comprehension - have(thesis) by UniqueComprehension.fromOriginalSet( - cartesianProduct(A, union(relationRange(B))), - lambda(t, ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))), - inclusion - ) - } - - /** - * Dependent Sum (Sigma) -- The generalized product. - * - * Given a set `A` and a function `B`, the dependent sum `Σ(A, B)` - * is the set of all pairs `(a, b)` such that `a` is in `A` and `b` - * is in `B(a)`. - * - * `Σ(A, B) = { (a, b) | a ∈ A, b ∈ B(a) }` - */ - val Sigma = DEF(A, B) --> The( - z, - ∀(t, in(t, z) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) - )(sigmaUniqueness) - - /** - * Theorem --- Sigma with Empty Set is the empty set - * - * The Sigma of the empty set with any function is the empty set. - * - * `Σ(∅, B) = ∅` - */ - val sigmaWithEmptySet = Theorem( - Sigma(∅, B) === ∅ - ) { - have(∀(t, in(t, Sigma(∅, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))))))) by InstantiateForall(Sigma(∅, B))( - Sigma.definition of (A -> ∅) - ) - val sigmaDef = thenHave(in(t, Sigma(∅, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a)))))) by InstantiateForall(t) - - val emptySetDef = have(in(t, ∅) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a)))))) subproof { - val lhs = have(in(t, ∅) |- (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a)))))) by Weakening( - emptySet.definition of (x -> t) - ) - - have(((t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))) |- in(t, ∅)) by Weakening(emptySet.definition of (x -> a)) - thenHave(∃(b, (t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))) |- in(t, ∅)) by LeftExists - val rhs = thenHave(∃(a, ∃(b, ((t === pair(a, b)) /\ in(a, ∅) /\ in(b, app(B, a))))) |- in(t, ∅)) by LeftExists - - have(thesis) by Tautology.from(lhs, rhs) - } - - have(in(t, Sigma(∅, B)) <=> in(t, ∅)) by Tautology.from(sigmaDef, emptySetDef) - val ext = thenHave(∀(t, in(t, Sigma(∅, B)) <=> in(t, ∅))) by RightForall - - have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> Sigma(∅, B), y -> ∅)) - } - - /** - * Theorem --- Pairs in Sigma - * - * Describes the pairs inside of a Sigma set. - * - * `(a, b) ∈ Σ(A, B) ⟹ a ∈ A ∧ b ∈ B(a)` - */ - val pairsInSigma = Theorem( - in(p, Sigma(A, B)) |- in(firstInPair(p), A) /\ in(secondInPair(p), app(B, firstInPair(p))) - ) { - val firstInSigma = Theorem( - in(p, Sigma(A, B)) |- in(firstInPair(p), A) - ) { - assume(in(p, Sigma(A, B))) - - have(∀(t, in(t, Sigma(A, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))))) by InstantiateForall(Sigma(A, B))( - Sigma.definition - ) - thenHave(in(p, Sigma(A, B)) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) by InstantiateForall(p) - thenHave(∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A)))) by Tautology - - val aw = witness(lastStep) - thenHave(∃(b, (p === pair(aw, b)) /\ in(aw, A))) by Restate - val bw = witness(lastStep) - val isPairAndInA = thenHave((p === pair(aw, bw)) /\ in(aw, A)) by Restate - val isPair = have(p === pair(aw, bw)) by Weakening(isPairAndInA) - val inA = have(in(aw, A)) by Weakening(isPairAndInA) - - val first = have(firstInPair(pair(aw, bw)) === aw) by Tautology.from(firstInPairReduction of (x := aw, y := bw)) - - have(in(firstInPair(pair(aw, bw)), A)) by Substitution.ApplyRules(first)(inA) - thenHave(thesis) by Substitution.ApplyRules(isPair) - } - - val secondInSigma = Theorem( - in(p, Sigma(A, B)) |- in(secondInPair(p), app(B, firstInPair(p))) - ) { - assume(in(p, Sigma(A, B))) - - have(∀(t, in(t, Sigma(A, B)) <=> (∃(a, ∃(b, (t === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))))) by InstantiateForall(Sigma(A, B))( - Sigma.definition - ) - thenHave(in(p, Sigma(A, B)) <=> (∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a)))))) by InstantiateForall(p) - thenHave(∃(a, ∃(b, (p === pair(a, b)) /\ in(a, A) /\ in(b, app(B, a))))) by Tautology - - val aw = witness(lastStep) - thenHave(∃(b, (p === pair(aw, b)) /\ in(aw, A) /\ in(b, app(B, aw)))) by Restate - val bw = witness(lastStep) - val isPairAndInAAndBInApp = thenHave((p === pair(aw, bw)) /\ in(aw, A) /\ in(bw, app(B, aw))) by Restate - val isPair = have(p === pair(aw, bw)) by Weakening(isPairAndInAAndBInApp) - val inA = have(in(aw, A)) by Weakening(isPairAndInAAndBInApp) - val BInApp = have(in(bw, app(B, aw))) by Weakening(isPairAndInAAndBInApp) - - val first = have(firstInPair(pair(aw, bw)) === aw) by Tautology.from(firstInPairReduction of (x := aw, y := bw)) - val second = have(secondInPair(pair(aw, bw)) === bw) by Tautology.from(secondInPairReduction of (x := aw, y := bw)) - - have(in(secondInPair(pair(aw, bw)), app(B, firstInPair(pair(aw, bw))))) by Substitution.ApplyRules(first, second)(BInApp) - thenHave(thesis) by Substitution.ApplyRules(isPair) - } - - have(thesis) by Tautology.from(firstInSigma, secondInSigma) - } - - val piUniqueness = Theorem( - ∃!(z, ∀(g, in(g, z) <=> (in(g, powerSet(Sigma(x, f))) /\ functionalOver(g, x)))) - ) { - have(thesis) by UniqueComprehension( - powerSet(Sigma(x, f)), - lambda(z, functionalOver(z, x)) - ) - } - - /** - * Dependent Product (Pi) -- The generalized function space. - * - * Given a set `x` and a function `f`, the dependent product `Π(x, f)` - * is the set of all functions `g` such that `g` is a in the powerset - * of `Σ(x, f)` - * - * `Π(x, f) = { g ∈ P(Σ(x, f)) | functionalOver(g, x) }` - */ - val Pi = DEF(x, f) --> The( - z, - ∀(g, in(g, z) <=> (in(g, powerSet(Sigma(x, f))) /\ functionalOver(g, x))) - )(piUniqueness) - - /** - * Theorem --- Pi with Empty Set is the Power Set of the Empty Set - * - * The Pi of the empty set with any function is the power set of the empty set. - * - * `Π(∅, f) = P(∅)` - */ - val piWithEmptySet = Theorem( - Pi(∅, f) === powerSet(∅) - ) { - have(∀(g, in(g, Pi(∅, f)) <=> (in(g, powerSet(Sigma(∅, f))) /\ functionalOver(g, ∅)))) by InstantiateForall(Pi(∅, f))( - Pi.definition of (x -> ∅) - ) - thenHave(∀(g, in(g, Pi(∅, f)) <=> (in(g, powerSet(∅)) /\ functionalOver(g, ∅)))) by Substitution.ApplyRules(sigmaWithEmptySet) - val piDef = thenHave(in(g, Pi(∅, f)) <=> (in(g, powerSet(∅)) /\ functionalOver(g, ∅))) by InstantiateForall(g) - - val lhs = have(in(g, Pi(∅, f)) ==> (in(g, powerSet(∅)))) by Weakening(piDef) - - val rhs = have(in(g, powerSet(∅)) ==> in(g, Pi(∅, f))) subproof { - val assumption = assume(in(g, powerSet(∅))) - - have(in(g, singleton(∅))) by Substitution.ApplyRules(powerSetEmptySet)(assumption) - val gIsEmpty = thenHave(g === ∅) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := g, x := ∅)) - - have(∅ === relationDomain(∅)) by Weakening(domainOfEmptySetIsEmpty) - val isDomain = thenHave(∅ === relationDomain(g)) by Substitution.ApplyRules(gIsEmpty) - - have(functional(∅)) by Weakening(emptySetFunctional) - val isFunctional = thenHave(functional(g)) by Substitution.ApplyRules(gIsEmpty) - - val isFunctionalOver = have(functionalOver(g, ∅)) by Tautology.from( - functionalOver.definition of (f := g, x := ∅), - isFunctional, - isDomain - ) - - val result = have(in(g, Pi(∅, f))) by Tautology.from(piDef, assumption, isFunctionalOver) - - have(thesis) by Tautology.from(assumption, result) - } - - have(in(g, Pi(∅, f)) <=> in(g, powerSet(∅))) by Tautology.from(lhs, rhs) - val ext = thenHave(∀(g, in(g, Pi(∅, f)) <=> in(g, powerSet(∅)))) by RightForall - - have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> Pi(∅, f), y -> powerSet(∅))) - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala deleted file mode 100644 index ec68adf10..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/functions/package.scala +++ /dev/null @@ -1,74 +0,0 @@ -package lisa.maths.settheory - -/** - * Set-theoretic functions library - * - * Develops the set-theoretic functions, their properties, and common theorems. - */ -package object functions { - export lisa.maths.settheory.functions.Functionals.{ - functional, - functionalOver, - functionalMembership, - violatingPairInFunction, - pairSingletonIsFunctional, - setOfFunctionsUniqueness, - setOfFunctions, - functionFrom, - functionFromImpliesFunctional, - functionApplicationUniqueness, - app, - pairInFunctionIsApp, - functionalOverApplication, - elemOfFunctional, - elemOfFunctionalOver, - elemOfFunctionFrom, - functionsEqualIfEqualOnDomain, - functionsSubsetIfEqualOnSubsetDomain, - restrictedFunctionUniqueness, - restrictedFunction, - restrictedFunctionPairMembership, - restrictedFunctionDomain, - restrictedFunctionIsFunctionalOver, - restrictedFunctionApplication, - restrictedFunctionCancellation, - restrictedFunctionAbsorption, - functionalOverImpliesDomain, - functionFromImpliesDomainEq, - functionImpliesRangeSubsetOfCodomain, - inRangeImpliesPullbackExists, - unionOfFunctionsIsAFunction, - unionOfFunctionsWithDisjointDomains, - unionOfFunctionSet, - domainOfFunctionalUnion, - |=>, - functionRange, - functionDomain, - emptySetFunctional, - inAppIsFunction, - functionFromApplication, - sigmaUniqueness, - Sigma, - sigmaWithEmptySet, - pairsInSigma, - piUniqueness, - Pi, - piWithEmptySet - } - export lisa.maths.settheory.functions.FunctionProperties.{ - surjective, - onto, - injective, - oneone, - bijective, - invertibleFunction, - inverseFunctionOf, - surjectiveImpliesRangeIsCodomain, - cantorTheorem, - constantFunction, - constantFunctionDomain, - constantFunctionIsFunctional, - constantFunctionFunctionFrom, - constantFunctionApplication - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala deleted file mode 100644 index 7a1be0cfa..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/InclusionOrders.scala +++ /dev/null @@ -1,207 +0,0 @@ -package lisa.maths.settheory.orderings - -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.Quantifiers.* -import lisa.maths.settheory.SetTheory.* -import lisa.maths.settheory.orderings.PartialOrders.* - -object InclusionOrders extends lisa.Main { - - // var defs - private val w = variable - private val x = variable - private val y = variable - private val z = variable - private val h = formulaVariable - private val t = variable - private val a = variable - private val b = variable - private val c = variable - private val d = variable - - // relation and function symbols - private val r = variable - private val p = variable - private val q = variable - private val f = variable - private val g = variable - private val F = function[1] - private val G = function[2] - - private val P = predicate[1] - private val Q = predicate[1] - private val schemPred = predicate[1] - - val inclusionOnUniqueness = Lemma( - () |- existsOne(z, forall(t, in(t, z) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) - ) { - have(thesis) by UniqueComprehension(cartesianProduct(a, a), lambda(t, exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))) - } - - /** - * The relation induced by inclusion on a set, noted `∈_a`. - * - * `∈_a = {(y, x) ∈ a * a | y ∈ x}` - */ - val inclusionOn = DEF(a) --> The(z, forall(t, in(t, z) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))))(inclusionOnUniqueness) - - /** - * The partial order `(a, ∈_a)` induced by the inclusion relation - * ([[inclusionOn]]) on a set. - */ - val inclusionOrderOn = DEF(a) --> pair(a, inclusionOn(a)) - - /** - * Theorem --- the inclusion order on a set is defined by the meta inclusion [[in]]. - */ - val inclusionOrderElem = Lemma( - () |- (in(b, a) /\ in(c, a) /\ in(b, c)) <=> in(pair(b, c), inclusionOn(a)) - ) { - val prodElem = have((in(b, a) /\ in(c, a)) <=> in(pair(b, c), cartesianProduct(a, a))) by Restate.from(pairInCartesianProduct of (a -> b, b -> c, x -> a, y -> a)) - - val exXY = have(in(b, c) <=> exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x))))) subproof { - val fwd = have(in(b, c) |- exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x))))) subproof { - have(in(b, c) |- in(b, c) /\ (pair(b, c) === pair(b, c))) by Restate - thenHave(in(b, c) |- exists(x, in(b, x) /\ (pair(b, c) === pair(b, x)))) by RightExists - thenHave(thesis) by RightExists - } - val bwd = have(exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x)))) |- in(b, c)) subproof { - val pairExt = have((pair(b, c) === pair(y, x)) |- (b === y) /\ (c === x)) by Weakening(pairExtensionality of (a -> b, b -> c, c -> y, d -> x)) - - have(in(y, x) |- in(y, x)) by Hypothesis - thenHave((in(y, x), b === y, c === x) |- in(b, c)) by Substitution.ApplyRules(b === y, c === x) - have((in(y, x) /\ (pair(b, c) === pair(y, x))) |- in(b, c)) by Tautology.from(pairExt, lastStep) - thenHave(exists(x, in(y, x) /\ (pair(b, c) === pair(y, x))) |- in(b, c)) by LeftExists - thenHave(thesis) by LeftExists - } - - have(thesis) by Tautology.from(fwd, bwd) - } - - have(forall(t, in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(a))(inclusionOn.definition) - thenHave(in(pair(b, c), inclusionOn(a)) <=> (in(pair(b, c), cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (pair(b, c) === pair(y, x)))))) by InstantiateForall(pair(b, c)) - - have(thesis) by Tautology.from(lastStep, prodElem, exXY) - } - - /** - * Theorem --- the inclusion order on the any set is a relation. - */ - val inclusionIsRelation = Theorem( - () |- relationBetween(inclusionOn(a), a, a) - ) { - have(forall(t, in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(a))(inclusionOn.definition) - thenHave(in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))) by InstantiateForall(t) - thenHave(in(t, inclusionOn(a)) ==> in(t, cartesianProduct(a, a))) by Weakening - thenHave(forall(t, in(t, inclusionOn(a)) ==> in(t, cartesianProduct(a, a)))) by RightForall - // thenHave(forall(z, in(z, inclusionOn(a)) ==> in(z, cartesianProduct(a, a)))) by Restate - val subs = thenHave(subset(inclusionOn(a), cartesianProduct(a, a))) by Substitution.ApplyRules(subsetAxiom of (x -> inclusionOn(a), y -> cartesianProduct(a, a))) - - have(thesis) by Tautology.from(subs, relationBetween.definition of (r -> inclusionOn(a), a -> a, b -> a)) - } - - val inclusionIsAntiReflexive = Theorem( - antiReflexive(inclusionOn(a), a) - ) { - sorry - } - - val inclusionIsAntiSymmetric = Theorem( - antiSymmetric(inclusionOn(a), a) - ) { - sorry - } - - /** - * Theorem --- the inclusion order on the empty set is the empty relation. - */ - val emptySetInclusionEmpty = Lemma( - () |- (inclusionOn(emptySet) === emptySet) - ) { - have(forall(t, in(t, inclusionOn(emptySet)) <=> (in(t, cartesianProduct(emptySet, emptySet)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall( - inclusionOn(emptySet) - )(inclusionOn.definition of (a -> emptySet)) - val incDef = thenHave(in(t, inclusionOn(emptySet)) <=> (in(t, cartesianProduct(emptySet, emptySet)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))) by InstantiateForall(t) - - have(forall(t, in(t, cartesianProduct(emptySet, emptySet)) <=> in(t, emptySet))) by Tautology.from( - productWithEmptySetEmpty of (x -> emptySet), - extensionalityAxiom of (x -> cartesianProduct(emptySet, emptySet), y -> emptySet) - ) - val emp = thenHave(in(t, cartesianProduct(emptySet, emptySet)) <=> in(t, emptySet)) by InstantiateForall(t) - - val impl = have(in(t, inclusionOn(emptySet)) <=> in(t, emptySet)) subproof { - val lhs = have(in(t, inclusionOn(emptySet)) |- in(t, emptySet)) by Tautology.from(incDef, emp) - val rhs = have(in(t, emptySet) |- in(t, inclusionOn(emptySet))) by Weakening(emptySet.definition of (x -> t)) - have(thesis) by Tautology.from(lhs, rhs) - } - - val ext = thenHave(forall(t, in(t, inclusionOn(emptySet)) <=> in(t, emptySet))) by RightForall - have(thesis) by Tautology.from(ext, extensionalityAxiom of (x -> inclusionOn(emptySet), y -> emptySet)) - } - - /** - * Theorem --- the inclusion order on the empty set is a reflexive relation. - */ - val emptyInclusionReflexive = Lemma( - () |- reflexive(inclusionOn(emptySet), emptySet) - ) { - have(reflexive(emptySet, emptySet)) by Restate.from(emptyRelationReflexiveOnItself) - thenHave(thesis) by Substitution.ApplyRules(emptySetInclusionEmpty) - } - - /** - * Theorem --- the inclusion order on the empty set is an irreflexive relation. - */ - val emptyInclusionIrreflexive = Lemma( - () |- irreflexive(inclusionOn(emptySet), a) - ) { - have(irreflexive(emptySet, a)) by Restate.from(emptyRelationIrreflexive) - thenHave(thesis) by Substitution.ApplyRules(emptySetInclusionEmpty) - } - - /** - * Theorem --- the inclusion order on the empty set is a transitive relation. - */ - val emptyInclusionTransitive = Lemma( - () |- transitive(inclusionOn(emptySet), a) - ) { - have(transitive(emptySet, a)) by Restate.from(emptyRelationTransitive) - thenHave(thesis) by Substitution.ApplyRules(emptySetInclusionEmpty) - } - - /** - * Theorem --- the empty relation partially orders the empty set - */ - val emptySetPartialOrder = Lemma( - () |- partialOrder(pair(emptySet, emptySet)) - ) { - have( - partialOrder(pair(emptySet, emptySet)) <=> (relationBetween(emptySet, emptySet, emptySet) /\ antiSymmetric(emptySet, emptySet) /\ antiReflexive( - emptySet, - emptySet - ) /\ transitive(emptySet, emptySet)) - ) by Substitution.ApplyRules(firstInPairReduction, secondInPairReduction)( - partialOrder.definition of p -> pair(emptySet, emptySet) - ) - have(thesis) by Tautology.from( - lastStep, - emptySetRelationOnItself, - emptyRelationIrreflexive of a -> emptySet, - emptyRelationTransitive of a -> emptySet, - emptyRelationAntiSymmetric of a -> emptySet - ) - } - - /** - * Theorem --- the empty relation totally orders the empty set - */ - val emptySetTotalOrder = Lemma( - () |- totalOrder(pair(emptySet, emptySet)) - ) { - have(totalOrder(pair(emptySet, emptySet)) <=> (partialOrder(pair(emptySet, emptySet)) /\ total(emptySet, emptySet))) by Substitution.ApplyRules( - firstInPairReduction of (x -> emptySet, y -> emptySet), - secondInPairReduction of (x -> emptySet, y -> emptySet) - )(totalOrder.definition of p -> pair(emptySet, emptySet)) - have(thesis) by Tautology.from(lastStep, emptySetPartialOrder, emptyRelationTotalOnItself) - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala deleted file mode 100644 index b1feffb59..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Induction.scala +++ /dev/null @@ -1,325 +0,0 @@ -package lisa.maths.settheory.orderings - -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.Quantifiers.* -import lisa.maths.settheory.SetTheory.* -import lisa.maths.settheory.orderings.InclusionOrders.* -import lisa.maths.settheory.orderings.PartialOrders.* -import lisa.maths.settheory.orderings.Segments.* -import lisa.maths.settheory.orderings.WellOrders.* - -import Ordinals.* - -object Induction extends lisa.Main { - - // var defs - private val a = variable - private val b = variable - private val p = variable - private val r = variable - private val t = variable - private val w = variable - private val x = variable - private val y = variable - private val z = variable - - // relation and function symbols - - private val P = predicate[1] - private val Q = predicate[1] - - /** - * Theorem --- Well Ordered Induction on a Subclass - * - * If `p` is a strict well-ordering, and `Q` is a subclass of the base set of - * `p`, called `A`, then - * - * `\forall x \in A. (A |^ x) \subseteq Q ==> x \in Q |- A = Q` - * - * i.e., if `Q` is a subclass of `A`, and the property `Q` passes to `x` from - * its initial segment, then `A` is `Q`. - */ - val wellOrderedInductionSubclass = Theorem( - { - val A = firstInPair(p) - ( - wellOrder(p), - forall(x, Q(x) ==> in(x, A)), - forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) - ) - |- forall(x, Q(x) <=> in(x, A)) - } - ) { - // renaming - val A = firstInPair(p) - val ` in(x, A)), - forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) - ) - - // assume, towards a contradiction - val contra = !forall(x, Q(x) <=> in(x, A)) - assume(contra) - - val contraDis = have(exists(x, (Q(x) /\ !in(x, A)) \/ (!Q(x) /\ in(x, A)))) by Restate - - val lhs = have(Q(x) /\ !in(x, A) |- ()) subproof { - have(Q(x) ==> in(x, A)) by InstantiateForall - thenHave(thesis) by Tautology - } - - val rhs = have(!Q(x) /\ in(x, A) |- ()) subproof { - val zDef = forall(t, in(t, z) <=> (in(t, A) /\ !Q(t))) - - // z exists by comprehension - val zExists = have(exists(z, zDef)) subproof { - have(existsOne(z, zDef)) by UniqueComprehension(A, lambda(t, !Q(t))) - have(thesis) by Cut(lastStep, existsOneImpliesExists of P -> lambda(z, zDef)) - } - - // z is a subset of A - val zSubset = have(zDef |- subset(z, A)) subproof { - have(zDef |- in(t, z) <=> (in(t, A) /\ !Q(t))) by InstantiateForall - thenHave(zDef |- in(t, z) ==> in(t, A)) by Weakening - thenHave(zDef |- forall(t, in(t, z) ==> in(t, A))) by RightForall - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> z, y -> A)) - } - - // there exists a least element y in z - val yDef = in(y, z) /\ forall(w, (!Q(w) /\ in(w, A)) ==> (in(pair(y, w), ` (in(x, A) /\ !Q(x))) by InstantiateForall - thenHave(in(x, z)) by Tautology - val zNonEmpty = have(!(z === emptySet)) by Tautology.from(lastStep, setWithElementNonEmpty of (y -> x, x -> z)) - - have(forall(b, (subset(b, A) /\ !(b === emptySet)) ==> exists(y, in(y, b) /\ forall(w, in(w, b) ==> (in(pair(y, w), ` exists(y, in(y, z) /\ forall(w, in(w, z) ==> (in(pair(y, w), ` (in(pair(y, w), ` (in(w, A) /\ !Q(w))) by InstantiateForall - thenHave(in(w, z) ==> (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` (in(pair(y, w), ` Q(w))) subproof { - assume(zDef, yDef) - - // TODO: assumptions annoy instantiations of external imports, so this is done rather verbosely here - // see https://github.com/epfl-lara/lisa/issues/161 - have(forall(z, (z === initialSegment(p, y)) <=> forall(t, in(t, z) <=> (in(t, A) /\ in(pair(t, y), ` y)) - thenHave(forall(t, in(t, initialSegment(p, y)) <=> (in(t, A) /\ in(pair(t, y), ` (in(w, A) /\ in(pair(w, y), ` (in(pair(y, w), ` (in(pair(y, w), ` !in(pair(y, y), ` ` A)) - } - - thenHave(in(w, A) ==> !in(pair(w, w), ` (y === w)))) subproof { - have(antiSymmetric(` ` A)) - } - - thenHave((in(pair(y, w), ` (y === w)) by InstantiateForall(y, w) - have(thesis) by Tautology.from(lastStep, yw, rhs) - } - - have(thesis) by Tautology.from(lhs, rhs, cases) - } - - have(in(w, initialSegment(p, y)) ==> Q(w)) by Tautology.from(lastStep, wInInit) - thenHave(thesis) by RightForall - } - - // but if the initial segment of y is a subset of Q, then y is in Q - val yInQ = have((zDef, yDef, in(y, A)) |- Q(y)) subproof { - have(in(y, A) ==> (forall(w, in(w, initialSegment(p, y)) ==> Q(w)) ==> Q(y))) by InstantiateForall - thenHave((in(y, A), (forall(w, in(w, initialSegment(p, y)) ==> Q(w)))) |- Q(y)) by Restate - have(thesis) by Cut(yInitInQ, lastStep) - } - - // however, we know y is in z, so !Q(y), hence contradiction - have((zDef, yDef) |- ()) subproof { - assume(zDef, yDef) - val ynotQ = have(in(y, z) <=> (in(y, A) /\ !Q(y))) by InstantiateForall - have(in(y, z)) by Restate - have(thesis) by Tautology.from(lastStep, ynotQ, yInQ) - } - - thenHave((zDef, exists(y, yDef)) |- ()) by LeftExists - have((zDef, !Q(x) /\ in(x, A)) |- ()) by Cut(yExists, lastStep) - thenHave((exists(z, zDef), !Q(x) /\ in(x, A)) |- ()) by LeftExists - have(thesis) by Cut(zExists, lastStep) - } - - have(((Q(x) /\ !in(x, A)) \/ (!Q(x) /\ in(x, A))) |- ()) by Tautology.from(lhs, rhs) - thenHave(exists(x, (Q(x) /\ !in(x, A)) \/ (!Q(x) /\ in(x, A))) |- ()) by LeftExists - - have(thesis) by Tautology.from(lastStep, contraDis) - } - - /** - * Theorem --- Well Ordered Induction - * - * If `p` is a strict well-ordering, `Q` is a class, and `A` the base set of - * `p`, then - * - * `∀ x ∈ A. (A |^ x) ⊆ Q ==> x ∈ Q |- ∀ x ∈ A. x ∈ Q` - * - * i.e., if the property `Q` passes to `x` from its initial segment, then `Q` - * holds for every element of `A`. - */ - val wellOrderedInduction = Theorem( - { - val A = firstInPair(p) - ( - wellOrder(p), - forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) - ) - |- forall(x, in(x, A) ==> Q(x)) - } - ) { - val A = firstInPair(p) - val ` (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) - ) - - // make a subclass out of Q by intersecting with A - def prop(x: Term): Formula = Q(x) /\ in(x, A) - - have(prop(x) ==> in(x, A)) by Restate - val subclassProp = thenHave(forall(x, prop(x) ==> in(x, A))) by Restate - - have(forall(x, in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x)))) subproof { - have(in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) by InstantiateForall - val fy = thenHave(in(x, A) |- (forall(y, in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x))) by Restate - // have(forall(y, in(y, initialSegment(p, x)) ==> Q(y)) |- (in(y, initialSegment(p, x)) ==> Q(y))) by InstantiateForall - // val inst = have(in(x, A) |- (in(y, initialSegment(p, x)) ==> Q(y)) ==> Q(x)) by Tautology.from(lastStep, fy) - - have(in(y, initialSegment(p, x)) |- in(y, A)) subproof { - have(forall(z, (z === initialSegment(p, x)) <=> forall(t, in(t, z) <=> (in(t, A) /\ in(pair(t, x), ` x)) - thenHave(forall(t, in(t, initialSegment(p, x)) <=> (in(t, A) /\ in(pair(t, x), ` (in(y, A) /\ in(pair(y, x), ` Q(y)) <=> (in(y, initialSegment(p, x)) ==> prop(y))) by Tautology.from(lastStep) - thenHave(forall(y, (in(y, initialSegment(p, x)) ==> Q(y)) <=> (in(y, initialSegment(p, x)) ==> prop(y)))) by RightForall - have(forall(y, in(y, initialSegment(p, x)) ==> Q(y)) <=> forall(y, in(y, initialSegment(p, x)) ==> prop(y))) by Cut( - lastStep, - universalEquivalenceDistribution of (P -> lambda(y, in(y, initialSegment(p, x)) ==> Q(y)), Q -> lambda(y, in(y, initialSegment(p, x)) ==> prop(y))) - ) - - have(in(x, A) |- forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x)) by Tautology.from(lastStep, fy) - thenHave(in(x, A) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x))) by Restate - thenHave(thesis) by RightForall - } - - have(forall(x, in(x, A) <=> prop(x))) by Tautology.from(lastStep, subclassProp, wellOrderedInductionSubclass of Q -> lambda(x, prop(x))) - thenHave(in(x, A) <=> prop(x)) by InstantiateForall(x) - thenHave(in(x, A) ==> Q(x)) by Tautology - thenHave(thesis) by RightForall - } - - val transfiniteInduction = Theorem( - forall(x, ordinal(x) ==> (forall(y, in(y, x) ==> Q(y)) ==> Q(x))) |- forall(x, ordinal(x) ==> Q(x)) - ) { - - assume(forall(x, ordinal(x) ==> (forall(y, in(y, x) ==> Q(y)) ==> Q(x)))) - assume(exists(x, ordinal(x) /\ !Q(x))) // negated conclusion - - // we assume the negated conjecture and derive a contradiction - - // prop := On \ Q - def prop(x: Term) = ordinal(x) /\ !Q(x) - - // there is a minimal element in prop - val yDef = prop(y) /\ forall(x, prop(x) ==> in(y, x)) - - have(prop(x) ==> ordinal(x)) by Restate - thenHave(forall(x, prop(x) ==> ordinal(x))) - val yExists = have(exists(y, yDef)) by Tautology.from(lastStep, ordinalSubclassHasMinimalElement of (P -> lambda(x, prop(x)))) - - // so everything less than y is not in prop - val fz = have(yDef |- forall(z, in(z, y) ==> !prop(z))) subproof { - assume(yDef) - - // assume z \in y - // but \forall x. prop(x) ==> y \in x - // so prop(z) ==> y \in z - have(forall(x, prop(x) ==> in(y, x))) by Restate - thenHave(prop(z) ==> in(y, z)) by InstantiateForall(z) - - // but inclusion is anti symmetric (regularity) - have(in(z, y) |- !prop(z)) by Tautology.from(lastStep, inclusionAntiSymmetric of (x -> z, y -> y)) - thenHave(in(z, y) ==> !prop(z)) by Restate - thenHave(thesis) by RightForall - } - - // but by assumption, this must mean Q(y) - have(yDef |- Q(y)) subproof { - assume(yDef) - have(forall(z, in(z, y) ==> !prop(z))) by Restate.from(fz) - thenHave(in(z, y) ==> !prop(z)) by InstantiateForall(z) - have(in(z, y) ==> Q(z)) by Tautology.from(lastStep, elementsOfOrdinalsAreOrdinals of (b -> z, a -> y)) - val zy = thenHave(forall(z, in(z, y) ==> Q(z))) by RightForall - have(ordinal(y) ==> (forall(z, in(z, y) ==> Q(z)) ==> Q(y))) by InstantiateForall - have(thesis) by Tautology.from(zy, lastStep) - } - - // contradiction - thenHave(yDef |- ()) by Tautology - thenHave(exists(y, yDef) |- ()) by LeftExists - have(() |- ()) by Cut(yExists, lastStep) - thenHave(thesis) by Restate - } - - val minimalOrdinalCounterexample = Theorem( - exists(x, ordinal(x) /\ !Q(x)) |- exists(x, ordinal(x) /\ !Q(x) /\ forall(y, in(y, x) ==> Q(y))) - ) { - have(thesis) by Restate.from(transfiniteInduction) - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala deleted file mode 100644 index 618d1fa26..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Orderings.scala +++ /dev/null @@ -1,5 +0,0 @@ -package lisa.maths.settheory.orderings - -object Orderings { - // export everything in this package -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala deleted file mode 100644 index 5dd1ed309..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Ordinals.scala +++ /dev/null @@ -1,338 +0,0 @@ -package lisa.maths.settheory.orderings - -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.Quantifiers.* -import lisa.maths.settheory.SetTheory.* -import lisa.maths.settheory.orderings.InclusionOrders.* -import lisa.maths.settheory.orderings.PartialOrders.* -import lisa.maths.settheory.orderings.WellOrders.* - -object Ordinals extends lisa.Main { - - // var defs - private val w = variable - private val x = variable - private val y = variable - private val z = variable - private val h = formulaVariable - private val t = variable - private val a = variable - private val b = variable - private val c = variable - private val d = variable - - // relation and function symbols - private val r = variable - private val p = variable - private val q = variable - private val f = variable - private val g = variable - private val F = function[1] - private val G = function[2] - - private val P = predicate[1] - private val Q = predicate[1] - private val schemPred = predicate[1] - - /** - * A set is an ordinal iff it is transitive ([[transitiveSet]]) and - * well-ordered ([[wellOrder]]) by inclusion. - * - * Since inclusion is not precisely a relation in the sense of set theory, the - * well-ordered clause is explicitly written. - */ - val ordinal = DEF(a) --> transitiveSet(a) /\ wellOrder(inclusionOrderOn(a)) - - /** - * Defining properties of the [[ordinal]] class - * - * - the [[emptySet]] is an ordinal --- [[emptySetOrdinal]] - * - if `a` is an ordinal and `b ∈ a`, then `b` is an ordinal --- [[ordinalInclusionClosure]] - * - if `a`, `b` are ordinals and `b ⊂ a`, then `b ∈ a` --- [[ordinalSubsetClosure]] - * - if `a` and `b` are distinct ordinals, then either `a ⊂ b` or `b ⊂ a` --- [[ordinalSOMETHING]] TODO: - * - * Other properties - * - * - the ordinals form a proper class --- [[noSetOfOrdinals]] - * - every subclass of the ordinals has a minimal element --- [[ordinalSubclassHasMinimalElement]] - */ - - /** - * Theorem --- the empty set is transitive. - */ - val emptySetTransitive = Lemma( - () |- transitiveSet(emptySet) - ) { - val hypo = have(!in(y, emptySet) |- in(y, emptySet) ==> subset(y, emptySet)) by Restate - have(() |- in(y, emptySet) ==> subset(y, emptySet)) by Cut(emptySetAxiom of (x -> y), hypo) - thenHave(() |- forall(y, in(y, emptySet) ==> subset(y, emptySet))) by RightForall - thenHave(thesis) by Substitution.ApplyRules(transitiveSet.definition) - } - - /** - * Theorem --- the empty set is well ordered by inclusion. - */ - val emptySetWellOrderedByInclusion = Lemma( - () |- wellOrder(inclusionOrderOn(emptySet)) - ) { - val incDef = have(inclusionOrderOn(emptySet) === pair(emptySet, inclusionOn(emptySet))) by InstantiateForall(inclusionOrderOn(emptySet))(inclusionOrderOn.definition of a -> emptySet) - have(wellOrder(pair(emptySet, inclusionOn(emptySet)))) by Substitution.ApplyRules(emptySetInclusionEmpty)(emptySetWellOrder) - thenHave(thesis) by Substitution.ApplyRules(incDef) - } - - /** - * Theorem --- the empty set is an ordinal (zero). - */ - val emptySetOrdinal = Theorem( - () |- ordinal(emptySet) - ) { - have(thesis) by Tautology.from(emptySetWellOrderedByInclusion, emptySetTransitive, ordinal.definition of (a -> emptySet)) - } - - val ordinalsHereditarilyTransitive = Lemma( - ordinal(a) |- transitiveSet(a) /\ forall(b, in(b, a) ==> transitiveSet(b)) - ) { - val ordinalTrans = have(ordinal(a) |- transitiveSet(a)) by Weakening(ordinal.definition) - val wellOrdInca = have(ordinal(a) |- wellOrder(inclusionOrderOn(a))) by Weakening(ordinal.definition) - have(inclusionOrderOn(a) === pair(a, inclusionOn(a))) by InstantiateForall(inclusionOrderOn(a))(inclusionOrderOn.definition) - val wellOrda = have(ordinal(a) |- wellOrder(pair(a, inclusionOn(a)))) by Substitution.ApplyRules(lastStep)(wellOrdInca) - - have(transitiveSet(a) |- forall(b, in(b, a) ==> subset(b, a))) by Weakening(transitiveSet.definition of x -> a) - val bIna = thenHave((transitiveSet(a), in(b, a)) |- subset(b, a)) by InstantiateForall(b) - have((transitiveSet(a), in(b, a)) |- forall(z, in(z, b) ==> in(z, a))) by Tautology.from(lastStep, subsetAxiom of (x -> b, y -> a)) - thenHave((transitiveSet(a), in(b, a)) |- in(z, b) ==> in(z, a)) by InstantiateForall(z) - val bcz = have((transitiveSet(a), in(b, a), in(z, b), in(c, z)) |- in(b, a) /\ in(c, a) /\ in(z, a)) by Tautology.from(lastStep, lastStep of (z -> c, b -> z)) - - val cInb = have((in(b, a), in(z, b), in(c, z), in(c, a), in(z, a), wellOrder(pair(a, inclusionOn(a)))) |- in(c, b)) subproof { - val bz = have(in(b, a) /\ in(z, a) /\ in(z, b) |- in(pair(z, b), inclusionOn(a))) by Weakening(inclusionOrderElem of (b -> z, c -> b)) - val zc = have(in(z, a) /\ in(c, a) /\ in(c, z) |- in(pair(c, z), inclusionOn(a))) by Weakening(inclusionOrderElem of (c -> z, b -> c)) - val bc = have(in(pair(c, b), inclusionOn(a)) |- in(b, a) /\ in(c, a) /\ in(c, b)) by Weakening(inclusionOrderElem of (c -> b, b -> c)) - - have(wellOrder(pair(a, inclusionOn(a))) |- forall(w, forall(y, forall(z, (in(pair(w, y), inclusionOn(a)) /\ in(pair(y, z), inclusionOn(a))) ==> in(pair(w, z), inclusionOn(a)))))) by Substitution - .ApplyRules(secondInPairReduction of (x -> a, y -> inclusionOn(a)))(wellOrderTransitivity of p -> pair(a, inclusionOn(a))) - thenHave(wellOrder(pair(a, inclusionOn(a))) |- forall(y, forall(z, (in(pair(c, y), inclusionOn(a)) /\ in(pair(y, z), inclusionOn(a))) ==> in(pair(c, z), inclusionOn(a))))) by InstantiateForall( - c - ) - thenHave(wellOrder(pair(a, inclusionOn(a))) |- forall(w, (in(pair(c, z), inclusionOn(a)) /\ in(pair(z, w), inclusionOn(a))) ==> in(pair(c, w), inclusionOn(a)))) by InstantiateForall(z) - thenHave(wellOrder(pair(a, inclusionOn(a))) |- (in(pair(c, z), inclusionOn(a)) /\ in(pair(z, b), inclusionOn(a))) ==> in(pair(c, b), inclusionOn(a))) by InstantiateForall(b) - - have(thesis) by Tautology.from(lastStep, bz, zc, bc) - } - - have((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a), in(z, b), in(c, z)) |- in(c, b)) by Tautology.from(bcz, cInb) - thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- (in(c, z) /\ in(z, b)) ==> in(c, b)) by Restate - thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- forall(z, (in(c, z) /\ in(z, b)) ==> in(c, b))) by RightForall - thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- forall(c, forall(z, (in(c, z) /\ in(z, b)) ==> in(c, b)))) by RightForall - thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a))), in(b, a)) |- transitiveSet(b)) by Substitution.ApplyRules(transitiveSetInclusionDef of x -> b) - thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a)))) |- in(b, a) ==> transitiveSet(b)) by Restate - thenHave((transitiveSet(a), wellOrder(pair(a, inclusionOn(a)))) |- forall(b, in(b, a) ==> transitiveSet(b))) by RightForall - - have(thesis) by Tautology.from(lastStep, wellOrda, ordinalTrans) - } - - val elementsOfOrdinalsAreOrdinals = Theorem( - (ordinal(a), in(b, a)) |- ordinal(b) - ) { - assume(ordinal(a)) - assume(in(b, a)) - - // transitive :: - val transitiveB = have(transitiveSet(b)) subproof { - have(forall(b, in(b, a) ==> transitiveSet(b))) by Weakening(ordinalsHereditarilyTransitive) - thenHave(thesis) by InstantiateForall(b) - } - - // and well ordered by inclusion :: - - // what defines \in_b as a subset of \in_a? - // one direction (a ==> b) is sufficient here - val incAToB = have(in(y, b) /\ in(z, b) /\ in(pair(z, y), inclusionOn(a)) |- in(pair(z, y), inclusionOn(b))) subproof { - assume(in(y, b)) - assume(in(z, b)) - assume(in(pair(z, y), inclusionOn(a))) - - // instantiating definition of inclusion (a bit painful with assumes) - have(forall(z, (z === inclusionOn(a)) <=> forall(t, in(t, z) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))))) by Weakening(inclusionOn.definition) - thenHave(forall(t, in(t, inclusionOn(a)) <=> (in(t, cartesianProduct(a, a)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(a)) - val incDefA = - thenHave(in(pair(z, y), inclusionOn(a)) <=> (in(pair(z, y), cartesianProduct(a, a)) /\ exists(d, exists(c, in(d, c) /\ (pair(z, y) === pair(d, c)))))) by InstantiateForall(pair(z, y)) - have(forall(z, (z === inclusionOn(b)) <=> forall(t, in(t, z) <=> (in(t, cartesianProduct(b, b)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x)))))))) by Weakening( - inclusionOn.definition of a -> b - ) - thenHave(forall(t, in(t, inclusionOn(b)) <=> (in(t, cartesianProduct(b, b)) /\ exists(y, exists(x, in(y, x) /\ (t === pair(y, x))))))) by InstantiateForall(inclusionOn(b)) - val incDefB = - thenHave(in(pair(z, y), inclusionOn(b)) <=> (in(pair(z, y), cartesianProduct(b, b)) /\ exists(d, exists(c, in(d, c) /\ (pair(z, y) === pair(d, c)))))) by InstantiateForall(pair(z, y)) - - have(in(pair(z, y), cartesianProduct(b, b))) by Tautology.from(pairInCartesianProduct of (a -> z, b -> y, x -> b, y -> b)) - have(thesis) by Tautology.from(lastStep, incDefA, incDefB) - } - - val totalB = have(totalOrder(inclusionOrderOn(b))) subproof { - // the totality of \in_b follows from the totality of \in_a and the fact that \in_b does not exclude any elements of b - val totA = have(totalOrder(inclusionOrderOn(a))) by Tautology.from(ordinal.definition, wellOrder.definition of p -> inclusionOrderOn(a)) - - val totalDef = have(totalOrder(p) <=> (partialOrder(p) /\ total(secondInPair(p), firstInPair(p)))) by Weakening(totalOrder.definition) - - // \in_b is a partial order - val inBPartial = have(partialOrder(inclusionOrderOn(b))) by Tautology.from(inclusionOnTransitiveSetIsPartialOrder of a -> b, transitiveB) - - // \in_b is total as a homogeneous relation on b - val inBTotal = have(total(secondInPair(inclusionOrderOn(b)), firstInPair(inclusionOrderOn(b)))) subproof { - val totB = have(total(inclusionOn(b), b)) subproof { - have(forall(z, (z === inclusionOrderOn(a)) <=> (z === pair(a, inclusionOn(a))))) by Weakening(inclusionOrderOn.definition) - val incEq = thenHave(inclusionOrderOn(a) === pair(a, inclusionOn(a))) by InstantiateForall(inclusionOrderOn(a)) - have(total(secondInPair(inclusionOrderOn(a)), firstInPair(inclusionOrderOn(a)))) by Tautology.from(totalDef of p -> inclusionOrderOn(a), totA) - thenHave(total(secondInPair(pair(a, inclusionOn(a))), firstInPair(pair(a, inclusionOn(a))))) by Substitution.ApplyRules(incEq) - val totIncA = - thenHave(total(inclusionOn(a), a)) by Substitution.ApplyRules(secondInPairReduction of (x -> a, y -> inclusionOn(a)), firstInPairReduction of (x -> a, y -> inclusionOn(a))) - - val totRelDef = - have(total(r, x) <=> (relationBetween(r, x, x) /\ ∀(y, ∀(z, (in(y, x) /\ in(z, x)) ==> (in(pair(y, z), r) \/ in(pair(z, y), r) \/ (y === z)))))) by Weakening(total.definition) - - // need to show - // \forall y, z \in b. y \in_b z \/ z \in_b y \/ (z = y) - // y, z \in b ==> y, z \in a - // y, z \in a ==> y \in_a z \/ z \in_a y \/ (z = y) - // but each of these imply a literal above - // done - have(total(inclusionOn(a), a) |- (in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z))) subproof { - assume(total(inclusionOn(a), a)) - assume(in(y, b)) - assume(in(z, b)) - - have(forall(y, in(y, a) ==> subset(y, a))) by Tautology.from(ordinal.definition, transitiveSet.definition of x -> a) - thenHave(in(b, a) ==> subset(b, a)) by InstantiateForall(b) - have(forall(x, in(x, b) ==> in(x, a))) by Tautology.from(lastStep, subsetAxiom of (x -> b, y -> a)) - thenHave(in(x, b) ==> in(x, a)) by InstantiateForall(x) - val yza = have(in(y, a) /\ in(z, a)) by Tautology.from(lastStep of x -> y, lastStep of x -> z) - - have(forall(y, forall(z, (in(y, a) /\ in(z, a)) ==> (in(pair(y, z), inclusionOn(a)) \/ in(pair(z, y), inclusionOn(a)) \/ (y === z))))) by Tautology.from( - totRelDef of (r -> inclusionOn(a), x -> a) - ) - thenHave((in(y, a) /\ in(z, a)) ==> (in(pair(y, z), inclusionOn(a)) \/ in(pair(z, y), inclusionOn(a)) \/ (y === z))) by InstantiateForall(y, z) - have((in(pair(y, z), inclusionOn(a)) \/ in(pair(z, y), inclusionOn(a)) \/ (y === z))) by Tautology.from(lastStep, yza) - - have(thesis) by Tautology.from(lastStep, incAToB, incAToB of (y -> z, z -> y)) - } - - have((in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z))) by Cut(totIncA, lastStep) - thenHave(forall(z, (in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z)))) by RightForall - thenHave(forall(y, forall(z, (in(y, b) /\ in(z, b)) ==> (in(pair(y, z), inclusionOn(b)) \/ in(pair(z, y), inclusionOn(b)) \/ (y === z))))) by RightForall - - have(thesis) by Tautology.from(lastStep, inclusionIsRelation of a -> b, totRelDef of (r -> inclusionOn(b), x -> b)) - } - - have(forall(z, (z === inclusionOrderOn(b)) <=> (z === pair(b, inclusionOn(b))))) by Weakening(inclusionOrderOn.definition of a -> b) - val incEq = thenHave(inclusionOrderOn(b) === pair(b, inclusionOn(b))) by InstantiateForall(inclusionOrderOn(b)) - - have(secondInPair(pair(b, inclusionOn(b))) === inclusionOn(b)) by Weakening(secondInPairReduction of (x -> b, y -> inclusionOn(b))) - val snd = thenHave(secondInPair(inclusionOrderOn(b)) === inclusionOn(b)) by Substitution.ApplyRules(incEq) - have(firstInPair(pair(b, inclusionOn(b))) === (b)) by Weakening(firstInPairReduction of (x -> b, y -> inclusionOn(b))) - val fst = thenHave(firstInPair(inclusionOrderOn(b)) === (b)) by Substitution.ApplyRules(incEq) - - have(thesis) by Substitution.ApplyRules(snd, fst)(totB) - } - - have(totalOrder(inclusionOrderOn(b)) <=> (partialOrder(inclusionOrderOn(b)) /\ total(secondInPair(inclusionOrderOn(b)), firstInPair(inclusionOrderOn(b))))) by Weakening( - totalDef of p -> inclusionOrderOn(b) - ) - have(thesis) by Tautology.from(lastStep, inBPartial, inBTotal) - } - - val woProp = have(forall(c, (subset(c, b) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))))) subproof { - // painful expansion - // subset c b ==> subset c a - have(forall(y, in(y, a) ==> subset(y, a))) by Tautology.from(ordinal.definition, transitiveSet.definition of x -> a) - thenHave(in(b, a) ==> subset(b, a)) by InstantiateForall(b) - thenHave(subset(b, a)) by Restate - have(subset(c, b) |- subset(c, a)) by Tautology.from(lastStep, subsetTransitivity of (a -> c, c -> a)) - val bToA = thenHave(subset(c, b) /\ !(c === emptySet) |- subset(c, a) /\ !(c === emptySet)) by Tautology - - have(forall(z, (z === inclusionOrderOn(a)) <=> (z === pair(a, inclusionOn(a))))) by Weakening(inclusionOrderOn.definition) - val incDef = thenHave(inclusionOrderOn(a) === pair(a, inclusionOn(a))) by InstantiateForall(inclusionOrderOn(a)) - - // so there exists a minimal element wrt a - have( - forall( - c, - (subset(c, firstInPair(inclusionOrderOn(a))) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(inclusionOrderOn(a))) \/ (z === y)))) - ) - ) by Tautology.from(ordinal.definition, wellOrder.definition of p -> inclusionOrderOn(a)) - thenHave( - forall( - c, - (subset(c, firstInPair(pair(a, inclusionOn(a)))) /\ !(c === emptySet)) ==> exists( - z, - in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(pair(a, inclusionOn(a)))) \/ (z === y))) - ) - ) - ) by Substitution.ApplyRules(incDef) - thenHave(forall(c, (subset(c, a) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y)))))) by Substitution.ApplyRules( - firstInPairReduction of (x -> a, y -> inclusionOn(a)), - secondInPairReduction of (x -> a, y -> inclusionOn(a)) - ) - val caWO = thenHave((subset(c, a) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))))) by InstantiateForall(c) - - // but if this element is minimal wrt \in_a, it is minimal wrt \in_b as well - have( - (subset(c, b), exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))))) |- exists( - z, - in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y))) - ) - ) subproof { - assume(subset(c, b)) - val subCB = have(forall(x, in(x, c) ==> in(x, b))) by Tautology.from(subsetAxiom of (x -> c, y -> b)) - val yb = have(in(y, c) ==> in(y, b)) by InstantiateForall(y)(subCB) - val zb = have(in(z, c) ==> in(z, b)) by InstantiateForall(z)(subCB) - - have(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y)))) by Restate - thenHave(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) by InstantiateForall(y) - have(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y))) by Tautology.from( - lastStep, - incAToB, - yb, - zb - ) - thenHave(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))) by RightForall - thenHave(in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))) by Tautology - thenHave( - in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(a)) \/ (z === y))) |- exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y)))) - ) by RightExists - thenHave(thesis) by LeftExists - } - - have((subset(c, b) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), inclusionOn(b)) \/ (z === y))))) by Tautology.from(lastStep, caWO, bToA) - thenHave(thesis) by RightForall - } - - val wo = have(wellOrder(inclusionOrderOn(b))) subproof { - have(forall(z, (z === inclusionOrderOn(b)) <=> (z === pair(b, inclusionOn(b))))) by Weakening(inclusionOrderOn.definition of a -> b) - val incDef = thenHave(inclusionOrderOn(b) === pair(b, inclusionOn(b))) by InstantiateForall(inclusionOrderOn(b)) - - have( - forall( - c, - (subset(c, firstInPair(pair(b, inclusionOn(b)))) /\ !(c === emptySet)) ==> exists( - z, - in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(pair(b, inclusionOn(b)))) \/ (z === y))) - ) - ) - ) by Substitution.ApplyRules(firstInPairReduction of (x -> b, y -> inclusionOn(b)), secondInPairReduction of (x -> b, y -> inclusionOn(b)))(woProp) - thenHave( - forall( - c, - (subset(c, firstInPair(inclusionOrderOn(b))) /\ !(c === emptySet)) ==> exists(z, in(z, c) /\ forall(y, in(y, c) ==> (in(pair(z, y), secondInPair(inclusionOrderOn(b))) \/ (z === y)))) - ) - ) by Substitution.ApplyRules(incDef) - have(thesis) by Tautology.from(lastStep, totalB, wellOrder.definition of p -> inclusionOrderOn(b)) - } - - have(thesis) by Tautology.from(wo, transitiveB, ordinal.definition of (a -> b)) - } - - val ordinalSubclassHasMinimalElement = Lemma( - forall(x, P(x) ==> ordinal(x)) /\ exists(x, P(x)) |- exists(y, P(y) /\ ordinal(y) /\ forall(x, P(x) ==> in(y, x))) - ) { - sorry - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala deleted file mode 100644 index eb45f1d5f..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/PartialOrders.scala +++ /dev/null @@ -1,356 +0,0 @@ -package lisa.maths.settheory.orderings - -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.Quantifiers.* -import lisa.maths.settheory.SetTheory.* -import lisa.maths.settheory.functions.* - -object PartialOrders extends lisa.Main { - - // var defs - private val w = variable - private val x = variable - private val y = variable - private val z = variable - private val h = formulaVariable - private val t = variable - private val a = variable - private val b = variable - private val c = variable - private val d = variable - - // relation and function symbols - private val r = variable - private val p = variable - private val q = variable - private val f = variable - private val g = variable - private val F = function[1] - private val G = function[2] - - private val P = predicate[1] - private val Q = predicate[1] - private val schemPred = predicate[1] - - /** - * Linear and Partial Ordering - */ - - /** - * (Strict) Partial Order --- `p` is a partial order on `x` if it is a pair `(x, r)`, - * and `r` is an [[antiReflexive]], [[antiSymmetric]], and [[transitive]] binary - * [[relation]] on `x`. - */ - val partialOrder = - DEF(p) --> relationBetween(secondInPair(p), firstInPair(p), firstInPair(p)) /\ antiSymmetric(secondInPair(p), firstInPair(p)) /\ antiReflexive(secondInPair(p), firstInPair(p)) /\ transitive( - secondInPair(p), - firstInPair(p) - ) - - /** - * Linear Order --- a partial order `p = (r, x)` is called a linear order if - * `r` is [[total]] as a [[relation]] on `x`. - */ - val totalOrder = DEF(p) --> partialOrder(p) /\ total(secondInPair(p), firstInPair(p)) - - /** - * Properties of elements under partial orders - */ - - /** - * Maximal Element --- `a` is a maximal element of `y` with respect to `r`, - * where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. - * - * `∀ b ∈ y. ! a r b` - */ - val maximalElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (!in(pair(a, b), secondInPair(p)))) - - /** - * Minimal Element --- `a` is a minimal element of `y` with respect to `r`, - * where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. - * - * `∀ b ∈ y. ! b r a` - */ - val minimalElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (!in(pair(b, a), secondInPair(p)))) - - /** - * Greatest Element --- `a` is the greatest element of `y` with respect to - * `r`, where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. - * - * `∀ b ∈ y. b r a ⋁ b = a` - */ - val greatestElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (in(pair(b, a), secondInPair(p)) \/ (a === b))) - - /** - * Least Element --- `a` is the least element of `y` with respect to `r`, - * where `p = (r, x)` is a partial order on `x`, and `y ⊆ x`. - * - * `∀ b ∈ y. a r b ⋁ b = a` - */ - val leastElement = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ in(a, y) /\ ∀(b, in(b, y) ==> (in(pair(a, b), secondInPair(p)) \/ (a === b))) - - /** - * Upper Bound --- `a` is an upper bound on `y` with respect to `r`, where `p - * = (r, x)` is a partial order on `x`, and `y ⊆ x`. - * - * `∀ b ∈ y. b r a ⋁ b = a` - * - * Note that as opposed to the greatest element, `a` is not enforced to be an - * element of `y`. - */ - val upperBound = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ ∀(b, in(b, y) ==> (in(pair(b, a), secondInPair(p)) \/ (a === b))) - - /** - * Lower Bound --- `a` is a lower bound on `y` with respect to `r`, where `p = - * (r, x)` is a partial order on `x`, and `y ⊆ x`. - * - * `∀ b ∈ y. a r b ⋁ b = a` - * - * Note that as opposed to the least element, `a` is not enforced to be an - * element of `y` - */ - val lowerBound = DEF(a, y, p) --> partialOrder(p) /\ subset(y, firstInPair(p)) /\ ∀(b, in(b, y) ==> (in(pair(a, b), secondInPair(p)) \/ (a === b))) - - val setOfLowerBoundsUniqueness = Theorem( - () |- ∃!(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ lowerBound(t, y, p)))) - ) { - have(thesis) by UniqueComprehension(secondInPair(p), lambda(t, lowerBound(t, y, p))) - } - - /** - * The set of all lower bounds of a set `y` under a partial order `p`. Used to define [[greatestLowerBound]] - */ - val setOfLowerBounds = DEF(y, p) --> The(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ lowerBound(t, y, p))))(setOfLowerBoundsUniqueness) - - /** - * Greatest Lower Bound --- `a` is the greatest lower bound on `y ⊆ x` - * under a partial order `p = (r, x)` if it is the greatest element in the - * [[setOfLowerBounds]] of `y` under `p`. - */ - val greatestLowerBound = DEF(a, y, p) --> greatestElement(a, setOfLowerBounds(y, p), p) - - /** - * Alias for [[greatestLowerBound]] - */ - val infimum = greatestLowerBound - - val setOfUpperBoundsUniqueness = Theorem( - () |- ∃!(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ upperBound(t, y, p)))) - ) { - have(thesis) by UniqueComprehension(secondInPair(p), lambda(t, upperBound(t, y, p))) - } - - /** - * The set of all upper bounds of a set `y` under a partial order `p`. Used to define [[leastUpperBound]] - */ - val setOfUpperBounds = DEF(y, p) --> The(z, ∀(t, in(t, z) <=> (in(t, secondInPair(p)) /\ upperBound(t, y, p))))(setOfUpperBoundsUniqueness) - - /** - * Least Upper Bound --- `a` is the least upper bound on `y ⊆ x` under - * a partial order `p = (r, x)` if it is the least element in the - * [[setOfUpperBounds]] of `y` under `p`. - */ - val greatestUpperBound = DEF(a, y, p) --> leastElement(a, setOfUpperBounds(y, p), p) - - /** - * Alias for [[greatestUpperBound]] - */ - val supremum = greatestUpperBound - - val predecessor = DEF(p, x, y) --> totalOrder(p) /\ in(x, firstInPair(p)) /\ in(y, firstInPair(p)) /\ in(pair(x, y), secondInPair(p)) /\ forall( - z, - !(in(pair(x, z), secondInPair(p)) /\ in(pair(z, y), secondInPair(p))) - ) - - val limitElement = DEF(p, x) --> totalOrder(p) /\ in(x, firstInPair(p)) /\ !exists(y, predecessor(p, y, x)) - - val successorElement = DEF(p, x) --> totalOrder(p) /\ in(x, firstInPair(p)) /\ exists(y, predecessor(p, y, x)) - - val everyElemInTotalOrderLimitOrSuccessor = Lemma( - totalOrder(p) /\ in(x, firstInPair(p)) |- (limitElement(p, x) \/ successorElement(p, x)) - ) { - // limit and successor are just negation of each other - have(thesis) by Tautology.from(successorElement.definition, limitElement.definition) - } - - val initialSegmentUnionForLimitElementsIsComplete = Lemma( - totalOrder(p) /\ limitElement(p, x) |- in(pair(t, x), secondInPair(p)) <=> exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p))) - ) { - assume(totalOrder(p)) - assume(limitElement(p, x)) - - val p1 = firstInPair(p) - val p2 = secondInPair(p) - - val fwd = have(in(pair(t, x), p2) |- exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2))) subproof { - assume(in(pair(t, x), p2)) - assume(forall(y, !(in(pair(t, y), p2) /\ in(pair(y, x), p2)))) // assume negated - - have(forall(y, !predecessor(p, y, x))) by Tautology.from(limitElement.definition) - thenHave(!predecessor(p, t, x)) by InstantiateForall(t) - val notInp1 = have(!in(t, p1)) by Tautology.from(lastStep, limitElement.definition, predecessor.definition of (x -> t, y -> x)) // y is free here, so instantiate it to x - - val inst = have(!(in(pair(t, y), p2) /\ in(pair(y, x), p2))) by InstantiateForall - - have(in(t, p1)) subproof { - have(relationBetween(p2, p1, p1)) by Tautology.from(totalOrder.definition, partialOrder.definition) - have(subset(p2, cartesianProduct(p1, p1))) by Tautology.from(lastStep, relationBetween.definition of (r -> p2, a -> p1, b -> p1)) - have(forall(z, in(z, p2) ==> in(z, cartesianProduct(p1, p1)))) by Tautology.from(lastStep, subsetAxiom of (x -> p2, y -> cartesianProduct(p1, p1))) - thenHave(in(pair(t, x), cartesianProduct(p1, p1))) by InstantiateForall(pair(t, x)) - have(in(t, p1)) by Tautology.from(lastStep, pairInCartesianProduct of (a -> t, b -> x, x -> p1, y -> p1)) - } - - have(bot) by Tautology.from(lastStep, notInp1) - } - - val bwd = have(exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)) |- in(pair(t, x), p2)) subproof { - have(in(pair(t, y), p2) /\ in(pair(y, x), p2) |- in(pair(t, x), p2)) subproof { - // total orders are transitive - have(forall(t, forall(y, forall(x, (in(pair(t, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(t, x), p2))))) by Tautology.from( - totalOrder.definition, - partialOrder.definition, - transitive.definition of (r -> p2, x -> p1) - ) - thenHave(thesis) by InstantiateForall(t, y, x) - } - - thenHave(thesis) by LeftExists - } - - have(thesis) by Tautology.from(fwd, bwd) - } - show - - val initialSegmentUnionForSuccessorElementsIsIncomplete = Lemma( - totalOrder(p) /\ successorElement(p, x) |- in(pair(t, x), secondInPair(p)) <=> (predecessor(p, t, x) \/ exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p)))) - ) { - assume(totalOrder(p)) - assume(successorElement(p, x)) - - val p1 = firstInPair(p) - val p2 = secondInPair(p) - - val fwd = have(in(pair(t, x), p2) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)))) subproof { - assume(in(pair(t, x), p2)) - - // t < x means t, x \in p1 - val txInp1 = have(in(t, p1) /\ in(x, p1)) subproof { - have(relationBetween(p2, p1, p1)) by Tautology.from(totalOrder.definition, partialOrder.definition) - have(subset(p2, cartesianProduct(p1, p1))) by Tautology.from(lastStep, relationBetween.definition of (r -> p2, a -> p1, b -> p1)) - have(forall(z, in(z, p2) ==> in(z, cartesianProduct(p1, p1)))) by Tautology.from(lastStep, subsetAxiom of (x -> p2, y -> cartesianProduct(p1, p1))) - thenHave(in(pair(t, x), cartesianProduct(p1, p1))) by InstantiateForall(pair(t, x)) - have(thesis) by Tautology.from(lastStep, pairInCartesianProduct of (a -> t, b -> x, x -> p1, y -> p1)) - } - - have(predecessor(p, y, x) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p))))) subproof { - assume(predecessor(p, y, x)) - - have(forall(z, !(in(pair(y, z), p2) /\ in(pair(z, x), p2)))) by Tautology.from(predecessor.definition of (x -> y, y -> x)) - thenHave(!(in(pair(y, t), p2) /\ in(pair(t, x), p2))) by InstantiateForall(t) - val yNLTt = thenHave(!in(pair(y, t), p2)) by Tautology - - have(forall(y, forall(t, (in(y, p1) /\ in(t, p1)) ==> (in(pair(y, t), p2) \/ in(pair(t, y), p2) \/ (y === t))))) by Tautology.from( - totalOrder.definition, - total.definition of (r -> p2, x -> p1) - ) - thenHave((in(y, p1) /\ in(t, p1)) ==> (in(pair(y, t), p2) \/ in(pair(t, y), p2) \/ (y === t))) by InstantiateForall(y, t) - val cases = have(in(pair(t, y), p2) \/ (y === t)) by Tautology.from(lastStep, predecessor.definition of (x -> y, y -> x), txInp1, yNLTt) - - val ltCase = have(in(pair(t, y), p2) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)))) subproof { - have(in(pair(t, y), p2) |- in(pair(t, y), p2) /\ in(pair(y, x), p2)) by Tautology.from(predecessor.definition of (x -> y, y -> x)) - thenHave(in(pair(t, y), p2) |- exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2))) by RightExists - thenHave(thesis) by Weakening - } - - val eqCase = have((y === t) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)))) subproof { - have(predecessor(p, y, x)) by Restate - thenHave((y === t) |- predecessor(p, t, x)) by Substitution.ApplyRules(y === t) - thenHave(thesis) by Weakening - } - - have(thesis) by Tautology.from(cases, ltCase, eqCase) - } - - thenHave(exists(y, predecessor(p, y, x)) |- (predecessor(p, t, x) \/ exists(y, in(pair(t, y), secondInPair(p)) /\ in(pair(y, x), secondInPair(p))))) by LeftExists - have(thesis) by Tautology.from(lastStep, successorElement.definition) - } - - val bwd = have((predecessor(p, t, x) \/ exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2))) |- in(pair(t, x), p2)) subproof { - val predCase = have(predecessor(p, t, x) |- in(pair(t, x), p2)) by Tautology.from(predecessor.definition of (x -> t, y -> x)) - have(in(pair(t, y), p2) /\ in(pair(y, x), p2) |- in(pair(t, x), p2)) subproof { - // transitivity of p - have(forall(t, forall(y, forall(x, (in(pair(t, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(t, x), p2))))) by Tautology.from( - totalOrder.definition, - partialOrder.definition, - transitive.definition of (r -> p2, x -> p1) - ) - thenHave(thesis) by InstantiateForall(t, y, x) - } - thenHave(exists(y, in(pair(t, y), p2) /\ in(pair(y, x), p2)) |- in(pair(t, x), p2)) by LeftExists - - have(thesis) by LeftOr(lastStep, predCase) - } - - have(thesis) by Tautology.from(fwd, bwd) - } - show - - /** - * Properties of functions under partial orders - */ - - /** - * Order Preserving Function --- a function `f` between `P` and `Q` such that - * `p = (P, <_p)` and `q = (Q, <_q)` are partially ordered is order-preserving - * if - * - * `∀ x y. x <_p y ⟹ f(x) <_q f(y)` - */ - val orderPreserving = DEF(f, p, q) --> partialOrder(p) /\ partialOrder(q) /\ functionFrom(f, firstInPair(p), firstInPair(q)) /\ ∀( - x, - ∀(y, in(pair(x, y), secondInPair(p)) ==> in(pair(app(f, x), app(f, y)), secondInPair(q))) - ) - - /** - * Increasing Function --- an order preserving function ([[orderPreserving]]) - * between two partially ordered sets is increasing if the two sets are - * linearly ordered ([[totalOrder]]). - */ - val increasing = DEF(f, p, q) --> totalOrder(p) /\ totalOrder(q) /\ orderPreserving(f, p, q) - - /** - * Isomorphism of Partially Ordered Sets --- a function `f` is an isomorphism - * between two partially ordered sets `p = (P, <_p)` and `q = (Q, <_q)` if it - * is an [[injective]] function from `P` to `Q`, and both `f` and `f^-1` are - * [[orderPreserving]]. - */ - // val isomorphismOfPartialOrders = DEF (f, p, q) --> injective(f, firstInPair(p), firstInPair(q)) /\ orderPreserving(f, p, q) /\ orderPreserving(inverseFunction(f), p, q) - - private val pA = variable // order - private val pB = variable // order - val orderIsomorphism = DEF(pA, pB, f) --> { - val A = firstInPair(pA) - val B = firstInPair(pB) - val ` ∀( - y, - in(y, A) ==> - (in(pair(x, y), ` in(pair(app(f, x), app(f, y)), ` (app(g, a) === F(orderedRestriction(g, a, p))))) - def prop(t: Term): Formula = in(t, p1) /\ existsOne(g, fun(g, t)) - - // Lemmas: - - /** - * Theorem --- Unique Recursive Function - * - * If a theorem as defined by [[wellOrderedRecursion]] exists, it is unique - */ - val uniqueRecursiveFunction = Lemma( - wellOrder(p) /\ exists(g, fun(g, t)) /\ in(t, p1) |- existsOne(g, fun(g, t)) - ) { - assume(wellOrder(p)) - assume(in(t, p1)) - - // pt is a well order over t, which is needed for induction - val pt = pair(initialSegment(p, t), initialSegmentOrder(p, t)) - val ptWO = have(wellOrder(pt)) by Weakening(initialSegmentWellOrdered of a -> t) - - // suppose there exist two such distinct functions g1 and g2 - val g1 = variable - val g2 = variable - - // expansion of ordered restriction - val ordResDef = have(orderedRestriction(g, z, p) === restrictedFunction(g, initialSegment(p, z))) subproof { - have(forall(b, (b === orderedRestriction(g, z, p)) <=> (b === restrictedFunction(g, initialSegment(p, z))))) by Weakening(orderedRestriction.definition of (f -> g, a -> z)) - thenHave(thesis) by InstantiateForall(orderedRestriction(g, z, p)) - } - - // if g1 and g2 agree on the initial segment of an element < z, they must agree on z - val initToz = have( - fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) /\ in(z, initialSegment(p, t)) /\ forall( - b, - in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b)) - ) |- (app(g1, z) === app(g2, z)) - ) subproof { - assume(fun(g1, t)) - assume(fun(g2, t)) - assume(!(g1 === g2)) - assume(in(z, initialSegment(p, t))) - assume(forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b)))) - - // the ordered restriction of g1 has domain initialSegment(p, z) - // it is functional, too - val restrictionIsFunction = have(fun(g, t) |- functionalOver(orderedRestriction(g, z, p), initialSegment(p, z))) subproof { - assume(fun(g, t)) - - // g_z has dom g, x -> initialSegment(p, z)), - functionalOver.definition of (f -> g, x -> initialSegment(p, t)) - ) - thenHave(thesis) by Substitution.ApplyRules(ordResDef) - } - - // but dom g is g, x -> initialSegment(p, t))) - - have(thesis) by Substitution.ApplyRules(lastStep)(domrestriction) - } - - // forall(b, in(b, z) <=> (in(b, p1) /\ in(pair(b, t), p2))))) by Weakening(initialSegment.definition of a -> t) - thenHave(forall(b, in(b, initialSegment(p, t)) <=> (in(b, p1) /\ in(pair(b, t), p2)))) by InstantiateForall(initialSegment(p, t)) - thenHave(in(z, initialSegment(p, t)) <=> (in(z, p1) /\ in(pair(z, t), p2))) by InstantiateForall(z) - val zLTt = thenHave(in(pair(z, t), p2)) by Tautology - - have(partialOrder(p)) by Tautology.from(wellOrder.definition, totalOrder.definition) - - have(thesis) by Tautology.from(lastStep, zLTt, initialSegmentsSubset of (x -> z, y -> t), pIsAPartialOrder) - } - - // so dom g = initialSegment(p, z), y -> initialSegment(p, t)) - ) - - have(thesis) by Substitution.ApplyRules(lastStep)(domgz) - } - - // the double initial segment is redundant - val initPTEqual = have(initialSegment(pt, z) === initialSegment(p, z)) subproof { - - // expand defs - have(forall(z, (z === initialSegment(x, y)) <=> forall(t, in(t, z) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x)))))) by Weakening( - initialSegment.definition of (p -> x, a -> y) - ) - thenHave(forall(t, in(t, initialSegment(x, y)) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x))))) by InstantiateForall(initialSegment(x, y)) - val initXY = thenHave(in(c, initialSegment(x, y)) <=> (in(c, firstInPair(x)) /\ in(pair(c, y), secondInPair(x)))) by InstantiateForall(c) - - // forward - val fwd = have(in(b, initialSegment(pt, z)) |- in(b, initialSegment(p, z))) subproof { - assume(in(b, initialSegment(pt, z))) - - have(in(b, firstInPair(pt))) by Tautology.from(initXY of (x -> pt, y -> z, c -> b)) - val bpt = thenHave(in(b, initialSegment(p, t))) by Substitution.ApplyRules(firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) - have(in(b, initialSegment(p, t)) ==> in(b, p1)) by Tautology.from(initXY of (x -> p, y -> t, c -> b)) - val bInP1 = have(in(b, p1)) by Tautology.from(lastStep, bpt) - - val bzInP2 = have(in(pair(b, z), p2)) subproof { - have(in(z, initialSegment(p, t))) by Restate - val zt = have(in(pair(z, t), p2)) by Tautology.from(lastStep, initXY of (x -> p, y -> t, c -> z)) - - have(in(pair(b, z), secondInPair(pt))) by Tautology.from(initXY of (x -> pt, y -> z, c -> b)) - val bzpt = thenHave(in(pair(b, z), initialSegmentOrder(p, t))) by Substitution.ApplyRules(secondInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) - - have(thesis) subproof { - have( - forall( - z, - (z === initialSegmentOrder(p, t)) <=> forall(a, in(a, z) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) - ) - ) by Weakening(initialSegmentOrder.definition of a -> t) - thenHave( - forall(a, in(a, initialSegmentOrder(p, t)) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) - ) by InstantiateForall(initialSegmentOrder(p, t)) - thenHave( - in(pair(b, z), initialSegmentOrder(p, t)) <=> (in(pair(b, z), secondInPair(p)) /\ (in(firstInPair(pair(b, z)), initialSegment(p, t)) /\ in( - secondInPair(pair(b, z)), - initialSegment(p, t) - ))) - ) by InstantiateForall(pair(b, z)) - have(thesis) by Tautology.from(lastStep, bzpt) - } - } - - have(thesis) by Tautology.from(bInP1, bzInP2, initXY of (x -> p, y -> z, c -> b)) - } - - // backward - val bwd = have(in(b, initialSegment(p, z)) |- in(b, initialSegment(pt, z))) subproof { - assume(in(b, initialSegment(p, z))) - - val bpt = have(in(b, initialSegment(p, t))) subproof { - val bInP1 = have(in(b, p1)) by Tautology.from(initXY of (x -> p, y -> z, c -> b)) - - val bz = have(in(pair(b, z), p2)) by Tautology.from(initXY of (x -> p, y -> z, c -> b)) - val zt = have(in(pair(z, t), p2)) by Tautology.from(initXY of (x -> p, y -> t, c -> z)) - - have(forall(w, forall(y, forall(z, (in(pair(w, y), p2) /\ in(pair(y, z), p2)) ==> in(pair(w, z), p2))))) by Weakening(wellOrderTransitivity) - thenHave((in(pair(b, z), p2) /\ in(pair(z, t), p2)) ==> in(pair(b, t), p2)) by InstantiateForall(b, z, t) - - have(in(pair(b, t), p2)) by Tautology.from(lastStep, bz, zt) - have(thesis) by Tautology.from(lastStep, bInP1, initXY of (x -> p, y -> t, c -> b)) - } - - val bzInP2 = have(in(pair(b, z), initialSegmentOrder(p, t))) subproof { - have( - forall( - z, - (z === initialSegmentOrder(p, t)) <=> forall(a, in(a, z) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) - ) - ) by Weakening(initialSegmentOrder.definition of a -> t) - thenHave( - forall(a, in(a, initialSegmentOrder(p, t)) <=> (in(a, secondInPair(p)) /\ (in(firstInPair(a), initialSegment(p, t)) /\ in(secondInPair(a), initialSegment(p, t))))) - ) by InstantiateForall(initialSegmentOrder(p, t)) - thenHave( - in(pair(b, z), initialSegmentOrder(p, t)) <=> (in(pair(b, z), secondInPair(p)) /\ (in(firstInPair(pair(b, z)), initialSegment(p, t)) /\ in( - secondInPair(pair(b, z)), - initialSegment(p, t) - ))) - ) by InstantiateForall(pair(b, z)) - val ordDef = thenHave(in(pair(b, z), initialSegmentOrder(p, t)) <=> (in(pair(b, z), secondInPair(p)) /\ (in(b, initialSegment(p, t)) /\ in(z, initialSegment(p, t))))) by Substitution - .ApplyRules(firstInPairReduction of (x -> b, y -> z), secondInPairReduction of (x -> b, y -> z)) - - val bz = have(in(pair(b, z), p2)) by Tautology.from(initXY of (x -> p, y -> z, c -> b)) - have(thesis) by Tautology.from(ordDef, bz, bpt) - } - - have(in(b, initialSegment(pt, z)) <=> (in(b, initialSegment(p, t)) /\ in(pair(b, z), initialSegmentOrder(p, t)))) by Substitution.ApplyRules( - firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t)), - secondInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t)) - )(initXY of (x -> pt, y -> z, c -> b)) - have(thesis) by Tautology.from(lastStep, bpt, bzInP2) - } - - // combine - have(in(b, initialSegment(p, z)) <=> in(b, initialSegment(pt, z))) by Tautology.from(fwd, bwd) - thenHave(forall(b, in(b, initialSegment(p, z)) <=> in(b, initialSegment(pt, z)))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> initialSegment(p, z), y -> initialSegment(pt, z))) - } - - // on the restricted domain, app(orderedRestriction(g, z, p), b) = app(g, b) - val ordApp = have(forall(b, in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g, z, p), b) === app(g, b)))) subproof { - // b < z ==> g_z(b) = g(b) - val bToApp = have(in(b, initialSegment(p, z)) ==> (app(orderedRestriction(g, z, p), b) === app(g, b))) subproof { - have(in(b, initialSegment(p, z)) ==> (app(restrictedFunction(g, initialSegment(p, z)), b) === app(g, b))) by Tautology.from( - restrictedFunctionApplication of (f -> g, x -> initialSegment(p, z), y -> b) - ) - thenHave(thesis) by Substitution.ApplyRules(ordResDef) - } - - // b <_t z ==> b < z - val btTobz = have(in(b, initialSegment(pt, z)) ==> in(b, initialSegment(p, z))) subproof { - have(in(b, initialSegment(pt, z)) ==> in(b, initialSegment(pt, z))) by Restate - thenHave(thesis) by Substitution.ApplyRules(initPTEqual) - } - - // so b <_t z ==> g_z(b) = g(b) - have(in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g, z, p), b) === app(g, b))) by Tautology.from(bToApp, btTobz) - - // quantify - thenHave(thesis) by RightForall - } - - // for every element in the restricted domain, g1_z(b) = g2_z(b) - val eqOnDom = have(forall(b, in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g1, z, p), b) === app(orderedRestriction(g2, z, p), b)))) subproof { - val unquantified = have(in(b, initialSegment(pt, z)) |- (app(orderedRestriction(g1, z, p), b) === app(orderedRestriction(g2, z, p), b))) subproof { - assume(in(b, initialSegment(pt, z))) - - val instOrd = have((app(orderedRestriction(g, z, p), b) === app(g, b))) by InstantiateForall(b)(ordApp) - - val eqTg2zg1 = equalityTransitivity of (x -> app(orderedRestriction(g2, z, p), b), z -> app(orderedRestriction(g1, z, p), b), y -> app(g1, b)) - val eqTg1g2 = equalityTransitivity of (x -> app(orderedRestriction(g2, z, p), b), y -> app(g2, b), z -> app(g1, b)) - - have(in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) by InstantiateForall - thenHave(app(g1, b) === app(g2, b)) by Tautology - have(thesis) by Tautology.from(lastStep, instOrd of g -> g1, instOrd of g -> g2, eqTg2zg1, eqTg1g2) - } - - thenHave(in(b, initialSegment(pt, z)) ==> (app(orderedRestriction(g1, z, p), b) === app(orderedRestriction(g2, z, p), b))) by Restate - thenHave(thesis) by RightForall - } - - // but then g1_z = g2_z - val orderedRestrictionsEqual = have(orderedRestriction(g1, z, p) === orderedRestriction(g2, z, p)) subproof { - have(fun(g, t) |- functionalOver(orderedRestriction(g, z, p), initialSegment(p, z))) by Restate.from(restrictionIsFunction) - - // but initialSegment pt z = initialSegment p z - val fung = thenHave(fun(g, t) |- functionalOver(orderedRestriction(g, z, p), initialSegment(pt, z))) by Substitution.ApplyRules(initPTEqual) - - have(thesis) by Tautology.from( - fung of g -> g1, - fung of g -> g2, - eqOnDom, - functionsEqualIfEqualOnDomain of (f -> orderedRestriction(g1, z, p), g -> orderedRestriction(g2, z, p), a -> initialSegment(pt, z)) - ) - } - - // and thus F(g1_z) = F(g2_z) - val fg1g2eq = have(F(orderedRestriction(g1, z, p)) === F(orderedRestriction(g2, z, p))) subproof { - have(F(orderedRestriction(g1, z, p)) === F(orderedRestriction(g1, z, p))) by Restate - thenHave(thesis) by Substitution.ApplyRules(orderedRestrictionsEqual) - } - - // but then app(g1, z) = F (g1_z) = F(g1_z) = app(g2, z) - have(thesis) subproof { - val gzf = have(fun(g, t) |- app(g, z) === F(orderedRestriction(g, z, p))) subproof { - assume(fun(g, t)) - have(forall(a, in(a, initialSegment(p, t)) ==> (app(g, a) === F(orderedRestriction(g, a, p))))) by Restate - thenHave(in(z, initialSegment(p, t)) ==> (app(g, z) === F(orderedRestriction(g, z, p)))) by InstantiateForall(z) - thenHave(thesis) by Tautology - } - - // g1(z) = F(g1_z) - val g1f = gzf of g -> g1 - - // g2(z) = F(g2_z) - val g2f = gzf of g -> g2 - - // F(g1_z) = F(g2_z) - // fg1g2eq - - val fg1fg2Tog1 = equalityTransitivity of (x -> F(orderedRestriction(g1, z, p)), y -> F(orderedRestriction(g2, z, p)), z -> app(g2, z)) - val g2fg2Tog1 = equalityTransitivity of (x -> app(g2, z), y -> F(orderedRestriction(g1, z, p)), z -> app(g1, z)) - - // g1(z) = g2(z) - have(thesis) by Tautology.from(fg1fg2Tog1, g2fg2Tog1, g1f, g2f, fg1g2eq) - } - } - - // thus, they must agree on the whole domain - val eqZ = have(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) |- forall(z, in(z, initialSegment(p, t)) ==> (app(g1, z) === app(g2, z)))) subproof { - assume(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2)) - have(in(z, initialSegment(p, t)) |- forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z))) by Weakening( - initToz - ) - thenHave( - in(z, firstInPair(pt)) |- forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z)) - ) by Substitution.ApplyRules(firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) - thenHave( - in(z, firstInPair(pt)) ==> (forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z))) - ) by Restate - thenHave( - forall(z, in(z, firstInPair(pt)) ==> (forall(b, in(b, initialSegment(pt, z)) ==> (app(g1, b) === app(g2, b))) ==> (app(g1, z) === app(g2, z)))) - ) by RightForall - have( - forall(z, in(z, firstInPair(pt)) ==> (app(g1, z) === app(g2, z))) - ) by Tautology.from(lastStep, ptWO, wellOrderedInduction of (p -> pt, Q -> lambda(x, app(g1, x) === app(g2, x)))) - thenHave(thesis) by Substitution.ApplyRules(firstInPairReduction of (x -> initialSegment(p, t), y -> initialSegmentOrder(p, t))) - } - - // so g1 = g2, but this is a contradiction - val contra = have(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) |- ()) subproof { - assume(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2)) - have((g1 === g2)) by Tautology.from(eqZ, functionsEqualIfEqualOnDomain of (f -> g1, g -> g2, a -> initialSegment(p, t))) - thenHave(thesis) by Restate - } - - // so there exists a unique one, if there exists one at all - have(!exists(g, fun(g, t)) \/ existsOne(g, fun(g, t))) subproof { - have(fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2) |- ()) by Restate.from(contra) - thenHave(exists(g2, fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2)) |- ()) by LeftExists - thenHave(exists(g1, exists(g2, fun(g1, t) /\ fun(g2, t) /\ !(g1 === g2))) |- ()) by LeftExists - have(thesis) by Tautology.from(lastStep, atleastTwoExist of (P -> lambda(x, fun(x, t)))) - } - - thenHave(thesis) by Restate - } - - // EXISTENCE ---------------------------------------- - - // if there exists a unique function `g` for the initial segment of some `x`, get the set of these - val wDef = forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) - - // take its union - // this is a function `g` for `x` (almost) - val uw = union(w) // + `(predecessor x, F(U w))` in the successor case - - // properties of w / uw - - val elemsFunctional = Lemma( - wDef |- - forall(t, in(t, w) ==> functional(t)) - ) { - assume(wDef) - have(in(t, w) |- functional(t)) subproof { - assume(in(t, w)) - have(in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) by InstantiateForall - val exy = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) by Tautology - - have(exists(y, in(y, initialSegment(p, x)) /\ fun(t, y)) |- functional(t)) subproof { - have(fun(t, y) |- functional(t)) by Tautology.from(functionalOver.definition of (f -> t, x -> initialSegment(p, y))) - thenHave((in(y, initialSegment(p, x)) /\ fun(t, y)) |- functional(t)) by Weakening - thenHave(thesis) by LeftExists - } - - have(thesis) by Cut(exy, lastStep) - } - thenHave(in(t, w) ==> functional(t)) by Restate - thenHave(thesis) by RightForall - } - - val elemsSubset = Lemma( - wellOrder(p) /\ wDef |- - forall(t1, forall(t2, (in(t1, w) /\ in(t2, w)) ==> (subset(t1, t2) \/ subset(t2, t1)))) - ) { - assume(wDef, wellOrder(p)) - - have(in(t1, w) /\ in(t2, w) |- subset(t1, t2) \/ subset(t2, t1)) subproof { - assume(in(t1, w)) - assume(in(t2, w)) - - // given t1 and t2 - // they must come from y1 and y2 - - // if t1 == t2 - // done - val t1EQt2 = have((t1 === t2) |- subset(t1, t2) \/ subset(t2, t1)) by Weakening(subsetEqualitySymmetry of (x -> t1, y -> t2)) - - // if t1 != t2 - val t1NEQt2 = have(!(t1 === t2) |- subset(t1, t2) \/ subset(t2, t1)) subproof { - assume(!(t1 === t2)) - def ytDef(y: Term, t: Term) = in(y, initialSegment(p, x)) /\ fun(t, y) - val y1 = variable - val y2 = variable - - val initMemToP1 = have(in(y, initialSegment(p, a)) |- in(y, p1)) subproof { - have(forall(y, in(y, initialSegment(p, a)) <=> (in(y, p1) /\ in(pair(y, a), p2)))) by InstantiateForall(initialSegment(p, a))(initialSegment.definition) - thenHave(in(y, initialSegment(p, a)) <=> (in(y, p1) /\ in(pair(y, a), p2))) by InstantiateForall(y) - thenHave(thesis) by Tautology - } - - have(ytDef(y1, t1) /\ ytDef(y2, t2) |- subset(t1, t2) \/ subset(t2, t1)) subproof { - assume(ytDef(y1, t1)) - assume(ytDef(y2, t2)) - // cases: - // y1 == y2 - // done by the uniqueness lemma above - val yeq = have((y1 === y2) |- subset(t1, t2)) subproof { - assume(y1 === y2) - have(fun(t1, y1) /\ fun(t2, y2)) by Restate - thenHave(fun(t1, y1) /\ fun(t2, y1)) by Substitution.ApplyRules(y1 === y2) - thenHave(fun(t1, y1) /\ fun(t2, y1) /\ !(t1 === t2)) by Tautology - thenHave(exists(t2, fun(t1, y1) /\ fun(t2, y1) /\ !(t1 === t2))) by RightExists - thenHave(exists(t1, exists(t2, fun(t1, y1) /\ fun(t2, y1) /\ !(t1 === t2)))) by RightExists - have(exists(t1, fun(t1, y1)) /\ !existsOne(t1, fun(t1, y1))) by Tautology.from(lastStep, atleastTwoExist of P -> lambda(t1, fun(t1, y1))) - have(bot) by Tautology.from(lastStep, uniqueRecursiveFunction of t -> y1, initMemToP1 of (y -> y1, a -> x)) - thenHave(thesis) by Weakening - } - - // y1 != y2 - // real work to be done here :- - val neq = have(!(y1 === y2) |- subset(t1, t2) \/ subset(t2, t1)) subproof { - assume(!(y1 === y2)) - - // y1 < y2 or y2 < y1? - // we prove it in the generic case - val a1 = variable - val a2 = variable - val k1 = variable - val k2 = variable - val ltToSubset = have(ytDef(a1, k1) /\ ytDef(a2, k2) /\ in(pair(a1, a2), p2) |- subset(k1, k2)) subproof { - assume(ytDef(a1, k1)) - assume(ytDef(a2, k2)) - assume(in(pair(a1, a2), p2)) - // fun(k1, a1) - // fun(k2, a2) - // a1 < a2 - // we should have k1 \subseteq k2 - - // dom k1 \subseteq dom k2 - val domSubset = - have(subset(initialSegment(p, a1), initialSegment(p, a2))) by Tautology.from(initialSegmentsSubset of (x -> a1, y -> a2), pIsAPartialOrder) - - // suppose there is a minimal n such that k1 n != k2 n - val n = variable - val nDef = - in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall(b, (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b))) - - // if k1 and k2 disagree at all - val k1k2disagree = exists(n, in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n))) - - val nExists = have(k1k2disagree |- exists(n, nDef)) subproof { - assume(k1k2disagree) - - // B defined by x => x < a1 /\ k1 x != k2 x exists - val B = variable - val BDef = forall(x, in(x, B) <=> (in(x, initialSegment(p, a1)) /\ !(app(k1, x) === app(k2, x)))) - val BExists = have(exists(B, BDef)) by Weakening(comprehensionSchema of (z -> initialSegment(p, a1), φ -> lambda(x, !(app(k1, x) === app(k2, x))))) - - // B forms a subset of p1 - val subsetB = have(BDef |- subset(B, p1)) subproof { - assume(BDef) - have(in(y, B) <=> (in(y, initialSegment(p, a1)) /\ !(app(k1, y) === app(k2, y)))) by InstantiateForall - have(in(y, B) ==> in(y, p1)) by Tautology.from(lastStep, initMemToP1 of a -> a1) - thenHave(forall(y, in(y, B) ==> in(y, p1))) by RightForall - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x -> B, y -> p1)) - } - - // B is non-empty - val nonEmptyB = have(BDef |- !(B === emptySet)) subproof { - assume(BDef) - have(in(n, B) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)))) by InstantiateForall - thenHave((in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n))) |- in(n, B)) by Weakening - have((in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n))) |- !(B === emptySet)) by Cut(lastStep, setWithElementNonEmpty of (y -> n, x -> B)) - thenHave(thesis) by LeftExists - } - - // so it has a minimal element - val minimalB = have(BDef |- exists(n, nDef)) subproof { - assume(BDef) - have(forall(B, (subset(B, p1) /\ !(B === emptySet)) ==> exists(n, in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b)))))) by Tautology.from( - wellOrder.definition - ) - thenHave((subset(B, p1) /\ !(B === emptySet)) ==> exists(n, in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))))) by InstantiateForall(B) - val exN = have(exists(n, in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))))) by Tautology.from(lastStep, nonEmptyB, subsetB) - - // transform n \in B to n < a1 /\ k1 n != k2 n - have(in(b, B) <=> (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b)))) by InstantiateForall - thenHave( - (in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))) <=> ((in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b))) - ) by Tautology - thenHave( - forall(b, (in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))) <=> ((in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)))) - ) by RightForall - val bEq = have( - forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))) <=> forall( - b, - (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) - ) - ) by Tautology.from( - lastStep, - universalEquivalenceDistribution of (P := lambda(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))), Q := lambda( - b, - (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) - )) - ) - - have(in(n, B) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)))) by InstantiateForall - have( - (in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b)))) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall( - b, - (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) - )) - ) by Tautology.from(lastStep, bEq) - thenHave( - forall( - n, - (in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b)))) <=> (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall( - b, - (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) - )) - ) - ) by RightForall - - have(thesis) by Tautology.from( - lastStep, - exN, - existentialEquivalenceDistribution of (P -> lambda(n, (in(n, B) /\ forall(b, in(b, B) ==> (in(pair(n, b), p2) \/ (n === b))))), Q -> lambda( - n, - (in(n, initialSegment(p, a1)) /\ !(app(k1, n) === app(k2, n)) /\ forall( - b, - (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)) - )) - )) - ) - } - - thenHave(exists(B, BDef) |- exists(n, nDef)) by LeftExists - have(thesis) by Cut(BExists, lastStep) - } - - // but k1 n == F(k1 |^ n) and k2 n == F(k2 |^ n) - val fK1 = have(nDef |- app(k1, n) === F(orderedRestriction(k1, n, p))) subproof { - // n < a1 ==> k1 n = F(k1 |^ n) - have(forall(b, in(b, initialSegment(p, a1)) ==> (app(k1, b) === F(orderedRestriction(k1, b, p))))) by Tautology - thenHave(in(n, initialSegment(p, a1)) ==> (app(k1, n) === F(orderedRestriction(k1, n, p)))) by InstantiateForall(n) - - // we know n < a1, so result - thenHave(thesis) by Tautology - } - val fK2 = have(nDef |- app(k2, n) === F(orderedRestriction(k2, n, p))) subproof { - // n < a2 ==> k2 n = F(k2 |^ n) - have(forall(b, in(b, initialSegment(p, a2)) ==> (app(k2, b) === F(orderedRestriction(k2, b, p))))) by Tautology - val impl = thenHave(in(n, initialSegment(p, a2)) ==> (app(k2, n) === F(orderedRestriction(k2, n, p)))) by InstantiateForall(n) - - // n < a1 and a1 < a2, so n < a2 - have(forall(b, in(b, initialSegment(p, a1)) ==> in(b, initialSegment(p, a2)))) by Tautology.from( - domSubset, - subsetAxiom of (x -> initialSegment(p, a1), y -> initialSegment(p, a2)) - ) - thenHave(in(n, initialSegment(p, a1)) ==> in(n, initialSegment(p, a2))) by InstantiateForall(n) - - // so result - have(thesis) by Tautology.from(lastStep, impl) - } - - // k1 |^ n == k2 |^ n by minimality of n - // so F(k1 |^ n) == F(k2 |^ n) - val ordResEq = have(nDef |- F(orderedRestriction(k1, n, p)) === F(orderedRestriction(k2, n, p))) subproof { - assume(nDef) - - val k1k2 = have(orderedRestriction(k1, n, p) === orderedRestriction(k2, n, p)) subproof { - // suppose not - assume(!(orderedRestriction(k1, n, p) === orderedRestriction(k2, n, p))) - - // there must exist an element where they disagree, say m - val m = variable - val mDef = in(m, initialSegment(p, n)) /\ !(app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)) - val mExists = have(exists(m, mDef)) subproof { - // k1 |^ n != k2 |^ n by assumption - val k1k2unequal = have(!(orderedRestriction(k1, n, p) === orderedRestriction(k2, n, p))) by Restate - - // they are functions on the same domain - val k1k2functional = have(functionalOver(orderedRestriction(k1, n, p), initialSegment(p, n)) /\ functionalOver(orderedRestriction(k2, n, p), initialSegment(p, n))) subproof { - // n < a1 by definition - val nLTa1 = have(in(n, initialSegment(p, a1))) by Restate - - // but a1 < a2, so n < a2 - have(forall(n, in(n, initialSegment(p, a1)) ==> in(n, initialSegment(p, a2)))) by Tautology.from( - domSubset, - subsetAxiom of (x -> initialSegment(p, a1), y -> initialSegment(p, a2)) - ) - thenHave(in(n, initialSegment(p, a1)) ==> in(n, initialSegment(p, a2))) by InstantiateForall(n) - val nLTa2 = have(in(n, initialSegment(p, a2))) by Tautology.from(lastStep, nLTa1) - - // k1 functional over k1, a -> n, b -> a1)) - val k2n = - have(functionalOver(orderedRestriction(k2, n, p), initialSegment(p, n))) by Tautology.from(k2fun, nLTa2, orderedRestrictionFunctionalOverInit of (f -> k2, a -> n, b -> a2)) - - have(thesis) by Tautology.from(k1n, k2n) - } - - // so there is a violating element - have(!forall(m, in(m, initialSegment(p, n)) ==> (app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)))) by Tautology.from( - k1k2unequal, - k1k2functional, - functionsEqualIfEqualOnDomain of (f -> orderedRestriction(k1, n, p), g -> orderedRestriction(k2, n, p), a -> initialSegment(p, n)) - ) - thenHave(thesis) by Restate - } - - // we must have m < n - val mViolatesRestricted = - have(mDef |- in(m, initialSegment(p, a1)) /\ !(app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)) /\ in(pair(m, n), p2)) subproof { - assume(mDef) - // we have n < a1 - have(forall(z, (z === initialSegment(p, a)) <=> (forall(t, in(t, z) <=> (in(t, p1) /\ in(pair(t, a), p2)))))) by Weakening(initialSegment.definition) - val initSegMembership = thenHave((forall(t, in(t, initialSegment(p, a)) <=> (in(t, p1) /\ in(pair(t, a), p2))))) by InstantiateForall(initialSegment(p, a)) - - have(in(n, initialSegment(p, a1)) <=> (in(n, p1) /\ in(pair(n, a1), p2))) by InstantiateForall(n)(initSegMembership of a -> a1) - val nLTa1 = thenHave(in(pair(n, a1), p2)) by Tautology - - // and m < n - have(in(m, initialSegment(p, n)) <=> (in(m, p1) /\ in(pair(m, n), p2))) by InstantiateForall(m)(initSegMembership of a -> n) - val mLTn = thenHave(in(m, p1) /\ in(pair(m, n), p2)) by Tautology - - // by transitivity, m < a1 as well - have(forall(w, forall(y, forall(z, (in(pair(w, y), p2) /\ in(pair(y, z), p2)) ==> in(pair(w, z), p2))))) by Weakening(wellOrderTransitivity) - thenHave((in(pair(m, n), p2) /\ in(pair(n, a1), p2)) ==> in(pair(m, a1), p2)) by InstantiateForall(m, n, a1) - val mLTa1 = have(in(m, p1) /\ in(pair(m, a1), p2)) by Tautology.from(lastStep, nLTa1, mLTn) - - have(in(m, initialSegment(p, a1)) <=> (in(m, p1) /\ in(pair(m, a1), p2))) by InstantiateForall(m)(initSegMembership of a -> a1) - have(thesis) by Tautology.from(lastStep, mLTa1, mLTn) - } - - val mViolates = have(mDef |- in(m, initialSegment(p, a1)) /\ !(app(k1, m) === app(k2, m)) /\ in(pair(m, n), p2)) subproof { - assume(mDef) - - val mInDom1 = have(in(m, relationDomain(k1))) subproof { - val domEQ = have(initialSegment(p, a1) === relationDomain(k1)) by Tautology.from(functionalOver.definition of (f -> k1, x -> initialSegment(p, a1))) - - have(in(m, initialSegment(p, a1))) by Tautology.from(mViolatesRestricted) - thenHave(thesis) by Substitution.ApplyRules(domEQ) - } - - val mInDom2 = have(in(m, relationDomain(k2))) subproof { - val domEQ = have(initialSegment(p, a2) === relationDomain(k2)) by Tautology.from(functionalOver.definition of (f -> k2, x -> initialSegment(p, a2))) - - val mLTa1 = have(in(m, initialSegment(p, a1))) by Tautology.from(mViolatesRestricted) - have(forall(m, in(m, initialSegment(p, a1)) ==> in(m, initialSegment(p, a2)))) by Tautology.from( - subsetAxiom of (x -> initialSegment(p, a1), y -> initialSegment(p, a2)), - domSubset - ) - thenHave(in(m, initialSegment(p, a1)) ==> in(m, initialSegment(p, a2))) by InstantiateForall(m) - have(in(m, initialSegment(p, a2))) by Tautology.from(lastStep, mLTa1) - - thenHave(thesis) by Substitution.ApplyRules(domEQ) - } - - // if the application is equal on the ordered restriction, it must be equal on the entire functions - have((app(orderedRestriction(k1, n, p), m) === app(orderedRestriction(k2, n, p), m)) <=> (app(k1, m) === app(k2, m))) by Tautology.from( - orderedRestrictionAgreement of (f -> k1, g -> k2, a -> n, b -> m), - pIsAPartialOrder, - mInDom1, - mInDom2 - ) - have(thesis) by Tautology.from(mViolatesRestricted, lastStep) - } - - // but n was the minimal violation - // contradiction - have(mDef |- bot) subproof { - assume(mDef) - // m < a1 and k1 m != k2 m ==> n < m \/ n = m - have(forall(b, (in(b, initialSegment(p, a1)) /\ !(app(k1, b) === app(k2, b))) ==> (in(pair(n, b), p2) \/ (n === b)))) by Restate - thenHave((in(m, initialSegment(p, a1)) /\ !(app(k1, m) === app(k2, m))) ==> (in(pair(n, m), p2) \/ (n === m))) by InstantiateForall(m) - val mLeqn = have((in(pair(n, m), p2) \/ (n === m))) by Tautology.from(lastStep, mViolates) - - // we had m < n, and the order is anti-symmetric, so n = m - have(forall(n, forall(m, (in(pair(m, n), p2) /\ in(pair(n, m), p2)) ==> (n === m)))) by Tautology.from( - wellOrder.definition, - totalOrder.definition, - partialOrder.definition, - antiSymmetric.definition of (r -> p2, x -> p1) - ) - thenHave((in(pair(m, n), p2) /\ in(pair(n, m), p2)) ==> (n === m)) by InstantiateForall(n, m) - val nEQm = have((n === m)) by Tautology.from(lastStep, mViolates, mLeqn) - - // however, that means n < n, but the order is anti-reflexive - have(in(pair(m, n), p2)) by Weakening(mViolates) - val nLTn = thenHave(in(pair(n, n), p2)) by Substitution.ApplyRules(nEQm) - - have(forall(n, in(n, p1) ==> !in(pair(n, n), p2))) by Tautology.from(pIsAPartialOrder, partialOrder.definition, antiReflexive.definition of (r -> p2, x -> p1)) - thenHave(in(n, p1) ==> !in(pair(n, n), p2)) by InstantiateForall(n) - have(!in(pair(n, n), p2)) by Tautology.from(lastStep, initialSegmentBaseElement of (x -> n, y -> a1), pIsAPartialOrder) - - // this is a contradiction - have(thesis) by Tautology.from(lastStep, nLTn) - } - thenHave(exists(m, mDef) |- bot) by LeftExists - have(bot) by Cut(mExists, lastStep) - } - - have(F(orderedRestriction(k1, n, p)) === F(orderedRestriction(k1, n, p))) by Restate - thenHave(thesis) by Substitution.ApplyRules(k1k2) - } - - // // finally k1 n == k2 n - // // this is a contradiction - val appEq = have(nDef |- app(k1, n) === app(k2, n)) subproof { - assume(nDef) - val k1ToFK2 = equalityTransitivity of (x -> app(k1, n), y -> F(orderedRestriction(k1, n, p)), z -> F(orderedRestriction(k2, n, p))) - val k1ToK2 = equalityTransitivity of (x -> app(k1, n), y -> F(orderedRestriction(k2, n, p)), z -> app(k2, n)) - - have(thesis) by Tautology.from(fK1, fK2, ordResEq, k1ToFK2, k1ToK2) - } - - thenHave(nDef |- ()) by Tautology - thenHave(exists(n, nDef) |- ()) by LeftExists - val disagreeCase = have(k1k2disagree |- ()) by Cut(nExists, lastStep) - - have(!k1k2disagree |- subset(k1, k2)) subproof { - assume(!k1k2disagree) - val impl = have(forall(b, in(b, initialSegment(p, a1)) ==> (app(k1, b) === app(k2, b)))) by Restate - - // k1 k2 are functional - val funK1 = have(functionalOver(k1, initialSegment(p, a1))) by Tautology - val funK2 = have(functionalOver(k2, initialSegment(p, a2))) by Tautology - - // // and a1, y -> a2), pIsAPartialOrder) - - have(thesis) by Tautology.from(impl, funK1, funK2, domSubset, functionsSubsetIfEqualOnSubsetDomain of (f -> k1, g -> k2, a -> initialSegment(p, a1), b -> initialSegment(p, a2))) - } - - have(thesis) by Tautology.from(lastStep, disagreeCase) - } - - // finally, instantiate to y1 < y2 and to y2 < y1 - val y1LTy2 = have(in(pair(y1, y2), p2) |- subset(t1, t2)) by Restate.from(ltToSubset of (a1 -> y1, k1 -> t1, a2 -> y2, k2 -> t2)) - val y2LTy1 = have(in(pair(y2, y1), p2) |- subset(t2, t1)) by Restate.from(ltToSubset of (a1 -> y2, k1 -> t2, a2 -> y1, k2 -> t1)) - - // totality of the order means y1 < y2 or y2 < y1 - have(in(pair(y1, y2), p2) \/ in(pair(y2, y1), p2)) subproof { - have(forall(y1, forall(y2, (in(y1, p1) /\ in(y2, p1)) ==> (in(pair(y1, y2), p2) \/ in(pair(y2, y1), p2) \/ (y1 === y2))))) by Tautology.from( - wellOrder.definition, - totalOrder.definition, - total.definition of (r -> p2, x -> p1) - ) - val impl = thenHave((in(y1, p1) /\ in(y2, p1)) ==> (in(pair(y1, y2), p2) \/ in(pair(y2, y1), p2) \/ (y1 === y2))) by InstantiateForall(y1, y2) - - // expand defs - have(forall(z, (z === initialSegment(x, y)) <=> forall(t, in(t, z) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x)))))) by Weakening( - initialSegment.definition of (p -> x, a -> y) - ) - thenHave(forall(t, in(t, initialSegment(x, y)) <=> (in(t, firstInPair(x)) /\ in(pair(t, y), secondInPair(x))))) by InstantiateForall(initialSegment(x, y)) - val initXY = thenHave(in(c, initialSegment(x, y)) <=> (in(c, firstInPair(x)) /\ in(pair(c, y), secondInPair(x)))) by InstantiateForall(c) - - have(in(y1, p1) /\ in(y2, p1)) by Tautology.from(initialSegmentBaseElement of (y -> x, x -> y1), initialSegmentBaseElement of (y -> x, x -> y2), pIsAPartialOrder) - have(thesis) by Tautology.from(lastStep, impl) - } - - have(thesis) by Tautology.from(lastStep, y1LTy2, y2LTy1) - // sorry - } - - have(thesis) by Tautology.from(yeq, neq) - } - - thenHave((ytDef(y1, t1), ytDef(y2, t2)) |- subset(t1, t2) \/ subset(t2, t1)) by Restate - thenHave((ytDef(y1, t1), exists(y2, ytDef(y2, t2))) |- subset(t1, t2) \/ subset(t2, t1)) by LeftExists - val exToRes = thenHave((exists(y1, ytDef(y1, t1)), exists(y2, ytDef(y2, t2))) |- subset(t1, t2) \/ subset(t2, t1)) by LeftExists - - // wDef - val exy = have(in(b, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(b, y))) by InstantiateForall - - have(thesis) by Tautology.from(exToRes, exy of b -> t1, exy of b -> t2) - } - - have(thesis) by Tautology.from(t1EQt2, t1NEQt2) - } - - thenHave(in(t1, w) /\ in(t2, w) ==> subset(t1, t2) \/ subset(t2, t1)) by Restate - thenHave(forall(t2, in(t1, w) /\ in(t2, w) ==> subset(t1, t2) \/ subset(t2, t1))) by RightForall - thenHave(forall(t1, forall(t2, in(t1, w) /\ in(t2, w) ==> subset(t1, t2) \/ subset(t2, t1)))) by RightForall - } - - val uwfunctional = Lemma( - wDef /\ wellOrder(p) |- functional(uw) - ) { - have(thesis) by Tautology.from(elemsFunctional, elemsSubset, unionOfFunctionSet of z -> w) - } - - val uwRestrictedEq = Lemma( - wellOrder(p) /\ wDef /\ in(z, relationDomain(uw)) |- app(uw, z) === F(orderedRestriction(uw, z, p)) - ) { - assume(wellOrder(p), wDef) - assume(in(z, relationDomain(uw))) - - // \exists g \in w. uw z = F g |^ z - val gExists = have(exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))))) subproof { - // dom uw = < x - // val domUEq = have(relationDomain(uw) === initialSegment(p, x)) by Tautology.from(uwFunctionalOver, functionalOverImpliesDomain of (f -> uw, x -> initialSegment(p, x))) - - // z in dom uw - // have(in(z, initialSegment(p, x))) by Restate - // val zInDom = thenHave(in(z, relationDomain(uw))) by EqualityReasoning.ApplyRules(domUEq) - - // so exists g \in w, z \in dom g - have(functional(uw) /\ in(z, relationDomain(uw)) |- exists(g, in(g, w) /\ in(z, relationDomain(g)))) by InstantiateForall(z)(domainOfFunctionalUnion of z -> w) - val gExists = have(exists(g, in(g, w) /\ in(z, relationDomain(g)))) by Tautology.from(lastStep, uwfunctional) - - have((in(g, w), in(z, relationDomain(g))) |- app(uw, z) === F(orderedRestriction(g, z, p))) subproof { - assume(in(g, w)) - assume(in(z, relationDomain(g))) - - // given such a g, g(z) = uw(z) - val gEqU = have(app(g, z) === app(uw, z)) subproof { - have( - (b === app(f, z)) <=> ((functional(f) /\ in(z, relationDomain(f))) ==> in(pair(z, b), f)) /\ ((!functional(f) \/ !in(z, relationDomain(f))) ==> (b === ∅)) - ) by InstantiateForall(b)(app.definition of x -> z) - val appDef = thenHave(functional(f) /\ in(z, relationDomain(f)) |- (b === app(f, z)) <=> in(pair(z, b), f)) by Tautology - - // for g z - val gb = have((b === app(g, z)) <=> in(pair(z, b), g)) subproof { - // g is functional - have(in(g, w) ==> functional(g)) by InstantiateForall(g)(elemsFunctional) - have(thesis) by Tautology.from(lastStep, appDef of f -> g) - } - - // for uw z - val uwb = have((b === app(uw, z)) <=> in(pair(z, b), uw)) by Tautology.from(appDef of f -> uw, uwfunctional) - - // in g ==> in uw - have(in(t, g) ==> in(t, uw)) subproof { - assume(in(t, g)) - - // suffices to show the existence of g - val unionAx = have(in(t, uw) <=> exists(g, in(g, w) /\ in(t, g))) by Weakening(unionAxiom of (z -> t, x -> w)) - - have(in(g, w) /\ in(t, g)) by Restate - thenHave(exists(g, in(g, w) /\ in(t, g))) by RightExists - - have(thesis) by Tautology.from(lastStep, unionAx) - } - - // equal - have((b === app(g, z)) |- (b === app(uw, z))) by Tautology.from(lastStep of t -> pair(z, b), gb, uwb) - have(thesis) by Restate.from(lastStep of b -> app(g, z)) - } - - // we must also have g(z) = F(g |^ z) - have(app(g, z) === F(orderedRestriction(g, z, p))) subproof { - have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall - val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology - - have(fun(g, y) |- app(g, z) === F(orderedRestriction(g, z, p))) subproof { - assume(fun(g, y)) - - // dom g = < y - val domEq = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(functionalOverImpliesDomain of (f -> g, x -> initialSegment(p, y))) - - have(forall(a, in(a, initialSegment(p, y)) ==> (app(g, a) === F(orderedRestriction(g, a, p))))) by Restate - thenHave(in(z, initialSegment(p, y)) ==> (app(g, z) === F(orderedRestriction(g, z, p)))) by InstantiateForall(z) - thenHave(in(z, relationDomain(g)) ==> (app(g, z) === F(orderedRestriction(g, z, p)))) by Substitution.ApplyRules(domEq) - thenHave(thesis) by Restate - } - thenHave(in(y, initialSegment(p, x)) /\ fun(g, y) |- app(g, z) === F(orderedRestriction(g, z, p))) by Weakening - thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- app(g, z) === F(orderedRestriction(g, z, p))) by LeftExists - have(thesis) by Cut(yExists, lastStep) - } - - thenHave(thesis) by Substitution.ApplyRules(gEqU) - } - - thenHave((in(g, w) /\ in(z, relationDomain(g))) |- in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p)))) by Tautology - thenHave((in(g, w) /\ in(z, relationDomain(g))) |- exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))))) by RightExists - thenHave(exists(g, in(g, w) /\ in(z, relationDomain(g))) |- exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))))) by LeftExists - - have(thesis) by Cut(gExists, lastStep) - } - - // but g \in w ==> g |^ z = uw |^ z - val gRestrictedEq = have(in(g, w) /\ in(z, relationDomain(g)) |- orderedRestriction(g, z, p) === orderedRestriction(uw, z, p)) subproof { - assume(in(g, w)) - assume(in(z, relationDomain(g))) - - val og = orderedRestriction(g, z, p) - val ou = orderedRestriction(uw, z, p) - - // we prove this for a generic element t - val ogDef = have(in(t, og) <=> (in(t, g) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(orderedRestrictionMembership of (f -> g, a -> z, b -> t), pIsAPartialOrder) - - val ouDef = have(in(t, ou) <=> (in(t, uw) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(orderedRestrictionMembership of (f -> uw, a -> z, b -> t), pIsAPartialOrder) - - // t \in g |^ z ==> t \in uw |^ z - val fwd = have(in(t, og) |- in(t, ou)) subproof { - assume(in(t, og)) - val tInG = have((in(t, g) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(ogDef) - - // but g is a subset of uw - have(in(t, g) ==> in(t, uw)) subproof { - assume(in(t, g)) - - have(in(g, w) /\ in(t, g)) by Restate - thenHave(exists(g, in(g, w) /\ in(t, g))) by RightExists - have(thesis) by Tautology.from(lastStep, unionAxiom of (x -> w, z -> t)) - } - - have(thesis) by Tautology.from(lastStep, tInG, ouDef) - } - - // t \in uw |^ z ==> t \in g |^ z - val bwd = have(in(t, ou) |- in(t, og)) subproof { - assume(in(t, ou)) - val tInU = have((in(t, uw) /\ in(firstInPair(t), initialSegment(p, z)))) by Tautology.from(ouDef) - - // if t \in uw and t1 < z - have(in(t, uw) /\ in(firstInPair(t), initialSegment(p, z)) |- in(t, g)) subproof { - assume(in(t, uw)) - assume(in(firstInPair(t), initialSegment(p, z))) - - // suppose ! t \in g - have(!in(t, g) |- ()) subproof { - assume(!in(t, g)) - - // exists f \in w, t \in f by union axiom on uw - val fExists = have(exists(f, in(f, w) /\ in(t, f))) by Tautology.from(unionAxiom of (x -> w, z -> t)) - - // if such an f exists - have(in(f, w) /\ in(t, f) |- ()) subproof { - assume(in(f, w)) - assume(in(t, f)) - - // f \subseteq g or g \subseteq f - val cases = have(subset(f, g) \/ subset(g, f)) subproof { - have((in(g, w) /\ in(f, w)) ==> (subset(g, f) \/ subset(f, g))) by InstantiateForall(g, f)(elemsSubset) - thenHave(thesis) by Tautology - } - - // f \subseteq g ==> contradiction directly - val fg = have(subset(f, g) |- ()) subproof { - assume(subset(f, g)) - - have(forall(t, in(t, f) ==> in(t, g))) by Tautology.from(subsetAxiom of (x -> f, y -> g)) - thenHave(in(t, f) ==> in(t, g)) by InstantiateForall(t) - thenHave(thesis) by Tautology - } - - // g \subseteq f - val gf = have(subset(g, f) |- ()) subproof { - assume(subset(g, f)) - - val t1 = firstInPair(t) - val t2 = secondInPair(t) - - // t1 \in dom og - val t1InDomOG = have(in(t1, relationDomain(og))) subproof { - // t \in ou - // so t1 \in exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall - val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology - - have(in(y, initialSegment(p, x)) /\ fun(g, y) |- relationDomain(og) === initialSegment(p, z)) subproof { - assume(in(y, initialSegment(p, x))) - assume(fun(g, y)) - - // dom g = < y - have(functionalOver(g, initialSegment(p, y))) by Restate - val domEq = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(lastStep, functionalOver.definition of (f -> g, x -> initialSegment(p, y))) - - // but z in dom g, so z < y - have(in(pair(z, y), p2)) subproof { - have(in(z, relationDomain(g))) by Restate - thenHave(in(z, initialSegment(p, y))) by Substitution.ApplyRules(domEq) - have(thesis) by Tautology.from(lastStep, initialSegmentElement of x -> z, pIsAPartialOrder) - } - - // so < z \subseteq < y - val zySubset = have(subset(initialSegment(p, z), initialSegment(p, y))) by Tautology.from(lastStep, initialSegmentsSubset of (x -> z, y -> y), pIsAPartialOrder) - - // dom og = < y \cap < z = < z - have(thesis) subproof { - // dom og = < y \cap < z - val domEQ = have(relationDomain(og) === setIntersection(initialSegment(p, z), initialSegment(p, y))) subproof { - val ogExpand = have(restrictedFunction(g, initialSegment(p, z)) === og) by InstantiateForall(og)(orderedRestriction.definition of (f -> g, a -> z)) - - have(relationDomain(restrictedFunction(g, initialSegment(p, z))) === setIntersection(initialSegment(p, z), relationDomain(g))) by Weakening( - restrictedFunctionDomain of (f -> g, x -> initialSegment(p, z)) - ) - thenHave(thesis) by Substitution.ApplyRules(ogExpand, domEq) - } - - have(setIntersection(initialSegment(p, z), initialSegment(p, y)) === initialSegment(p, z)) by Tautology.from( - lastStep, - zySubset, - intersectionOfSubsets of (x -> initialSegment(p, z), y -> initialSegment(p, y)) - ) - - have(thesis) by Substitution.ApplyRules(lastStep)(domEQ) - } - } - - thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- relationDomain(og) === initialSegment(p, z)) by LeftExists - have(thesis) by Cut(yExists, lastStep) - } - - // t1 \in dom g - have(thesis) by Substitution.ApplyRules(lastStep)(t1LTz) - } - - // since t1 \in dom g, exists a. (t, a) \in g - val aExists = have(exists(a, in(pair(t1, a), g))) subproof { - have(forall(t, in(t, relationDomain(g)) <=> exists(a, in(pair(t, a), g)))) by InstantiateForall(relationDomain(g))(relationDomain.definition of r -> g) - val domIff = thenHave(in(t1, relationDomain(g)) <=> exists(a, in(pair(t1, a), g))) by InstantiateForall(t1) - - // t1 is in dom og, so it is in dom g - have(in(t1, relationDomain(g))) subproof { - val ordEQ = have(og === restrictedFunction(g, initialSegment(p, z))) by InstantiateForall(og)(orderedRestriction.definition of (f -> g, a -> z)) - - have(relationDomain(restrictedFunction(g, initialSegment(p, z))) === setIntersection(initialSegment(p, z), relationDomain(g))) by Tautology.from( - restrictedFunctionDomain of (f -> g, x -> initialSegment(p, z)) - ) - thenHave(relationDomain(og) === setIntersection(initialSegment(p, z), relationDomain(g))) by Substitution.ApplyRules(ordEQ) - - have(forall(b, in(b, relationDomain(og)) <=> in(b, setIntersection(initialSegment(p, z), relationDomain(g))))) by Tautology.from( - lastStep, - extensionalityAxiom of (x -> relationDomain(og), y -> setIntersection(initialSegment(p, z), relationDomain(g))) - ) - thenHave(in(t1, relationDomain(og)) <=> in(t1, setIntersection(initialSegment(p, z), relationDomain(g)))) by InstantiateForall(t1) - have(in(t1, setIntersection(initialSegment(p, z), relationDomain(g)))) by Tautology.from(lastStep, t1InDomOG) - have(thesis) by Tautology.from(lastStep, setIntersectionMembership of (t -> t1, x -> initialSegment(p, z), y -> relationDomain(g))) - } - - have(thesis) by Tautology.from(lastStep, domIff) - } - - have(in(pair(t1, a), g) |- ()) subproof { - assume(in(pair(t1, a), g)) - - // (t1, a) \in f - have(forall(t, in(t, g) ==> in(t, f))) by Tautology.from(subsetAxiom of (x -> g, y -> f)) - thenHave(in(pair(t1, a), g) ==> in(pair(t1, a), f)) by InstantiateForall(pair(t1, a)) - val t1aInF = thenHave(in(pair(t1, a), f)) by Tautology - - // t must be a pair - val tIsPair = have(exists(a, exists(b, pair(a, b) === t))) subproof { - have(forall(t, in(t, uw) ==> exists(a, exists(b, (pair(a, b) === t) /\ in(a, relationDomain(uw)))))) by Tautology.from(uwfunctional, functionalMembership of (f -> uw)) - val exIn = thenHave(exists(a, exists(b, (pair(a, b) === t) /\ in(a, relationDomain(uw))))) by InstantiateForall(t) - - // eliminate extra terms inside exists - have(exists(a, exists(b, (pair(a, b) === t) /\ in(a, relationDomain(uw)))) |- exists(a, exists(b, (pair(a, b) === t)))) subproof { - have((pair(c, b) === t) /\ in(c, relationDomain(uw)) |- (pair(c, b) === t)) by Restate - thenHave((pair(c, b) === t) /\ in(c, relationDomain(uw)) |- exists(b, (pair(c, b) === t))) by RightExists - thenHave((pair(c, b) === t) /\ in(c, relationDomain(uw)) |- exists(c, exists(b, (pair(c, b) === t)))) by RightExists - thenHave(exists(b, (pair(c, b) === t) /\ in(c, relationDomain(uw))) |- exists(c, exists(b, (pair(c, b) === t)))) by LeftExists - thenHave(exists(c, exists(b, (pair(c, b) === t) /\ in(c, relationDomain(uw)))) |- exists(c, exists(b, (pair(c, b) === t)))) by LeftExists - } - have(thesis) by Cut(exIn, lastStep) - } - val tEqt1t2 = have(t === pair(t1, t2)) by Tautology.from(tIsPair, pairReconstruction of x -> t) - - // but (t1, t2) \in f - val t1t2InF = have(in(pair(t1, t2), f)) subproof { - // t in f - val tInF = have(in(t, f)) by Restate - - // so (t1, t2) = t - have(thesis) by Substitution.ApplyRules(tEqt1t2)(tInF) - } - - // t2 = a - val t2Eqa = have(t2 === a) subproof { - // f is functional - have(in(f, w) ==> functional(f)) by InstantiateForall(f)(elemsFunctional) - - // given t1, there must be a unique element in ran f it maps to - have(forall(t, exists(b, in(pair(t, b), f)) ==> existsOne(b, in(pair(t, b), f)))) by Tautology.from(lastStep, functional.definition) - val exToExOne = thenHave(exists(b, in(pair(t1, b), f)) |- existsOne(b, in(pair(t1, b), f))) by InstantiateForall(t1) - - have(in(pair(t1, a), f)) by Weakening(t1aInF) - val ex = thenHave(exists(b, in(pair(t1, b), f))) by RightExists - val exOne = have(existsOne(b, in(pair(t1, b), f))) by Cut(lastStep, exToExOne) - - have(forall(a, forall(b, !in(pair(t1, a), f) \/ !in(pair(t1, b), f) \/ (a === b)))) by Tautology.from(atleastTwoExist of P -> lambda(a, in(pair(t1, a), f)), ex, exOne) - thenHave(!in(pair(t1, a), f) \/ !in(pair(t1, t2), f) \/ (a === t2)) by InstantiateForall(a, t2) - have(thesis) by Tautology.from(lastStep, t1aInF, t1t2InF) - } - - // but then (t1, t2) = t \in g - have(in(pair(t1, a), g)) by Restate - thenHave(in(pair(t1, t2), g)) by Substitution.ApplyRules(t2Eqa) - thenHave(in(t, g)) by Substitution.ApplyRules(tEqt1t2) - - // this is a contradiction - thenHave(thesis) by Tautology - } - - thenHave(exists(a, in(pair(t1, a), g)) |- ()) by LeftExists - have(thesis) by Tautology.from(lastStep, aExists) - } - - have(thesis) by Tautology.from(cases, gf, fg) - } - - thenHave(exists(f, in(f, w) /\ in(t, f)) |- ()) by LeftExists - have(thesis) by Tautology.from(lastStep, fExists) - } - } - - have(thesis) by Tautology.from(lastStep, tInU, ogDef) - } - - have(in(t, ou) <=> in(t, og)) by Tautology.from(fwd, bwd) - thenHave(forall(t, in(t, ou) <=> in(t, og))) by RightForall - have(ou === og) by Tautology.from(lastStep, extensionalityAxiom of (x -> ou, y -> og)) - } - - have(in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))) |- app(uw, z) === F(orderedRestriction(uw, z, p))) subproof { - have(in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p))) |- (app(uw, z) === F(orderedRestriction(g, z, p)))) by Restate - thenHave(thesis) by Substitution.ApplyRules(gRestrictedEq) - } - - thenHave(exists(g, in(g, w) /\ in(z, relationDomain(g)) /\ (app(uw, z) === F(orderedRestriction(g, z, p)))) |- app(uw, z) === F(orderedRestriction(uw, z, p))) by LeftExists - have(thesis) by Tautology.from(gExists, lastStep) - - } - - val uniquenessFromExistsOne = Theorem( - existsOne(x, P(x)) |- forall(y, forall(z, (P(y) /\ P(z)) ==> (y === z))) - ) { - val a1 = assume(exists(x, forall(y, P(y) <=> (y === x)))) - val xe = witness(a1) - have(P(y) <=> (y === xe)) by InstantiateForall(y)(xe.definition) - val py = thenHave(P(y) |- y === xe) by Weakening - have(P(z) <=> (z === xe)) by InstantiateForall(z)(xe.definition) - val pz = thenHave(P(z) |- z === xe) by Weakening - have((P(y), P(z)) |- (y === z)) by Substitution.ApplyRules(pz)(py) - have((P(y) /\ P(z)) ==> (y === z)) by Tautology.from(lastStep) - thenHave(forall(z, (P(y) /\ P(z)) ==> (y === z))) by RightForall - thenHave(thesis) by RightForall - - } - - private val A = variable - private val B = variable - private val R = predicate[2] - val strictReplacementSchema = Theorem( - forall(x, in(x, A) ==> existsOne(y, R(x, y))) - |- exists(B, forall(y, in(y, B) <=> exists(x, in(x, A) /\ R(x, y)))) - ) { - assume(forall(x, in(x, A) ==> existsOne(y, R(x, y)))) - thenHave(in(x, A) ==> existsOne(y, R(x, y))) by InstantiateForall(x) - have(in(x, A) ==> ∀(y, ∀(z, (R(x, y) /\ R(x, z)) ==> (y === z)))) by Tautology.from(uniquenessFromExistsOne of (P := lambda(y, R(x, y))), lastStep) - thenHave(forall(x, in(x, A) ==> ∀(y, ∀(z, (R(x, y) /\ R(x, z)) ==> (y === z))))) by RightForall - have(thesis) by Tautology.from(lastStep, replacementSchema of (SchematicPredicateLabel("P", 2) -> R)) - } - - /** - * Theorem --- Recursive Function Exists - * - * Given that for each element of the initial segment of `x`, a function as - * defined by [[wellOrderedRecursion]] exists, then a function with the same - * properties exists for `x`. - */ - val recursiveFunctionExistencePropagates = Theorem( - wellOrder(p) /\ in(x, p1) /\ forall(y, in(y, initialSegment(p, x)) ==> prop(y)) |- exists(g, fun(g, x)) - ) { - assume(wellOrder(p)) - assume(in(x, p1)) - assume(forall(y, in(y, initialSegment(p, x)) ==> prop(y))) - - // if w is as defined, there is a function g as required - have(wDef |- exists(g, fun(g, x))) subproof { - assume(wDef) - // properties of the elements of w - // 1. t \in w ==> functional t - // 2. t1, t2 \in w ==> t1 \subseteq t2 \/ t2 \subseteq t1 - - // 1. t \in w ==> functional t - // see [[elemsFunctional]] and [[elemsSubset]] - - // working with orderedRestrictions - val ordBreakdown = have(orderedRestriction(a, b, p) === restrictedFunction(a, initialSegment(p, b))) by InstantiateForall(orderedRestriction(a, b, p))( - orderedRestriction.definition of (f -> a, a -> b) - ) - - // see [[uwfunctional]] - - // now, from w, we will construct the requisite function g for x - // see [[uwRestrictedEq]] - - // common subproof - val zyInDomUw = have(in(z, initialSegment(p, y)) /\ in(y, initialSegment(p, x)) |- in(z, relationDomain(uw))) subproof { - assume(in(z, initialSegment(p, y)), in(y, initialSegment(p, x))) - - have(in(y, initialSegment(p, x)) ==> (in(y, p1) /\ existsOne(g, fun(g, y)))) by InstantiateForall - val gExists = have(exists(g, fun(g, y))) by Tautology.from(lastStep, existsOneImpliesExists of (P -> lambda(g, fun(g, y)))) - - have(fun(g, y) |- in(z, relationDomain(uw))) subproof { - assume(fun(g, y)) - - val zIng = have(in(z, relationDomain(g))) subproof { - have(functionalOver(g, initialSegment(p, y))) by Tautology - val domEQ = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(lastStep, functionalOver.definition of (f -> g, x -> initialSegment(p, y))) - - have(in(z, initialSegment(p, y))) by Restate - thenHave(thesis) by Substitution.ApplyRules(domEQ) - } - - val gInw = have(in(g, w)) subproof { - have(in(y, initialSegment(p, x)) /\ fun(g, y)) by Restate - val yEx = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by RightExists - - have(forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y)))) by Restate - thenHave(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall(g) - have(thesis) by Tautology.from(lastStep, yEx) - } - - have(forall(z, in(z, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(z, relationDomain(g))))) by Tautology.from(uwfunctional, domainOfFunctionalUnion of z -> w) - val zInUiff = thenHave(in(z, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(z, relationDomain(g)))) by InstantiateForall(z) - - have(in(g, w) /\ in(z, relationDomain(g))) by Tautology.from(zIng, gInw) - thenHave(exists(g, in(g, w) /\ in(z, relationDomain(g)))) by RightExists - have(in(z, relationDomain(uw))) by Tautology.from(lastStep, zInUiff) - } - - thenHave(exists(g, fun(g, y)) |- in(z, relationDomain(uw))) by LeftExists - have(in(z, relationDomain(uw))) by Cut.withParameters(exists(g, fun(g, y)))(gExists, lastStep) - } - - // there are two cases, either x has a predecessor, or it doesn't - val limSuccCases = have(limitElement(p, x) \/ successorElement(p, x)) by Tautology.from(everyElemInTotalOrderLimitOrSuccessor, wellOrder.definition) - - // if x has no predecessor, i.e., it is a limit element, w is the required function - val limitCase = have(limitElement(p, x) |- exists(g, fun(g, x))) subproof { - assume(limitElement(p, x)) - - // exists(y, in(pair(y, x), p2) /\ in(pair(elem, y), p2))) by Tautology.from(initialSegmentUnionForLimitElementsIsComplete of t -> elem, pIsATotalOrder) - - have(forall(t, in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g))))) by Tautology.from(domainOfFunctionalUnion of z -> w, uwfunctional) - val domDef = thenHave(in(elem, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by InstantiateForall(elem) - - have(exists(g, in(g, w) /\ in(elem, relationDomain(g))) <=> in(elem, initialSegment(p, x))) subproof { - val fwd = have(exists(g, in(g, w) /\ in(elem, relationDomain(g))) |- in(elem, initialSegment(p, x))) subproof { - have(in(g, w) /\ in(elem, relationDomain(g)) |- in(elem, initialSegment(p, x))) subproof { - assume(in(g, w)) - assume(in(elem, relationDomain(g))) - - // there must be a y < x that g is a recursive function for - have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall - val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology - - // elem is in dom g, which is g, x -> initialSegment(p, y))) - have(forall(elem, in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y)))) by Tautology.from( - lastStep, - extensionalityAxiom of (x -> relationDomain(g), y -> initialSegment(p, y)) - ) - thenHave(in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y))) by InstantiateForall(elem) - val eLTy = have(in(pair(elem, y), p2)) by Tautology.from(lastStep, initialSegmentElement of x -> elem, pIsAPartialOrder) - val yLTx = have(in(pair(y, x), p2)) by Tautology.from(initialSegmentElement of (x -> y, y -> x), pIsAPartialOrder) - - // apply transitivity - have(forall(elem, forall(y, forall(x, (in(pair(elem, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(elem, x), p2))))) by Weakening(wellOrderTransitivity) - thenHave((in(pair(elem, y), p2) /\ in(pair(y, x), p2)) ==> in(pair(elem, x), p2)) by InstantiateForall(elem, y, x) - have(in(pair(elem, x), p2)) by Tautology.from(lastStep, eLTy, yLTx) - have(thesis) by Tautology.from(lastStep, initialSegmentElement of (y -> x, x -> elem), pIsAPartialOrder) - } - - thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- in(elem, initialSegment(p, x))) by LeftExists - have(thesis) by Tautology.from(yExists, lastStep) - } - - thenHave(thesis) by LeftExists - } - - val bwd = have(in(elem, initialSegment(p, x)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) subproof { - assume(in(elem, initialSegment(p, x))) - - // find a y s.t. elem < y < x - // this exists since x is a limit element - val yExists = have(exists(y, in(pair(elem, y), p2) /\ in(pair(y, x), p2))) by Tautology.from(limitProp, initialSegmentElement of (y -> x, x -> elem), pIsAPartialOrder) - - // given y, there is a g which is defined recursively for y - val gExists = have(in(pair(y, x), p2) |- exists(g, fun(g, y))) subproof { - have(in(y, initialSegment(p, x)) ==> (in(y, p1) /\ existsOne(g, fun(g, y)))) by InstantiateForall - have(thesis) by Tautology.from(lastStep, initialSegmentElement of (x -> y, y -> x), existsOneImpliesExists of P -> lambda(g, fun(g, y)), pIsAPartialOrder) - } - - // these g and y give us the required witness for elem - have((in(pair(elem, y), p2) /\ in(pair(y, x), p2), fun(g, y)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) subproof { - assume(in(pair(elem, y), p2) /\ in(pair(y, x), p2)) - assume(fun(g, y)) - - have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(functionalOver.definition of (f -> g, x -> initialSegment(p, y))) - have(forall(elem, in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y)))) by Tautology.from( - lastStep, - extensionalityAxiom of (x -> relationDomain(g), y -> initialSegment(p, y)) - ) - thenHave(in(elem, relationDomain(g)) <=> in(elem, initialSegment(p, y))) by InstantiateForall(elem) - - val elemInDom = have(in(elem, relationDomain(g))) by Tautology.from(lastStep, initialSegmentElement of x -> elem, pIsAPartialOrder) - - have(in(y, initialSegment(p, x)) /\ fun(g, y)) by Tautology.from(initialSegmentElement of (x -> y, y -> x), pIsAPartialOrder) - val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by RightExists - - have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall - have(in(g, w) /\ in(elem, relationDomain(g))) by Tautology.from(lastStep, yExists, elemInDom) - thenHave(thesis) by RightExists - } - - thenHave((in(pair(elem, y), p2) /\ in(pair(y, x), p2), exists(g, fun(g, y))) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by LeftExists - have((in(pair(elem, y), p2) /\ in(pair(y, x), p2)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by Tautology.from(lastStep, gExists) - thenHave(exists(y, in(pair(elem, y), p2) /\ in(pair(y, x), p2)) |- exists(g, in(g, w) /\ in(elem, relationDomain(g)))) by LeftExists - have(thesis) by Cut(yExists, lastStep) - } - - have(thesis) by Tautology.from(fwd, bwd) - } - - have(in(elem, relationDomain(uw)) <=> in(elem, initialSegment(p, x))) by Tautology.from(lastStep, domDef) - thenHave(forall(elem, in(elem, relationDomain(uw)) <=> in(elem, initialSegment(p, x)))) by RightForall - have(relationDomain(uw) === initialSegment(p, x)) by Tautology.from(lastStep, extensionalityAxiom of (x -> relationDomain(uw), y -> initialSegment(p, x))) - - have(thesis) by Tautology.from(functionalOver.definition of (f -> uw, x -> initialSegment(p, x)), lastStep, uwfunctional) - } - - // z < x ==> uw z = F uw |^ z - have(in(z, initialSegment(p, x)) |- app(uw, z) === F(orderedRestriction(uw, z, p))) subproof { - assume(in(z, initialSegment(p, x))) - - // z in dom uw - have(in(z, relationDomain(uw))) subproof { - // \exists y. z < y < x as x is limit - // \exists g \in w. fun g y - // z in dom g - // so z in dom uw - have(in(pair(z, x), p2)) by Tautology.from(initialSegmentElement of (x -> z, y -> x), pIsAPartialOrder) - val yExists = have(exists(y, in(pair(z, y), p2) /\ in(pair(y, x), p2))) by Tautology.from(lastStep, initialSegmentUnionForLimitElementsIsComplete of t -> z, pIsATotalOrder) - - have(in(pair(z, y), p2) /\ in(pair(y, x), p2) |- in(z, relationDomain(uw))) subproof { - assume(in(pair(z, y), p2), in(pair(y, x), p2)) - - val zLTy = have(in(z, initialSegment(p, y))) by Tautology.from(initialSegmentElement of (x -> z, y -> y), pIsAPartialOrder) - val yLTx = have(in(y, initialSegment(p, x))) by Tautology.from(initialSegmentElement of (x -> y, y -> x), pIsAPartialOrder) - - have(thesis) by Tautology.from(zLTy, yLTx, zyInDomUw) - } - - thenHave(exists(y, in(pair(z, y), p2) /\ in(pair(y, x), p2)) |- in(z, relationDomain(uw))) by LeftExists - have(thesis) by Tautology.from(yExists, lastStep) - } - - have(thesis) by Tautology.from(lastStep, uwRestrictedEq) - } - - thenHave(in(z, initialSegment(p, x)) ==> (app(uw, z) === F(orderedRestriction(uw, z, p)))) by Restate - thenHave(forall(z, in(z, initialSegment(p, x)) ==> (app(uw, z) === F(orderedRestriction(uw, z, p))))) by RightForall - have(fun(uw, x)) by Tautology.from(lastStep, uwFunctionalOver) - thenHave(exists(g, fun(g, x))) by RightExists - } - - // if x has a predecessor, then we need to add an element to uw, giving us v as the requisite function - val successorCase = have(successorElement(p, x) |- exists(g, fun(g, x))) subproof { - assume(successorElement(p, x)) - // the right function is v = Uw \cup {(pred x, F Uw)} - // i.e., Uw with a recursive addition for the predecessor of x - // which is not included in any initial segment below x (! (pred x < y) for y < x) - // define pr as the predecessor of x - val pr = variable - val prFun = singleton(pair(pr, F(uw))) - val v = setUnion(uw, prFun) - - val vFun = have(predecessor(p, pr, x) |- fun(v, x)) subproof { - assume(predecessor(p, pr, x)) - // to this end, we show: - // 1. v is functional over (relationDomain(uw) === initialSegment(p, pr))) by Tautology.from( - functionalOver.definition of (f -> uw, x -> initialSegment(p, pr)), - uwfunctional - ) - - have(relationDomain(uw) === initialSegment(p, pr)) subproof { - val fwd = have(in(t, relationDomain(uw)) |- in(t, initialSegment(p, pr))) subproof { - assume(in(t, relationDomain(uw))) - - have(forall(t, in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g))))) by Tautology.from(uwfunctional, domainOfFunctionalUnion of z -> w) - thenHave(in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g)))) by InstantiateForall(t) - val gExists = thenHave(exists(g, in(g, w) /\ in(t, relationDomain(g)))) by Tautology - - have(in(g, w) /\ in(t, relationDomain(g)) |- in(t, initialSegment(p, pr))) subproof { - assume(in(g, w)) - assume(in(t, relationDomain(g))) - - have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall - val yExists = thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by Tautology - - have(in(y, initialSegment(p, x)) /\ fun(g, y) |- in(t, initialSegment(p, pr))) subproof { - assume(in(y, initialSegment(p, x)), fun(g, y)) - // t < y - val domEQ = have(relationDomain(g) === initialSegment(p, y)) by Tautology.from(functionalOver.definition of (f -> g, x -> initialSegment(p, y))) - have(in(t, relationDomain(g))) by Restate - val tLTy = thenHave(in(t, initialSegment(p, y))) by Substitution.ApplyRules(domEQ) - - // y <= pr - val cases = have((y === pr) \/ in(y, initialSegment(p, pr))) by Tautology.from(initialSegmentPredecessorSplit of (y -> pr, z -> y), pIsATotalOrder) - - val eqCase = have((y === pr) |- in(t, initialSegment(p, pr))) by Substitution.ApplyRules(y === pr)(tLTy) - val initCase = have(in(y, initialSegment(p, pr)) |- in(t, initialSegment(p, pr))) by Tautology.from(tLTy, initialSegmentTransitivity of (x -> t, y -> y, z -> pr), pIsAPartialOrder) - - have(thesis) by Tautology.from(cases, eqCase, initCase) - } - - thenHave(exists(y, in(y, initialSegment(p, x)) /\ fun(g, y)) |- in(t, initialSegment(p, pr))) by LeftExists - have(thesis) by Tautology.from(lastStep, yExists) - } - - thenHave(exists(g, in(g, w) /\ in(t, relationDomain(g))) |- in(t, initialSegment(p, pr))) by LeftExists - have(thesis) by Cut(gExists, lastStep) - } - - val bwd = have(in(t, initialSegment(p, pr)) |- in(t, relationDomain(uw))) subproof { - assume(in(t, initialSegment(p, pr))) - - have(in(pair(pr, x), p2)) by Tautology.from(predecessor.definition of (x -> pr, y -> x)) - val prLTx = have(in(pr, initialSegment(p, x))) by Tautology.from(lastStep, pIsAPartialOrder, initialSegmentElement of (y -> x, x -> pr)) - - have(forall(y, in(y, initialSegment(p, x)) ==> existsOne(g, fun(g, y)))) by Restate - thenHave(in(pr, initialSegment(p, x)) ==> existsOne(g, fun(g, pr))) by InstantiateForall(pr) - val gExists = have(exists(g, fun(g, pr))) by Tautology.from(lastStep, prLTx, existsOneImpliesExists of P -> lambda(g, fun(g, pr))) - - have(fun(g, pr) |- in(g, w) /\ in(t, relationDomain(g))) subproof { - assume(fun(g, pr)) - - have(in(pr, initialSegment(p, x)) /\ fun(g, pr)) by Tautology.from(prLTx) - val exPR = thenHave(exists(pr, in(pr, initialSegment(p, x)) /\ fun(g, pr))) by RightExists - - have(in(g, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(g, y))) by InstantiateForall - val gW = have(in(g, w)) by Tautology.from(lastStep, exPR) - - have(functionalOver(g, initialSegment(p, pr))) by Tautology - val domEQ = have(relationDomain(g) === initialSegment(p, pr)) by Tautology.from(lastStep, functionalOver.definition of (f -> g, x -> initialSegment(p, pr))) - - have(in(t, initialSegment(p, pr))) by Restate - thenHave(in(t, relationDomain(g))) by Substitution.ApplyRules(domEQ) - have(thesis) by Tautology.from(lastStep, gW) - } - - thenHave(fun(g, pr) |- exists(g, in(g, w) /\ in(t, relationDomain(g)))) by RightExists - thenHave(exists(g, fun(g, pr)) |- exists(g, in(g, w) /\ in(t, relationDomain(g)))) by LeftExists - val gInW = have(exists(g, in(g, w) /\ in(t, relationDomain(g)))) by Cut(gExists, lastStep) - - have(forall(t, in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g))))) by Tautology.from(uwfunctional, domainOfFunctionalUnion of z -> w) - thenHave(in(t, relationDomain(uw)) <=> exists(g, in(g, w) /\ in(t, relationDomain(g)))) by InstantiateForall(t) - have(thesis) by Tautology.from(lastStep, gInW) - } - - have(in(t, relationDomain(uw)) <=> in(t, initialSegment(p, pr))) by Tautology.from(fwd, bwd) - thenHave(forall(t, in(t, relationDomain(uw)) <=> in(t, initialSegment(p, pr)))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> relationDomain(uw), y -> initialSegment(p, pr))) - } - - have(thesis) by Tautology.from(lastStep, iffDom) - } - - // 1. v is functional over pr, y -> F(uw)), functionalOver.definition of (f -> prFun, x -> singleton(pr))) - - // 3. (t === pr)) by Weakening(singletonHasNoExtraElements of (y -> t, x -> pr)) - - val initMembership = have(in(t, initialSegment(p, pr)) <=> in(pair(t, pr), p2)) by Tautology.from(initialSegmentElement of (x -> t, y -> pr), pIsAPartialOrder) - - have(in(t, singleton(pr)) /\ in(t, initialSegment(p, pr)) |- ()) subproof { - assume(in(t, singleton(pr))) - assume(in(t, initialSegment(p, pr))) - - val tEQpr = have(t === pr) by Tautology.from(singletonMembership) - val tLTpr = have(in(pair(t, pr), p2)) by Tautology.from(initMembership) - val prprp2 = thenHave(in(pair(pr, pr), p2)) by Substitution.ApplyRules(tEQpr) - - // but the order is anti reflexive - have(forall(pr, in(pr, p1) ==> !in(pair(pr, pr), p2))) by Tautology.from(pIsAPartialOrder, partialOrder.definition, antiReflexive.definition of (r -> p2, x -> p1)) - thenHave(in(pr, p1) ==> !in(pair(pr, pr), p2)) by InstantiateForall(pr) - have(!in(pair(pr, pr), p2)) by Tautology.from(lastStep, predecessor.definition of (x -> pr, y -> x)) - - have(thesis) by Tautology.from(lastStep, prprp2) - } - - val inEmpty = thenHave((in(t, singleton(pr)) /\ in(t, initialSegment(p, pr))) ==> in(t, emptySet)) by Weakening - - have(in(t, setIntersection(initialSegment(p, pr), singleton(pr))) <=> (in(t, singleton(pr)) /\ in(t, initialSegment(p, pr)))) by Tautology.from( - setIntersectionMembership of (x -> initialSegment(p, pr), y -> singleton(pr)) - ) - have(in(t, setIntersection(initialSegment(p, pr), singleton(pr))) ==> in(t, emptySet)) by Substitution.ApplyRules(lastStep)(inEmpty) - thenHave(forall(t, in(t, setIntersection(initialSegment(p, pr), singleton(pr))) ==> in(t, emptySet))) by RightForall - have(subset(setIntersection(initialSegment(p, pr), singleton(pr)), emptySet)) by Tautology.from( - lastStep, - subsetAxiom of (x -> setIntersection(initialSegment(p, pr), singleton(pr)), y -> emptySet) - ) - have(thesis) by Tautology.from(lastStep, emptySetIsItsOwnOnlySubset of x -> setIntersection(initialSegment(p, pr), singleton(pr))) - } - - // 4. So v is functional over uw, a -> initialSegment(p, pr), g -> prFun, b -> singleton(pr)) - ) - - // 5. and = pr \/ < pr - val initBreakdown = - have(in(t, initialSegment(p, x)) <=> ((t === pr) \/ in(t, initialSegment(p, pr)))) by Tautology.from(initialSegmentPredecessorSplit of (z -> t, y -> pr), pIsATotalOrder) - - val singletonMembership = have(in(t, singleton(pr)) <=> (t === pr)) by Tautology.from(singletonHasNoExtraElements of (y -> t, x -> pr)) - - val initMembership = have(in(t, initialSegment(p, x)) <=> (in(t, singleton(pr)) \/ in(t, initialSegment(p, pr)))) by Substitution.ApplyRules(singletonMembership)(initBreakdown) - - have(in(t, setUnion(initialSegment(p, pr), singleton(pr))) <=> (in(t, singleton(pr)) \/ in(t, initialSegment(p, pr)))) by Tautology.from( - setUnionMembership of (z -> t, x -> initialSegment(p, pr), y -> singleton(pr)) - ) - have(in(t, initialSegment(p, x)) <=> in(t, setUnion(initialSegment(p, pr), singleton(pr)))) by Substitution.ApplyRules(lastStep)(initMembership) - thenHave(forall(t, in(t, initialSegment(p, x)) <=> in(t, setUnion(initialSegment(p, pr), singleton(pr))))) by RightForall - - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x -> initialSegment(p, x), y -> setUnion(initialSegment(p, pr), singleton(pr)))) - } - - have(thesis) by Substitution.ApplyRules(unionEQ)(vFunctionalOverUnion) - } - - // 2. v is recursive - val vRecursive = have(forall(z, in(z, initialSegment(p, x)) ==> (app(v, z) === F(orderedRestriction(v, z, p))))) subproof { - have(in(z, initialSegment(p, x)) |- (app(v, z) === F(orderedRestriction(v, z, p)))) subproof { - assume(in(z, initialSegment(p, x))) - - // z is in dom v - val zInDom = have(in(z, relationDomain(v))) subproof { - val domEQ = have(relationDomain(v) === initialSegment(p, x)) by Tautology.from(functionalOver.definition of (f -> v, x -> initialSegment(p, x)), vFunctionalOver) - have(in(z, initialSegment(p, x))) by Restate - thenHave(thesis) by Substitution.ApplyRules(domEQ) - } - - // app v z is well defined - val vAppDef = have((a === app(v, z)) <=> in(pair(z, a), v)) subproof { - have(functional(v)) by Tautology.from(vFunctionalOver, functionalOver.definition of (f -> v, x -> initialSegment(p, x))) - have(thesis) by Tautology.from(lastStep, zInDom, pairInFunctionIsApp of (f -> v, a -> z, b -> a)) - } - - // z is either the predecessor or below it - val zSplit = have(((z === pr) \/ in(z, initialSegment(p, pr)))) by Tautology.from(pIsATotalOrder, initialSegmentPredecessorSplit of (y -> pr)) - - // uw is actually the ordered restriction of v to pr - val uwEq = have((uw === orderedRestriction(v, pr, p))) subproof { - val vpr = orderedRestriction(v, pr, p) - - // uw has domain v, x -> initialSegment(p, x))) - have(functionalOver(restrictedFunction(v, initialSegment(p, pr)), setIntersection(initialSegment(p, pr), relationDomain(v)))) by Tautology.from( - lastStep, - restrictedFunctionIsFunctionalOver of (f -> v, x -> initialSegment(p, pr)) - ) - val vprFun = thenHave(functionalOver(vpr, setIntersection(initialSegment(p, pr), relationDomain(v)))) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> pr)) - - have(relationDomain(v) === initialSegment(p, x)) by Tautology.from(vFunctionalOver, functionalOver.definition of (f -> v, x -> initialSegment(p, x))) - val initIntersection = have(functionalOver(vpr, setIntersection(initialSegment(p, pr), initialSegment(p, x)))) by Substitution.ApplyRules(lastStep)(vprFun) - - have(setIntersection(initialSegment(p, pr), initialSegment(p, x)) === initialSegment(p, pr)) by Tautology.from( - predecessorInInitialSegment of y -> pr, - initialSegmentIntersection of y -> pr, - pIsAPartialOrder, - pIsATotalOrder - ) - have(thesis) by Substitution.ApplyRules(lastStep)(initIntersection) - } - - // and they agree on their domain - have(in(b, initialSegment(p, pr)) |- (app(uw, b) === app(vpr, b))) subproof { - assume(in(b, initialSegment(p, pr))) - - val appDefw = have((a === app(uw, b)) <=> in(pair(b, a), uw)) by Tautology.from(functionalOverApplication of (f -> uw, x -> initialSegment(p, pr), b -> a, a -> b), domUW) - val appDefv = have((a === app(vpr, b)) <=> in(pair(b, a), vpr)) by Tautology.from(functionalOverApplication of (f -> vpr, x -> initialSegment(p, pr), b -> a, a -> b), domRV) - - val fwd = have(in(pair(b, a), uw) |- in(pair(b, a), vpr)) subproof { - assume(in(pair(b, a), uw)) - - have(in(pair(b, a), v)) by Tautology.from(setUnionMembership of (z -> pair(b, a), x -> uw, y -> prFun)) - have(in(pair(b, a), restrictedFunction(v, initialSegment(p, pr)))) by Tautology.from( - lastStep, - restrictedFunctionPairMembership of (f -> v, x -> initialSegment(p, pr), t -> b, a -> a) - ) - thenHave(thesis) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> pr)) - } - - val bwd = have(in(pair(b, a), vpr) |- in(pair(b, a), uw)) subproof { - assume(in(pair(b, a), vpr)) - have(in(pair(b, a), vpr)) by Restate - thenHave(in(pair(b, a), restrictedFunction(v, initialSegment(p, pr)))) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> pr)) - have(in(pair(b, a), v)) by Tautology.from(lastStep, restrictedFunctionPairMembership of (f -> v, x -> initialSegment(p, pr), t -> b, a -> a)) - val cases = have(in(pair(b, a), uw) \/ in(pair(b, a), prFun)) by Tautology.from(lastStep, setUnionMembership of (z -> pair(b, a), x -> uw, y -> prFun)) - - have(!in(pair(b, a), prFun)) subproof { - // towards a contradiction, assume otherwise - assume(in(pair(b, a), prFun)) - - have(pair(b, a) === pair(pr, F(uw))) by Tautology.from(singletonHasNoExtraElements of (y -> pair(b, a), x -> pair(pr, F(uw)))) - val bEQpr = have(b === pr) by Tautology.from(lastStep, pairExtensionality of (a -> b, b -> a, c -> pr, d -> F(uw))) - - have(in(b, initialSegment(p, pr))) by Restate - thenHave(in(pr, initialSegment(p, pr))) by Substitution.ApplyRules(bEQpr) - have(bot) by Tautology.from(lastStep, initialSegmentIrreflexivity of (x -> pr), pIsAPartialOrder) - } - - have(thesis) by Tautology.from(lastStep, cases) - } - - have(in(pair(b, a), uw) <=> in(pair(b, a), vpr)) by Tautology.from(fwd, bwd) - have((a === app(uw, b)) <=> (a === app(vpr, b))) by Tautology.from(appDefw, appDefv, lastStep) - have(thesis) by Restate.from(lastStep of a -> app(uw, b)) - } - - thenHave(in(b, initialSegment(p, pr)) ==> (app(uw, b) === app(vpr, b))) by Restate - thenHave(forall(b, in(b, initialSegment(p, pr)) ==> (app(uw, b) === app(vpr, b)))) by RightForall - - have(thesis) by Tautology.from(functionsEqualIfEqualOnDomain of (f -> uw, g -> vpr, a -> initialSegment(p, pr)), lastStep, domUW, domRV) - } - - // the property holds for the predecessor - val prCase = have((z === pr) |- (app(v, z) === F(orderedRestriction(v, z, p)))) subproof { - have(app(v, pr) === F(orderedRestriction(v, pr, p))) subproof { - val fuwEq = have(F(uw) === F(orderedRestriction(v, pr, p))) subproof { - have(F(uw) === F(uw)) by Restate - thenHave(thesis) by Substitution.ApplyRules(uwEq) - } - - // app v pr = uw - val appEq = have(app(v, pr) === F(uw)) subproof { - val pairInV = have(in(pair(pr, F(uw)), v)) by Tautology.from( - setUnionMembership of (z -> pair(pr, F(uw)), x -> uw, y -> prFun), - singletonHasNoExtraElements of (x -> pair(pr, F(uw)), y -> pair(pr, F(uw))) - ) - have(in(pr, initialSegment(p, x))) by Tautology.from(predecessorInInitialSegment of y -> pr, pIsATotalOrder) - have(thesis) by Tautology.from(vAppDef of (a -> F(uw), z -> pr), lastStep, pairInV) - } - - have(thesis) by Tautology.from(equalityTransitivity of (x -> app(v, pr), y -> F(uw), z -> F(orderedRestriction(v, pr, p))), fuwEq, appEq) - } - - thenHave(thesis) by Substitution.ApplyRules(z === pr) - } - - // the property holds for elements < pr - val wCase = have(in(z, initialSegment(p, pr)) |- (app(v, z) === F(orderedRestriction(v, z, p)))) subproof { - assume(in(z, initialSegment(p, pr))) - - // uw is functional over z, x -> pr), pIsAPartialOrder) - - thenHave(thesis) by Substitution.ApplyRules(setIntersectionCommutativity of (x -> initialSegment(p, z), y -> initialSegment(p, pr))) - } - - have(orderedRestriction(orderedRestriction(v, pr, p), z, p) === orderedRestriction(orderedRestriction(v, pr, p), z, p)) by Restate - thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(orderedRestriction(v, pr, p), initialSegment(p, z))) by Substitution.ApplyRules( - ordBreakdown of (a -> orderedRestriction(v, pr, p), b -> z) - ) - thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(restrictedFunction(v, initialSegment(p, pr)), initialSegment(p, z))) by Substitution - .ApplyRules( - ordBreakdown of (a -> v, b -> pr) - ) - thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(v, setIntersection(initialSegment(p, pr), initialSegment(p, z)))) by Substitution - .ApplyRules( - restrictedFunctionAbsorption of (f -> v, x -> initialSegment(p, pr), y -> initialSegment(p, z)) - ) - thenHave(orderedRestriction(orderedRestriction(v, pr, p), z, p) === restrictedFunction(v, initialSegment(p, z))) by Substitution.ApplyRules(intersectionEQ) - thenHave(thesis) by Substitution.ApplyRules(ordBreakdown of (a -> v, b -> z)) - } - - have(thesis) by Tautology.from( - lastStep, - doubleRestriction, - equalityTransitivity of (x -> orderedRestriction(uw, z, p), y -> orderedRestriction(orderedRestriction(v, pr, p), z, p), z -> orderedRestriction(v, z, p)) - ) - } - - val restrictionFVW = have(F(orderedRestriction(v, z, p)) === F(orderedRestriction(uw, z, p))) subproof { - have(F(orderedRestriction(v, z, p)) === F(orderedRestriction(v, z, p))) by Restate - thenHave(thesis) by Substitution.ApplyRules(restrictionVW) - } - - // app v z = app uw z - val appVW = have(app(v, z) === app(uw, z)) subproof { - have(app(uw, z) === app(uw, z)) by Restate - val uwToOrd = thenHave(app(uw, z) === app(orderedRestriction(v, pr, p), z)) by Substitution.ApplyRules(uwEq) - - have(orderedRestriction(v, pr, p) === restrictedFunction(v, initialSegment(p, pr))) by InstantiateForall(orderedRestriction(v, pr, p))( - orderedRestriction.definition of (f -> v, a -> pr) - ) - val uwToRest = have(app(uw, z) === app(restrictedFunction(v, initialSegment(p, pr)), z)) by Substitution.ApplyRules(lastStep)(uwToOrd) - - have(app(restrictedFunction(v, initialSegment(p, pr)), z) === app(v, z)) by Tautology.from(restrictedFunctionApplication of (f -> v, x -> initialSegment(p, pr), y -> z)) - have(app(uw, z) === app(v, z)) by Substitution.ApplyRules(lastStep)(uwToRest) - - } - - // app uw z = F (uw |^ z) - val appRestrictionUW = have(app(uw, z) === F(orderedRestriction(uw, z, p))) subproof { - // z in dom uw - have(in(z, relationDomain(uw))) subproof { - // z < pr < x - // \exists g \in w. fun g pr - // z in dom g - // so z in dom uw - val zLTpr = have(in(z, initialSegment(p, pr))) by Restate - val prLTx = have(in(pr, initialSegment(p, x))) by Tautology.from(predecessorInInitialSegment of y -> pr, pIsATotalOrder) - - have(thesis) by Tautology.from(zLTpr, prLTx, zyInDomUw of y -> pr) - } - - have(thesis) by Tautology.from(lastStep, uwRestrictedEq) - } - - // equality transitivity - have(app(v, z) === F(orderedRestriction(uw, z, p))) by Tautology.from( - equalityTransitivity of (x -> app(v, z), y -> app(uw, z), z -> F(orderedRestriction(uw, z, p))), - appVW, - appRestrictionUW - ) - have(app(v, z) === F(orderedRestriction(v, z, p))) by Tautology.from( - equalityTransitivity of (x -> app(v, z), y -> F(orderedRestriction(uw, z, p)), z -> F(orderedRestriction(v, z, p))), - lastStep, - restrictionFVW - ) - } - - have(thesis) by Tautology.from(zSplit, prCase, wCase) - } - - thenHave(in(z, initialSegment(p, x)) ==> (app(v, z) === F(orderedRestriction(v, z, p)))) by Restate - thenHave(thesis) by RightForall - } - - have(fun(v, x)) by Tautology.from(vRecursive, vFunctionalOver) - } - - val prExists = have(exists(pr, predecessor(p, pr, x))) by Tautology.from(successorElement.definition) - - have(predecessor(p, pr, x) |- exists(g, fun(g, x))) by RightExists(vFun) - thenHave(exists(pr, predecessor(p, pr, x)) |- exists(g, fun(g, x))) by LeftExists - have(thesis) by Cut(prExists, lastStep) - } - - have(thesis) by Tautology.from(limSuccCases, limitCase, successorCase) - } - - val funExists = thenHave(exists(w, wDef) |- exists(g, fun(g, x))) by LeftExists - - have(exists(w, wDef)) subproof { - // restating the definition of w - // val wDef = forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))) - // forall t. t \in w <=> \exists y < x. fun(t, y) - - // we know that there exists a *unique* g for each y in the initial segment - // so, there is a functional map from the initial segment that produces these values - // by replacement, we can take the image of the initial segment - // restating replacement: - // forall(a, in(a, x) ==> existsOne(b, sPsi(x, a, b))) ==> - // exists(y, forall(b, in(b, y) <=> exists(a, in(a, x) /\ sPsi(x, a, b)))) - val sPsi = SchematicPredicateLabel("P", 3) - - have(forall(y, in(y, initialSegment(p, x)) ==> existsOne(g, fun(g, y)))) by Restate - have(exists(w, forall(t, in(t, w) <=> exists(y, in(y, initialSegment(p, x)) /\ fun(t, y))))) by Tautology.from( - lastStep, - strictReplacementSchema of (A -> initialSegment(p, x), R -> lambda((y, g), fun(g, y))) - ) - } - - have(thesis) by Tautology.from(lastStep, funExists) - } - - /** - * Theorem --- Well-Ordered Recursion - * - * Given any element `t` of a well order `p`, and a class function `F`, there - * exists a unique function `g` with ` existsOne(g, (functionalOver(g, initialSegment(p, t)) /\ forall(a, in(a, initialSegment(p, t)) ==> (app(g, a) === F(orderedRestriction(g, a, p)))))) - ) - ) { - assume(wellOrder(p)) - - val pIsAPartialOrder = have(partialOrder(p)) by Tautology.from(wellOrder.definition, totalOrder.definition) - val pIsATotalOrder = have(totalOrder(p)) by Tautology.from(wellOrder.definition) - - // the existence of g propagates up from initial segments - val initPropagate = have(in(x, p1) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x))) subproof { - - assume( - in(x, p1), - forall(y, in(y, initialSegment(p, x)) ==> prop(y)) - ) - - // see [[uniqueRecursiveFunction]] and [[recursiveFunctionExistencePropagates]] - - have(thesis) by Tautology.from(recursiveFunctionExistencePropagates, uniqueRecursiveFunction of t -> x) - } - - // so we induct on the well-ordering - thenHave(forall(x, in(x, p1) ==> (forall(y, in(y, initialSegment(p, x)) ==> prop(y)) ==> prop(x)))) by RightForall - have(thesis) by Tautology.from(lastStep, wellOrderedInduction of Q -> lambda(x, prop(x))) - } - show - - /** - * Theorem --- Transfinite Recursion - * - * Given any ordinal `a` and a class function `F`, there exists a unique - * function `g` with `a` as its domain, defined recursively as - * - * `\forall b < a. g(b) = F(g |^ b)` - * - * where `g |^ b` is `g` with its domain restricted to `b`. - */ - val transfiniteRecursion = Theorem( - ordinal(a) |- existsOne(g, functionalOver(g, a) /\ forall(b, in(b, a) ==> (app(g, b) === F(restrictedFunction(g, b))))) - ) { - sorry - } - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala deleted file mode 100644 index 00c85ab76..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/Segments.scala +++ /dev/null @@ -1,225 +0,0 @@ -package lisa.maths.settheory.orderings - -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.Quantifiers.* -import lisa.maths.settheory.SetTheory.* -import lisa.maths.settheory.functions.* -import lisa.maths.settheory.orderings.InclusionOrders.* -import lisa.maths.settheory.orderings.PartialOrders.* -import lisa.maths.settheory.orderings.WellOrders.* - -import Ordinals.* - -object Segments extends lisa.Main { - - // var defs - private val w = variable - private val x = variable - private val y = variable - private val z = variable - private val h = formulaVariable - private val t = variable - private val a = variable - private val b = variable - private val c = variable - private val d = variable - - // relation and function symbols - private val r = variable - private val p = variable - private val q = variable - private val f = variable - private val g = variable - private val F = function[1] - private val G = function[2] - - private val P = predicate[1] - private val Q = predicate[1] - private val schemPred = predicate[1] - - val initialSegmentUniqueness = Lemma( - existsOne(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ in(pair(t, a), secondInPair(p))))) - ) { - have(thesis) by UniqueComprehension(firstInPair(p), lambda(t, in(pair(t, a), secondInPair(p)))) - } - - val initialSegment = DEF(p, a) --> The(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ in(pair(t, a), secondInPair(p)))))(initialSegmentUniqueness) - - val initialSegmentLeqUniqueness = Lemma( - existsOne(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ (in(pair(t, a), secondInPair(p)) \/ (t === a))))) - ) { - have(thesis) by UniqueComprehension(firstInPair(p), lambda(t, (in(pair(t, a), secondInPair(p)) \/ (t === a)))) - } - - val initialSegmentLeq = DEF(p, a) --> The(z, forall(t, in(t, z) <=> (in(t, firstInPair(p)) /\ (in(pair(t, a), secondInPair(p)) \/ (t === a)))))(initialSegmentLeqUniqueness) - - val initialSegmentOrderUniqueness = Lemma( - existsOne(z, forall(t, in(t, z) <=> (in(t, secondInPair(p)) /\ (in(firstInPair(t), initialSegment(p, a)) /\ in(secondInPair(t), initialSegment(p, a)))))) - ) { - have(thesis) by UniqueComprehension(secondInPair(p), lambda(t, (in(firstInPair(t), initialSegment(p, a)) /\ in(secondInPair(t), initialSegment(p, a))))) - } - - val initialSegmentOrder = - DEF(p, a) --> The(z, forall(t, in(t, z) <=> (in(t, secondInPair(p)) /\ (in(firstInPair(t), initialSegment(p, a)) /\ in(secondInPair(t), initialSegment(p, a))))))(initialSegmentOrderUniqueness) - - val initialSegmentElement = Lemma( - partialOrder(p) |- in(pair(x, y), secondInPair(p)) <=> in(x, initialSegment(p, y)) - ) { - assume(partialOrder(p)) - - val p1 = firstInPair(p) - val p2 = secondInPair(p) - - val fwd = have(in(pair(x, y), p2) |- in(x, initialSegment(p, y))) subproof { - assume(in(pair(x, y), p2)) - - // p2 is a relation on p1 - have(relationBetween(p2, p1, p1)) by Tautology.from(partialOrder.definition) - val xInp1 = have(in(x, p1)) by Tautology.from(lastStep, pairInRelation of (r -> p2, a -> p1, b -> p1)) - have(forall(x, in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2)))) by InstantiateForall(initialSegment(p, y))(initialSegment.definition of a -> y) - thenHave(in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2))) by InstantiateForall(x) - - have(thesis) by Tautology.from(xInp1, lastStep) - } - - val bwd = have(in(x, initialSegment(p, y)) |- in(pair(x, y), p2)) subproof { - have(forall(x, in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2)))) by InstantiateForall(initialSegment(p, y))(initialSegment.definition of a -> y) - thenHave(in(x, initialSegment(p, y)) <=> (in(x, p1) /\ in(pair(x, y), p2))) by InstantiateForall(x) - thenHave(thesis) by Tautology - } - - have(thesis) by Tautology.from(fwd, bwd) - } - - val initialSegmentBaseElement = Lemma( - partialOrder(p) /\ in(x, initialSegment(p, y)) |- in(x, firstInPair(p)) - ) { - assume(partialOrder(p)) - assume(in(x, initialSegment(p, y))) - - val p1 = firstInPair(p) - val p2 = secondInPair(p) - - val pairInp2 = have(in(pair(x, y), p2)) by Tautology.from(initialSegmentElement) - have(relationBetween(p2, p1, p1)) by Tautology.from(partialOrder.definition) - have(in(x, p1)) by Tautology.from(lastStep, pairInp2, pairInRelation of (r -> p2, a -> p1, b -> p1)) - } - - val initialSegmentIrreflexivity = Lemma( - partialOrder(p) |- !in(x, initialSegment(p, x)) - ) { - sorry - } - - val predecessorInInitialSegment = Lemma( - totalOrder(p) /\ predecessor(p, y, x) |- in(y, initialSegment(p, x)) - ) { - assume(totalOrder(p)) - assume(predecessor(p, y, x)) - - have(in(pair(y, x), secondInPair(p))) by Tautology.from(predecessor.definition of (x -> y, y -> x)) - have(in(y, initialSegment(p, x))) by Tautology.from(lastStep, totalOrder.definition, initialSegmentElement of (x -> y, y -> x)) - } - - val initialSegmentsSubset = Lemma( - partialOrder(p) /\ in(pair(x, y), secondInPair(p)) |- subset(initialSegment(p, x), initialSegment(p, y)) - ) { - // t \in t \in x, y -> y)) - val yz = have(in(pair(y, z), secondInPair(p))) by Tautology.from(initialSegmentElement of (x -> y, y -> z)) - - have(forall(x, forall(y, forall(z, (in(pair(x, y), secondInPair(p)) /\ in(pair(y, z), secondInPair(p))) ==> in(pair(x, z), secondInPair(p)))))) by Tautology.from( - partialOrder.definition, - transitive.definition of (r -> secondInPair(p), x -> firstInPair(p)) - ) - thenHave((in(pair(x, y), secondInPair(p)) /\ in(pair(y, z), secondInPair(p))) ==> in(pair(x, z), secondInPair(p))) by InstantiateForall(x, y, z) - have(in(pair(x, z), secondInPair(p))) by Tautology.from(lastStep, xy, yz) - - have(thesis) by Tautology.from(lastStep, initialSegmentElement of (x -> x, y -> z)) - } - - val initialSegmentLeqBreakdown = Lemma( - in(t, initialSegmentLeq(p, a)) <=> (in(t, initialSegment(p, a)) \/ (t === a)) - ) { - sorry - } - - val initialSegmentOrderTotal = Lemma( - totalOrder(p) /\ in(a, firstInPair(p)) |- total(initialSegmentOrder(p, a), initialSegment(p, a)) - ) { - sorry - } - - // initial segment of well order is well ordered under the restricted order - val initialSegmentWellOrdered = Lemma( - wellOrder(p) /\ in(a, firstInPair(p)) |- wellOrder(pair(initialSegment(p, a), initialSegmentOrder(p, a))) - ) { - - sorry - } - - val initialSegmentPredecessorSplit = Lemma( - totalOrder(p) /\ predecessor(p, y, x) |- in(z, initialSegment(p, x)) <=> ((z === y) \/ in(z, initialSegment(p, y))) - ) { - sorry - } - - val initialSegmentIntersection = Lemma( - partialOrder(p) /\ in(y, initialSegment(p, x)) |- setIntersection(initialSegment(p, y), initialSegment(p, x)) === initialSegment(p, y) - ) { - sorry - } - - /** - * The restriction of a function `f` with respect to `a` relative to a - * partial order `p = (X, <)`. The result is `f` with its domain restricted - * to the elements less than `a` wrt `<`. - */ - val orderedRestriction = DEF(f, a, p) --> restrictedFunction(f, initialSegment(p, a)) - - /** - * Theorem --- Ordered Restriction Membership - * - * A pair `b` is in the ordered restriction of a function `f` to the initial - * segment of `a` under a partial order `p` iff it is in `f` and its first element - * (the one in the domain) is in the initial segment of `a` - */ - val orderedRestrictionMembership = Lemma( - partialOrder(p) |- in(b, orderedRestriction(f, a, p)) <=> (in(b, f) /\ in(firstInPair(b), initialSegment(p, a))) - ) { - sorry - } - - val orderedRestrictionFunctionalOverInit = Lemma( - in(a, initialSegment(p, b)) /\ functionalOver(f, initialSegment(p, b)) |- functionalOver(orderedRestriction(f, a, p), initialSegment(p, a)) - ) { - sorry - } - - val orderedRestrictionAgreement = Lemma( - partialOrder(p) /\ in(b, initialSegment(p, a)) /\ in(b, relationDomain(f)) /\ in(b, relationDomain(g)) |- (app(orderedRestriction(f, a, p), b) === app(orderedRestriction(g, a, p), b)) <=> (app( - f, - b - ) === app(g, b)) - ) { - sorry - } - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala deleted file mode 100644 index 01e68326c..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/orderings/WellOrders.scala +++ /dev/null @@ -1,140 +0,0 @@ -package lisa.maths.settheory.orderings - -import lisa.automation.settheory.SetTheoryTactics.* -import lisa.maths.Quantifiers.* -import lisa.maths.settheory.SetTheory.* -import lisa.maths.settheory.orderings.InclusionOrders.* -import lisa.maths.settheory.orderings.PartialOrders.* - -object WellOrders extends lisa.Main { - - // var defs - private val w = variable - private val x = variable - private val y = variable - private val z = variable - private val h = formulaVariable - private val t = variable - private val a = variable - private val b = variable - private val c = variable - private val d = variable - - // relation and function symbols - private val r = variable - private val p = variable - private val q = variable - private val f = variable - private val g = variable - private val F = function[1] - private val G = function[2] - - private val P = predicate[1] - private val Q = predicate[1] - private val schemPred = predicate[1] - - /** - * Well-Order --- a partial order `p = (A, <)` is said to be a well-order if - * it is total and if every subset of `A` has a least element under `<`. - */ - val wellOrder = DEF(p) --> { - val A = firstInPair(p) - val B = variable - val ` exists(z, in(z, B) /\ forall(x, in(x, B) ==> (in(pair(z, x), ` (totalOrder(pair(emptySet, emptySet)) /\ forall( - b, - (subset(b, emptySet) /\ !(b === emptySet)) ==> exists(z, in(z, b) /\ forall(x, in(x, b) ==> (in(pair(z, x), secondInPair(pair(emptySet, emptySet))) \/ (z === x)))) - )) - ) by Substitution.ApplyRules(firstInPairReduction of (x -> emptySet, y -> emptySet))(wellOrder.definition of p -> pair(emptySet, emptySet)) - - have((subset(b, emptySet) /\ !(b === emptySet)) ==> exists(z, in(z, b) /\ forall(x, in(x, b) ==> (in(pair(z, x), secondInPair(pair(emptySet, emptySet))) \/ (z === x))))) by Tautology.from( - emptySetIsItsOwnOnlySubset of x -> b - ) - thenHave( - forall( - b, - (subset(b, emptySet) /\ !(b === emptySet)) ==> exists(z, in(z, b) /\ forall(x, in(x, b) ==> (in(pair(z, x), secondInPair(pair(emptySet, emptySet))) \/ (z === x)))) - ) - ) by RightForall - - have(thesis) by Tautology.from(lastStep, woDef, emptySetTotalOrder) - } - - /** - * Useful inherited properties - */ - - /** - * Well-orders inherit relational-transitivity from being partial orders. - */ - val wellOrderTransitivity = Lemma( - wellOrder(p) |- forall(w, forall(y, forall(z, (in(pair(w, y), secondInPair(p)) /\ in(pair(y, z), secondInPair(p))) ==> in(pair(w, z), secondInPair(p))))) - ) { - have(thesis) by Tautology.from(wellOrder.definition, totalOrder.definition, partialOrder.definition, transitive.definition of (r -> secondInPair(p), x -> firstInPair(p))) - } - - val transitiveSet = DEF(x) --> forall(y, in(y, x) ==> subset(y, x)) - - /** - * Theorem --- the definition of a transitive set in terms of inclusion is - * equivalent to the subset based definition. - */ - val transitiveSetInclusionDef = Theorem( - () |- transitiveSet(x) <=> forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) - ) { - // prove defs equal - have(forall(y, in(y, x) ==> subset(y, x)) <=> forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) subproof { - val lhs = have(forall(y, in(y, x) ==> subset(y, x)) |- forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) subproof { - have(forall(y, in(y, x) ==> subset(y, x)) |- forall(y, in(y, x) ==> subset(y, x))) by Hypothesis - thenHave((forall(y, in(y, x) ==> subset(y, x)), in(y, x)) |- subset(y, x)) by InstantiateForall(y) - thenHave((forall(y, in(y, x) ==> subset(y, x)), in(y, x)) |- forall(z, in(z, y) ==> in(z, x))) by Substitution.ApplyRules(subsetAxiom of (x -> y, y -> x)) - thenHave((forall(y, in(y, x) ==> subset(y, x)), in(y, x)) |- in(z, y) ==> in(z, x)) by InstantiateForall(z) - thenHave((forall(y, in(y, x) ==> subset(y, x))) |- (in(z, y) /\ in(y, x)) ==> in(z, x)) by Restate - thenHave((forall(y, in(y, x) ==> subset(y, x))) |- forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) by RightForall - thenHave((forall(y, in(y, x) ==> subset(y, x))) |- forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) by RightForall - } - - val rhs = have(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- forall(y, in(y, x) ==> subset(y, x))) subproof { - have(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x)))) by Hypothesis - thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) by InstantiateForall(z) - thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) /\ in(y, x) |- (in(z, y)) ==> in(z, x)) by InstantiateForall(y) - thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) /\ in(y, x) |- forall(z, in(z, y) ==> in(z, x))) by RightForall - thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) /\ in(y, x) |- subset(y, x)) by Substitution.ApplyRules(subsetAxiom of (x -> y, y -> x)) - thenHave(forall(z, forall(y, (in(z, y) /\ in(y, x)) ==> in(z, x))) |- in(y, x) ==> subset(y, x)) by Restate - thenHave(thesis) by RightForall - } - - have(thesis) by Tautology.from(lhs, rhs) - } - - have(thesis) by Tautology.from(lastStep, transitiveSet.definition) - } - - val inclusionOnTransitiveSetIsPartialOrder = Theorem( - transitiveSet(a) |- partialOrder(inclusionOrderOn(a)) - ) { - - // inclusion is a relation - // anti reflexive - // anti symmetric - have(relationBetween(inclusionOn(a), a, a) /\ antiReflexive(inclusionOn(a), a) /\ antiSymmetric(inclusionOn(a), a)) by Tautology.from( - inclusionIsRelation, - inclusionIsAntiReflexive, - inclusionIsAntiSymmetric - ) - - // and transitive - - sorry - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala deleted file mode 100644 index 0fbc4b1c1..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala +++ /dev/null @@ -1,530 +0,0 @@ -package lisa.maths.settheory.types - -import lisa.prooflib.ProofTacticLib.* -import lisa.fol.FOL -import lisa.automation.Tautology -import lisa.fol.FOL.{*, given} -import lisa.prooflib.BasicStepTactic.* -import lisa.prooflib.SimpleDeducedSteps.* -import lisa.SetTheoryLibrary.{given, *} -import lisa.SetTheoryLibrary -import lisa.kernel.proof.SequentCalculus.SCProofStep -import lisa.prooflib.OutputManager -import lisa.maths.settheory.SetTheory.singleton -import lisa.maths.settheory.functions.{functional, app, functionFromApplication, |=>} - -import annotation.nowarn - -object TypeLib extends lisa.Main { - import TypeSystem.* - - val f = variable - val x = variable - val y = variable - val a = variable - val z = variable - val A = variable - val B = variable - val F = function[1] - val any = DEF(x) --> top - - - // A |=> B is the set of functions from A to B - // C |> D is the functional class of functionals from the class C to the class D - // F is C |> D desugars into ∀(x, (x is C) => (F(x) is D)) - - val testTheorem = Theorem((x `is` A, f `is` (A |=> B), F `is` (A |=> B) |> (A |=> B) ) |- (F(f)*(x) `is` B)) { - have(thesis) by TypeChecker.prove - } - - val 𝔹 = DEF() --> unorderedPair(∅, singleton(∅)) - - val empty_in_B = Theorem( (∅ :: 𝔹) ) { - have( ∅ :: unorderedPair(∅, singleton(∅))) by Tautology.from(pairAxiom of (z := ∅, x := ∅, y := singleton(∅))) - thenHave(thesis ) by Substitution.ApplyRules(𝔹.shortDefinition) - } - - val sing_empty_in_B = Theorem( (singleton(∅) :: 𝔹) ) { - have( singleton(∅) :: unorderedPair(∅, singleton(∅))) by Tautology.from(pairAxiom of (z := singleton(∅), x := ∅, y := singleton(∅))) - thenHave(thesis ) by Substitution.ApplyRules(𝔹.shortDefinition) - } - - val Zero = DEF() --> (∅.typedWith(𝔹)(empty_in_B), 𝔹) - - val One = { - val One = DEF() --> singleton(∅) - val One_in_B = Theorem( (One :: 𝔹) ) { - have(thesis) by Substitution.ApplyRules(One.shortDefinition)(sing_empty_in_B) - } - One.typedWith(𝔹)(One_in_B) - } - - val zero_in_B = Theorem( (Zero :: 𝔹) ) { - have( Zero :: 𝔹) by TypeChecker.prove - } - - -} - -object TypeSystem { - - import TypeLib.{ - f, x, y, a, any, definition, given - } - - type Class = Term | (Term**1 |-> Formula) - - type SmallClass = Term - - case class FunctionalClass(in: Seq[Class], args: Seq[Variable], out: Class, arity: Int) { - def formula[N <: Arity](f: (Term**N |-> Term)): Formula = - val inner = (args.zip(in.toSeq).map((term, typ) => (term `is` typ).asFormula).reduceLeft((a, b) => a /\ b)) ==> (f.applySeq(args) `is` out) - args.foldRight(inner)((v, form) => forall(v, form)) - - override def toString(): String = in.map(_.toStringSeparated()).mkString("(", ", ", ")") + " |> " + out.toStringSeparated() - } - object FunctionalClass { - def apply(in: Seq[Class], out: Class, arity: Int): FunctionalClass = FunctionalClass(in, Seq.empty, out, arity) - } - - extension [N <: Arity](f: (Term**N |-> Term)) { - def is (typ: FunctionalClass): FunctionalTypeAssignment[N] & Formula = FunctionalTypeAssignment[N](f, typ).asInstanceOf - } - - extension[A <: Class] (c: Class) { - //(1 to arity).map(i => freshVariable(in.toSeq :+ out, "%x" + i)) - def |>(out: Class): FunctionalClass = FunctionalClass(Seq(c),Seq(freshVariable(Seq(out), "$x")), out, 1) - def |>(out: FunctionalClass): FunctionalClass = - val newVar = freshVariable(out.in.toSeq :+ out.out, "$x") - FunctionalClass(c +: out.in, newVar +: out.args, out.out, out.arity+1) - def toStringSeparated(): String = c match - case t: Term => t.toStringSeparated() - case f: (Term**1 |-> Formula) @unchecked => f.toString() - } - - extension [A <: Class](t: Term) { - def is(clas:A): Formula & TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula & TypeAssignment[A]] - def ::(clas:A): Formula & TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula & TypeAssignment[A]] - def @@(t2: Term): AppliedFunction = AppliedFunction(t, t2) - def *(t2: Term): AppliedFunction = AppliedFunction(t, t2) - } - - - object * {def unapply(t: Term): Option[(Term, Term)] = t match { - case AppliedFunction(f, a) => Some((f, a)) - case app(f, a) => Some((f, a)) - case _ => None - }} - - - /** - * A type assumption is a pair of a variable and a type. - * It is also a formula, equal to the type applied to the variable. - */ - sealed trait TypeAssignment[A <: Class]{ - this: Formula => - val t: Term - val typ: A - val asFormula: Formula = this - - override def toString() = - t.toStringSeparated() + "::" + typ.toStringSeparated() - override def toStringSeparated(): String = "(" + toString() + ")" - } - object TypeAssignment { - - /** - * A type assumption is a pair of a variable and a type. - * It is also a formula, equal to the type applied to the variable. - */ - def apply[A <: Class](t: Term, typ:A): TypeAssignment[A] = - val form = typ match - case f: Term => in(t, f) - case f : (Term**1 |-> Formula) @unchecked => - ((f: Term**1 |-> Formula)(t): Formula) - form match - case f: VariableFormula => - throw new IllegalArgumentException("Class formula cannot be a variable formula, as we require a type to have no free variable.") - case f: ConstantFormula => new TypeAssignmentConstant(t, typ, f) - case f: AppliedPredicate => new TypeAssignmentPredicate(t, typ, f) - case f: AppliedConnector => new TypeAssignmentConnector(t, typ, f) - case f: BinderFormula => new TypeAssignmentBinder(t, typ, f) - - def unapply(ta: Formula): Option[(Term, Class)] = ta match - case ta: TypeAssignment[?] => Some((ta.t, ta.typ)) - case in(x, set) => Some((x, set)) - case AppliedPredicate(label, args) if label.arity == 1 => Some((args.head, label.asInstanceOf[Term**1 |-> Formula])) - case _ => None - - } - val is = TypeAssignment - - given [A <: Class]: Conversion[TypeAssignment[A], Formula] = _.asInstanceOf[Formula] - /* - given [A <: Class]: Conversion[TypeAssignment[A], Sequent] = ta => Sequent(Set.empty, Set(ta)) - given [A <: Class]: FormulaSetConverter[TypeAssignment[A]] with { - override def apply(f: TypeAssignment[A]): Set[Formula] = Set(f.asInstanceOf[Formula]) - } -*/ - - - private class TypeAssignmentConstant[A <: Class](val t: Term, val typ:A, formula: ConstantFormula) extends ConstantFormula(formula.id) with TypeAssignment[A] - private class TypeAssignmentPredicate[A <: Class](val t: Term, val typ:A, formula: AppliedPredicate) extends AppliedPredicate(formula.label, formula.args) with TypeAssignment[A] { - - override def substituteUnsafe(map: Map[FOL.SchematicLabel[?], FOL.LisaObject[?]]): FOL.Formula = - if map.keySet.exists(_ == typ) then super.substituteUnsafe(map) - else - val newArgs = args.map(_.substituteUnsafe(map)) - if newArgs == args then this else - val newTyp = (typ: LisaObject[?]).substituteUnsafe(map).asInstanceOf[A] - - TypeAssignmentPredicate(t.substituteUnsafe(map), newTyp, formula.copy(args = newArgs)) - } - private class TypeAssignmentConnector[A <: Class](val t: Term, val typ:A, formula: AppliedConnector) extends AppliedConnector(formula.label, formula.args) with TypeAssignment[A] - private class TypeAssignmentBinder[A <: Class](val t: Term, val typ:A, formula: BinderFormula) extends BinderFormula(formula.f, formula.bound, formula.body) with TypeAssignment[A] - - - type TypingContext = Iterable[TypeAssignment[?]] - - - sealed trait FunctionalTypeAssignment[N <: Arity]{ - this: Formula => - val functional: Term**N |-> Term - val typ: FunctionalClass - val asFormula: Formula = this - - - override def toString() = functional.toString() + " : " + typ.toString() - override def toStringSeparated(): String = "(" + toString + ")" - - } - object FunctionalTypeAssignment { - def apply[N <: Arity](functional: Term**N |-> Term, typ: FunctionalClass): FunctionalTypeAssignment[N] = - val form = typ.formula(functional) - form match - case fo: VariableFormula => - throw new IllegalArgumentException("Class formula cannot be a variable formula, as we require a type to have no free variable.") - case fo: ConstantFormula => new FunctionalTypeAssignmentConstant(functional, typ, fo) - case fo: AppliedPredicate => new FunctionalTypeAssignmentPredicate(functional, typ, fo) - case fo: AppliedConnector => new FunctionalTypeAssignmentConnector(functional, typ, fo) - case fo: BinderFormula => new FunctionalTypeAssignmentBinder(functional, typ, fo) - - def unapply[N <: Arity](f: Formula): Option[((Term ** N) |-> Term, FunctionalClass)] = - f match - case ta: FunctionalTypeAssignment[N] @unchecked => Some((ta.functional, ta.typ)) - case _ => None - } - private class FunctionalTypeAssignmentConstant[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: ConstantFormula) extends ConstantFormula(formula.id) with FunctionalTypeAssignment[N] - private class FunctionalTypeAssignmentPredicate[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: AppliedPredicate) extends AppliedPredicate(formula.label, formula.args) with FunctionalTypeAssignment[N] - private class FunctionalTypeAssignmentConnector[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: AppliedConnector) extends AppliedConnector(formula.label, formula.args) with FunctionalTypeAssignment[N] - private class FunctionalTypeAssignmentBinder[N <: Arity](val functional: Term**N |-> Term, val typ: FunctionalClass, formula: BinderFormula) extends BinderFormula(formula.f, formula.bound, formula.body) with FunctionalTypeAssignment[N] - - - - - - class TypedConstant[A <: Class] - (id: Identifier, val typ: A, val justif: JUSTIFICATION) extends Constant(id) with LisaObject[TypedConstant[A]] { - val formula = TypeAssignment(this, typ) - assert(justif.statement.left.isEmpty && (justif.statement.right.head == formula)) - - override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): TypedConstant[A] = this - } - - // Function Labels - - - - - - class TypedConstantFunctional[N <: Arity]( - id : Identifier, - arity: N, - val typ: FunctionalClass, - val justif: JUSTIFICATION - ) extends ConstantFunctionLabel[N](id, arity) with LisaObject[TypedConstantFunctional[N]] { - - override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): TypedConstantFunctional[N] = this - } - - - - - - class AppliedFunction(val func: Term, val arg: Term) extends AppliedFunctional(app, Seq(func, arg)) with LisaObject[AppliedFunction] { - - override def substituteUnsafe(map: Map[lisa.fol.FOL.SchematicLabel[?], lisa.fol.FOL.LisaObject[?]]): AppliedFunction = AppliedFunction(func.substituteUnsafe(map), arg.substituteUnsafe(map)) - - override def toString(): String = - func match - case AppliedFunction(af @ AppliedFunctional(label, args), t1) if label.id.name == "=:=" => - s"(${t1.toStringSeparated()} =:=_${args.head.toStringSeparated()} ${arg.toStringSeparated()})" - case _ => - func.toStringSeparated() + "*(" + arg.toStringSeparated() + ")" - override def toStringSeparated(): String = toString() - } - object AppliedFunction { - def unapply(af: AppliedFunctional): Option[(Term, Term)] = af match - case AppliedFunctional(label, Seq(func, arg)) if label == app => Some((func, arg)) - case _ => None - } - - - - /////////////////////// - ///// Definitions ///// - /////////////////////// - - - class TypedSimpleConstantDefinition[A <: Class](using om: OutputManager)(fullName: String, line: Int, file: String)( - val expression: Term, - out: Variable, - j: JUSTIFICATION, - val typ:A - ) extends SimpleFunctionDefinition[0](fullName, line, file)(lambda[Term, Term](Seq[Variable](), expression).asInstanceOf, out, j) { - val typingName = "typing_" + fullName - val typingJudgement = THM( label :: typ, typingName, line, file, InternalStatement)({ - have(expression :: typ) by TypeChecker.prove - thenHave(thesis) by lisa.automation.Substitution.ApplyRules(getShortDefinition(label).get) - }) - val typedLabel: TypedConstant[A] = TypedConstant(label.id, typ, typingJudgement) - - - } - object TypedSimpleConstantDefinition { - def apply[A <: Class](using om: OutputManager)(fullName: String, line: Int, file: String)(expression: Term, typ:A): TypedSimpleConstantDefinition[A] = { - val intName = "definition_" + fullName - val out = Variable(freshId(expression.allSchematicLabels.map(_.id), "y")) - val defThm = THM(ExistsOne(out, out === expression), intName, line, file, InternalStatement)({ - have(lisa.prooflib.SimpleDeducedSteps.simpleFunctionDefinition(lambda(Seq[Variable](), expression), out)) - }) - new TypedSimpleConstantDefinition(fullName, line, file)(expression, out, defThm, typ) - } - } - - - extension (d: definitionWithVars[0]) { - @nowarn - inline infix def -->[A<:Class]( - using om: OutputManager, name: sourcecode.FullName, line: sourcecode.Line, file: sourcecode.File)(term:Term, typ: A): TypedConstant[A] = - TypedSimpleConstantDefinition[A](name.value, line.value, file.value)(term, typ).typedLabel - } - - - extension (c: Constant) { - def typedWith[A <: Class](typ:A)(justif: JUSTIFICATION) : TypedConstant[A] = - if justif.statement.right.size != 1 || justif.statement.left.size != 0 || !K.isSame((c `is` typ).asFormula.underlying, justif.statement.right.head.underlying) then - throw new IllegalArgumentException(s"A proof of typing of $c must be of the form ${c :: typ}, but the given justification shows ${justif.statement}.") - else TypedConstant(c.id, typ, justif) - } - - - - - - - - - ///////////////////////// - ///// Type Checking ///// - ///////////////////////// - - object TypeChecker extends ProofTactic { - private val x = variable - - class TypingException(val msg: String) extends Exception(msg) - - def prove(using proof: SetTheoryLibrary.Proof)(bot:lisa.fol.FOL.Sequent): proof.ProofTacticJudgement = - val context = bot.left - var success: proof.ProofTacticJudgement = null - var typingError: proof.ProofTacticJudgement = null - bot.right.find(goal => - goal match - case (term `is` typ) => - val ptj = typecheck(using SetTheoryLibrary)(context.toSeq, term, Some(typ)) - if ptj.isValid then - success = ptj - true - else - typingError = ptj - false - case _ => false - ) - if success != null then success else if typingError != null then typingError else proof.InvalidProofTactic("The right hand side of the goal must be a typing judgement") - - private def fullFlat(context: Seq[Formula]): Seq[Formula] = context.flatMap{ - case AppliedConnector(And, cunj) => fullFlat(cunj) - case f => Seq(f) - } - - def typecheck(using lib: SetTheoryLibrary.type, proof: lib.Proof)(context: Seq[Formula], term:Term, typ: Option[Class]): proof.ProofTacticJudgement = - val typingAssumptions: Map[Term, Seq[Class]] = fullFlat(context).collect{ - case TypeAssignment(term, typ) => (term, typ) - }.groupBy(_._1).map((t, l) => (t, l.map(_._2))) - - val functionalTypingAssumptions: Map[(? |-> Term), Seq[FunctionalClass]] = context.collect{ - case FunctionalTypeAssignment(func, typ) => (func, typ) - }.groupBy(_._1).map((func, l) => (func, l.map(_._2))) - - TacticSubproof { - context.foreach(assume(_)) - try { - - def innerTypecheck(context2: Map[Term, Seq[Class]], term:Term, typ:Option[Class]): Class= { - val possibleTypes = typingAssumptions.getOrElse(term, Nil) - if typ == Some(any) then - have(term `is` any) by Restate.from(TypeLib.any.definition of (x := term)) - any - else if typ.isEmpty && possibleTypes.size >=1 then - have(term `is` possibleTypes.head) by Restate - possibleTypes.head - else if (typ.nonEmpty && possibleTypes.contains(typ.get)) then - have(term `is` typ.get) by Restate - typ.get - else term match - case tc: TypedConstant[?] => - if typ.isEmpty then - have(tc `is` tc.typ) by Restate.from(tc.justif) - tc.typ - else if K.isSame((tc `is` typ.get).asFormula.underlying, (tc `is` tc.typ).asFormula.underlying) then - have(tc `is` typ.get) by Restate.from(tc.justif) - typ.get - else throw TypingException("Constant " + tc + " expected to be of type " + typ + " but has type " + tc.typ + ".") - - case AppliedFunction(func, arg) => - val funcType = innerTypecheck(context2, func, None) - val funcProof = lastStep - val argType = innerTypecheck(context2, arg, None) - val argProof = lastStep - funcType match - case inType |=> outType => typ match - case None => - if K.isSame((arg `is` inType).asFormula.underlying, (arg `is` argType).asFormula.underlying) then - have(term `is` outType) by Tautology.from( - functionFromApplication of (f := func, a := arg, x := inType, y := outType), - funcProof, - argProof - ) - outType - else throw - TypingException("Function " + func + " found to have type " + funcType + ", but argument " + arg + " has type " + argType + " instead of expected " + inType + ".") - case Some(typ) if K.isSame((term `is` typ).asFormula.underlying, (term `is` outType).asFormula.underlying) => - if K.isSame((arg `is` inType).asFormula.underlying, (arg `is` argType).asFormula.underlying) then - have(term `is` outType) by Tautology.from( - functionFromApplication of (f := func, a := arg, x := inType, y := outType), - funcProof, - argProof - ) - typ - else - throw TypingException("Function " + func + " found to have type " + funcType + ", but argument " + arg + " has type " + argType + " instead of expected " + inType + ".") - - case _ => - throw TypingException("Function " + func + " expected to have function type ? |=> " + typ + ", but has type " + funcType + ". ") - case _ => - throw TypingException("Function " + func + " expected to have function type ? |=> " + typ + ", but has type " + funcType + ". Note that terms having multiple different types is only partialy supported.") - - case AppliedFunctional(label, args) => - val (argTypes, argTypesProofs) = args.map(arg => - try (innerTypecheck(context2, arg, None), lastStep) - catch - case e: TypingException => (any, any.definition of (x := arg)) //if no type could be constructed the normal way, we give it "any" - ).unzip - val labelTypes = label match - case label: TypedConstantFunctional[?] => - (label.typ, () => label.justif) +: - functionalTypingAssumptions.getOrElse(label, Nil).map(fc => (fc, () => (have( fc.formula(label.asInstanceOf) ) by Restate) )) - - case _ => functionalTypingAssumptions.getOrElse(label, Nil).map(fc => (fc, () => have( fc.formula(label.asInstanceOf) ) by Restate )) - functionalTypingAssumptions.get(label) - if labelTypes.isEmpty then - throw TypingException("Function " + label + " expected to have type (" + argTypes.mkString(", ") + ") |=> ? but is untyped.") - else - typ match - case None => - labelTypes.find((labelType, step) => - labelType.arity == args.size && - (args zip argTypes).zip(labelType.in.toSeq).forall((argAndTypes, inType) => - K.isSame((argAndTypes._1 `is` inType).asFormula.underlying, (argAndTypes._1 `is` argAndTypes._2).asFormula.underlying) // - ) - ) match - case None => - throw TypingException("Function " + label + " expected to have type (" + argTypes.mkString(", ") + ") |=> ? but is assigned " + labelTypes.mkString(" & ") + ". Note that terms having multiple different types is only partialy supported.") - case Some(labelType, step) => - val out: Class = labelType.out - - val in: Seq[Class] = labelType.in.toSeq - //val labelProp = labelType.formula(label.asInstanceOf) - val labelPropStatement = step() - val labInst = labelPropStatement.of(args*) - val subst = (labelType.args zip args).map((v, a) => (v := a)) - val newOut: Class = out match { - case t: Term => t.substitute(subst*) - case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst*) - } - have(term `is` newOut) by Tautology.from( - (argTypesProofs :+ labInst )* - ) - newOut - case Some(typValue) => - labelTypes.find((labelType, step) => - labelType.arity == args.size && - (args zip argTypes).zip(labelType.in.toSeq).forall((argAndTypes, inType) => - K.isSame((argAndTypes._1 `is` inType).asFormula.underlying, (argAndTypes._1 `is` argAndTypes._2).asFormula.underlying) - ) && { - val subst = (labelType.args zip args).map((v, a) => (v := a)) - val newOut: Class = labelType.out match { - case t: Term => t.substitute(subst*) - case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst*) - } - K.isSame((term `is` newOut).asFormula.underlying, (term `is` typValue).asFormula.underlying) - - } - ) match - case None => - throw TypingException("Function " + label + " expected to have type (" + argTypes.mkString(", ") + ") |=> " + typValue + " but is assigned " + labelTypes.mkString(" & ") + ". Note that terms having multiple different types is only partialy supported.") - case Some(labelType, step) => - val out: Class = labelType.out - val in: Seq[Class] = labelType.in.toSeq - //val labelProp = labelType.formula(label.asInstanceOf) - val labelPropStatement = step() - have(term `is` typValue) by Tautology.from( - (argTypesProofs :+ labelPropStatement.of(args*) )* - ) - typValue - - case v: Variable => - if possibleTypes.isEmpty then - throw TypingException("Variable " + v + " expected to be of type " + typ + " but is untyped.") - else throw TypingException("Variable " + v + " expected to be of type " + typ + " but is assigned " + possibleTypes.mkString(" & ") + ".") - - case c: Constant => - if possibleTypes.isEmpty then - throw TypingException("Constant " + c + " expected to be of type " + typ + " but is untyped.") - else throw TypingException("Constant " + c + " expected to be of type " + typ + " but is assigned " + possibleTypes.mkString(" & ") + ".") - - case _: AppliedFunctional => - throw Exception("Why is this not handled by the previous case? Scala reports an incomplete match") - - } - innerTypecheck(typingAssumptions, term, typ) - } - catch { - case e: TypingException => - return proof.InvalidProofTactic(e.msg) - } - } - - - } - - - - - - - - - - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala deleted file mode 100644 index fa455cd39..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala +++ /dev/null @@ -1,566 +0,0 @@ -package lisa.maths.settheory.types.adt - -/** - * This object provides a DSL for defining algebraic data types (ADTs) and functions over ADT in Lisa. - * For usage examples, please refer to the documentation of the package or the reference manual. - */ -object ADTSyntax { - - import ADTDefinitions.* - import lisa.maths.settheory.SetTheory.{*, given} - - /** - * Builder for defining a constructor specification. - * - * @param param the parameters of the constructor - */ - case class ConstructorBuilder (private val param: Seq[ConstructorArgument]) { - - /** - * The number of arguments the constructor takes - */ - def size: Int = param.length - - /** - * Merges the parameters of two constructors. - * - * @param b the other constructor - */ - infix def ++(b: ConstructorBuilder): ConstructorBuilder = ConstructorBuilder(param ++ b.param.toSeq) - - /** - * Converts this constructor into an ADT with a single constructor. - */ - def toADTBuilder = ADTBuilder(Seq(this)) - - /** - * Combines two constructors into an ADT. - * - * @param b the other constructor - */ - infix def |(b: ConstructorBuilder): ADTBuilder = this | b.toADTBuilder - - /** - * Adds this constructor to an ADT. - * - * @param b the ADT to which the constructor is added - */ - infix def |(b: ADTBuilder): ADTBuilder = toADTBuilder | b - - /** - * Outputs the [[UntypedConstructor]] associated with this builder. - * - * @param name the name of the constructor - */ - def build(variables1: Seq[Variable], variables2: Seq[Variable]): SyntacticConstructor = SyntacticConstructor(param, variables1, variables2) - } - - /** - * Companion object for the [[ConstructorBuilder]] class. - */ - object ConstructorBuilder { - - /** - * Creates an empty [[ConstructorBuilder]]. - */ - def empty: ConstructorBuilder = ConstructorBuilder(Seq.empty) - } - - trait ConstructorConverter[T] { - - /** - * Converts a value into a [[ConstructorBuilder]]. - */ - def apply(t: T): ConstructorBuilder - } - - /** - * Converts a value into a [[ConstructorBuilder]]. - * - * @param any the value to convert - * @param c the converter that is used for the conversion - */ - private def any_to_const[T](any: T)(using c: ConstructorConverter[T]): ConstructorBuilder = c(any) - - given unit_to_const: ConstructorConverter[Unit] with { - - /** - * Converts a unit value into a constructor taking no arguments. - */ - override def apply(u: Unit): ConstructorBuilder = ConstructorBuilder.empty - } - - given empty_to_const: ConstructorConverter[EmptyTuple] with { - - /** - * Converts an empty tuple into a constructor taking no arguments. - */ - override def apply(t: EmptyTuple): ConstructorBuilder = ConstructorBuilder.empty - } - - given term_to_const[T <: Term]: ConstructorConverter[T] with { - - /** - * Converts a term into a constructor taking one non inductive argument. - */ - override def apply(t: T): ConstructorBuilder = ConstructorBuilder(Seq(GroundType(t))) - } - - given adt_to_const[N <: Arity]: ConstructorConverter[ADT[N]] with { - - /** - * Converts an ADT into a constructor taking one inductive argument. - */ - override def apply(a: ADT[N]): ConstructorBuilder = ConstructorBuilder(Seq(Self)) - } - - given adt_tuple_to_const[N <: Arity, T <: Tuple](using ConstructorConverter[T]): ConstructorConverter[ADT[N] *: T] with { - - /** - * Converts a tuple prepended with a type into a constructor taking an argument and whose other arguments are deduced from - * applying recursively the conversion to the tuple. - */ - override def apply(t: ADT[N] *: T): ConstructorBuilder = - any_to_const(t.head) ++ any_to_const(t.tail) - } - - - given term_tuple_to_const[H <: Term, T <: Tuple](using ConstructorConverter[T]): ConstructorConverter[H *: T] with { - - /** - * Converts a tuple prepended with a type into a constructor taking an argument and whose other arguments are deduced from - * applying recursively the conversion to the tuple. - */ - override def apply(t: H *: T): ConstructorBuilder = - any_to_const(t.head) ++ any_to_const(t.tail) - } - - extension [T1](left: T1)(using c1: ConstructorConverter[T1]) - /** - * Converts two values into constructors and combines them into an ADT. - * - * @param right the other value to convert - * @param c2 the implicit converter for the second value - */ - infix def |[T2](right: T2)(using c2: ConstructorConverter[T2]): ADTBuilder = any_to_const(left) | any_to_const(right) - - /** - * Builder for defining ADT specifications. - * - * @param constructors the builders for each constructor of the ADT. - */ - case class ADTBuilder (private val constructors: Seq[ConstructorBuilder]) { - - /** - * The number of constructors in the ADT. - */ - def size: Int = constructors.length - - /** - * Combines this ADT with another one. - * - * @param b the other ADT - */ - infix def |(b: ADTBuilder): ADTBuilder = ADTBuilder(constructors ++ b.constructors) - - /** - * Adds a constructor to this ADT. - * - * @param b the constructor to add - */ - infix def |(b: ConstructorBuilder): ADTBuilder = this | b.toADTBuilder - - /** - * Converts a value into a constructor and adds it to this ADT. - * - * @param t the value to convert - * @param c the implicit converter - */ - infix def |[T](t: T)(using c: ConstructorConverter[T]): ADTBuilder = this | any_to_const(t) - - /** - * Outputs the corresponding ADT and its constructors. - * - * @tparam N the number of type variables appearing in the specification of the ADT - * @param typeVariables the type variables of the ADT - * @param names the names of the constructors and of the ADT - */ - def build[N <: Arity](typeVariables: Variable ** N, names: Seq[String]): (ADT[N], constructors[N]) = - - val trimmedNames = (if size == 0 then names else names.tail).take(size + 1) - require( - trimmedNames.length == constructors.length + 1, - s"The number of new identifiers for constructors must match the given specification.\nNew identifiers: ${names.length - 1}, number of constructors: ${constructors.length}." - ) - - val typeVarsSet = typeVariables.toSeq.toSet - val syntacticCons = constructors.map(c => - c.build(Helpers.chooseVars("x", c.size, typeVarsSet), Helpers.chooseVars("y", c.size, typeVarsSet)) - ) - val syntacticADT = SyntacticADT[N](trimmedNames.head, syntacticCons, typeVariables) - val semanticCons = trimmedNames.tail.zip(syntacticCons).map(SemanticConstructor(_, _, syntacticADT)) - val semanticADT = SemanticADT[N](syntacticADT, semanticCons) - val cons = semanticCons.map(Constructor(_)) - (ADT[N](semanticADT, cons), new constructors[N](cons*)) - - } - - /** - * Companion object for the [[ADTBuilder]] class. - */ - object ADTBuilder { - - /** - * Creates an empty [[ADTBuilder]]. - */ - def empty: ADTBuilder = ADTBuilder(Seq.empty) - - /** - * Creates an empty [[ADTBuilder]]. - */ - def | = empty - } - - /** - * Builder for defining polymorphic ADT specifications. - * - * @tparam N the number of type variables of the ADT - * @param typeVariable the type variables of the ADT - * @param specification the builder for ADT - */ - case class PolymorphicADTBuilder[N <: Arity](typeVariables: Variable ** N, specification: ADTBuilder) { - - /** - * Outputs the corresponding ADT and its constructors. - * - * @param names the names of the constructors and of the ADT - */ - def build(names: Seq[String]) = specification.build(typeVariables, names) - - /** - * Adds a constructor to the ADT specification - * - * @param b the builder of the constructor - */ - def | (b: ConstructorBuilder): PolymorphicADTBuilder[N] = PolymorphicADTBuilder(typeVariables, specification | b) - - /** - * Adds a constructor to the ADT specification - * - * @param t the value to be converted into a constructor - */ - def |[T] (t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[N] = | (any_to_const(t)) - } - - // Syntactic sugar for polymorphic ADT Builders - - extension (u: Unit) - def --> (builder: ADTBuilder): PolymorphicADTBuilder[0] = PolymorphicADTBuilder[0](**(), builder) - def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[0] = --> (builder.toADTBuilder) - def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[0] = --> (any_to_const(t)) - - extension (v: Variable) - def --> (builder: ADTBuilder): PolymorphicADTBuilder[1] = PolymorphicADTBuilder[1](**(v), builder) - def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[1] = --> (builder.toADTBuilder) - def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[1] = --> (any_to_const(t)) - - extension (v: (Variable, Variable)) - def --> (builder: ADTBuilder): PolymorphicADTBuilder[2] = PolymorphicADTBuilder[2](**(v._1, v._2), builder) - def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[2] = --> (builder.toADTBuilder) - def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[2] = --> (any_to_const(t)) - - extension (v: (Variable, Variable, Variable)) - def --> (builder: ADTBuilder): PolymorphicADTBuilder[3] = PolymorphicADTBuilder[3](**(v._1, v._2, v._3), builder) - def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[3] = --> (builder.toADTBuilder) - def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[3] = --> (any_to_const(t)) - - extension (v: (Variable, Variable, Variable, Variable)) - def --> (builder: ADTBuilder): PolymorphicADTBuilder[4] = PolymorphicADTBuilder[4](**(v._1, v._2, v._3, v._4), builder) - def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[4] = --> (builder.toADTBuilder) - def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[4] = --> (any_to_const(t)) - - extension (v: (Variable, Variable, Variable, Variable, Variable)) - def --> (builder: ADTBuilder): PolymorphicADTBuilder[5] = PolymorphicADTBuilder[5](**(v._1, v._2, v._3, v._4, v._5), builder) - def --> (builder: ConstructorBuilder): PolymorphicADTBuilder[5] = --> (builder.toADTBuilder) - def -->[T](t: T)(using ConstructorConverter[T]): PolymorphicADTBuilder[5] = --> (any_to_const(t)) - - - /** - * Lists all constructors of this ADT. - */ - case class constructors[N <: Arity](cons: Constructor[N]*) - - /** - * Companion object for the [[constructors]] class. - */ - object constructors { - def unapplySeq[N <: Arity](adt: ADT[N]): Seq[Constructor[N]] = adt.constructors - } - - /** - * Contains useful macros for ADT UI - */ - object Macro { - import scala.quoted._ - - /** - * Extract all the scala identifiers defined in the same line or after an expression. - * - * @param e the expression around which the names are extracted - */ - inline def extractNames[T](e: T): Seq[String] = ${extractNames('{e})} - - /** - * Macro implementing [[this.extractNames]]. - * - * @param e the quoted expression around which the names are extracted - */ - private def extractNames[T](using Quotes)(e: Expr[T]) : Expr[Seq[String]] = - - import quotes.reflect._ - - - val subscope = Symbol.spliceOwner.owner.owner.owner - val scope = subscope.owner - val tree = scope.tree - - case class traverser(s: Symbol) extends TreeTraverser { - var reachedADT: Boolean = false - var constructorNames: Seq[String] = Seq.empty[String] - - override def traverseTree(tree: Tree)(owner: Symbol): Unit = tree match - case v : ValDef => - if !reachedADT then - if v.symbol == s then - constructorNames = constructorNames :+ v.symbol.name - reachedADT = true - else - constructorNames = constructorNames :+ v.symbol.name - - super.traverseTree(tree)(owner) - case _ => super.traverseTree(tree)(owner) - } - - val trav = traverser(subscope) - trav.traverseTree(tree)(scope) - Expr(trav.constructorNames) - } - - /** - * Syntax to define Algebraic Data Types - */ - object define { - /** - * Extracts the constructors from an ADT. - * - * @param adt the ADT - * @return a tuple containing the ADT and its constructors - */ - private def extractConstructors[N <: Arity](adt: ADT[N]): (ADT[N], constructors[N]) = (adt, constructors(adt.constructors*)) - - /** - * Outputs a polymorphic ADT and constructors from a user specification - * Needs to be inline in order to fetch the name of the ADT and the constructor. - * - * @param builder the builder user for specifying the ADT - */ - inline def unapply[N <: Arity](builder: PolymorphicADTBuilder[N]): (ADT[N], constructors[N]) = builder.build(Macro.extractNames(builder)) - - /** - * Outputs a (non polymorphic) ADT and constructors from a user specification. - * Needs to be inline in order to fetch the name of the ADT and the constructor. - * - * @param builder the builder user for specifying the ADT - */ - inline def unapply(builder: ADTBuilder): (ADT[0], constructors[0]) = unapply[0](() --> builder) - - /** - * Returns an ADT containing only one constructor out of a user specification. - * Needs to be inline in order to fetch the name of the ADT and the constructor. - * - * @param builder the builder of the unique constructor of the ADT - */ - private inline def unapply(builder: ConstructorBuilder): (ADT[0], constructors[0]) = unapply(builder.toADTBuilder) - - /** - * Returns an ADT isomorphic to a given type. It has only one constructor taking as only argument an element of - * the provided type. - * Needs to be inline in order to fetch the name of the ADT and the constructor. - * - * @param t type given by the user - */ - inline def unapply(t: Term): (ADT[0], constructors[0]) = unapply(term_to_const(t)) - - /** - * Returns the unit type. This is an ADT containing only one value and hence having only one - * constructor (non-inductive and taking no arguments). - * Needs to be inline in order to fetch the name of the ADT and the constructor. - * - * @param u user specification indicating that they want to generate the unit type - */ - inline def unapply(u: Unit): (ADT[0], constructors[0]) = unapply(unit_to_const(u)) - - /** - * Returns a product type (also known as tuple). This is an ADT containing only one constructor. - * Generally its arguments are non inductive as the opposite would lead to the empty type. - * Needs to be inline in order to fetch the name of the ADT and the constructor. - * - * @param t user specification of the tuple - */ - inline def unapply[N <: Arity, T <: Tuple](t: (ADT[N] | Term) *: T)(using ConstructorConverter[T]): (ADT[0], constructors[0]) = - t.head match - case a: ADT[N] => unapply(adt_tuple_to_const(a *: t.tail)) - case term: Term => unapply(any_to_const(term *: t.tail)) - } - - /** - * Converts an ADT with no type variables into a term. - */ - given adt_to_term: Conversion[ADT[0], Term] = _.applyUnsafe(**()) - - /** - * Converts a function over an ADT with no type variables into a term (i.e a set function). - */ - given fun_to_term: Conversion[ADTFunction[0], Term] = _.applyUnsafe(**()) - - /** - * Converts a constructor with no type variables into a term (i.e a set function). - */ - given constructor_to_term: Conversion[Constructor[0], Term] = _.applyUnsafe(**()) - - /** - * Mutable data structure that registers the patterns that have been filled inside a pattern matching syntax. - * - * @tparam N the type variables of the ADT - * @param comp the complementary information stored in the builder - */ - class CaseBuilder[N <: Arity, T, R](val comp : R) { - - /** - * The underlying mutable map between patterns and the body of the corresponding cases. For each - * patterns stores the variables that have been used to represent its arguments. - * - */ - private val underlying = scala.collection.mutable.Map[Constructor[N], (Seq[Variable], T)]() - - /** - * Adds a case to the pattern matching - * - * @param cons the pattern / constructor - * @param value the value next to the variables that are used for the pattern's arguments - */ - def += (cons: Constructor[N], value: (Seq[Variable], T)) = underlying += (cons -> value) - - /** - * Checks if the cases of a pattern matching are valid. Specifically, it checks that: - * - All constructors are covered - * - There are no extra cases - * - The number of variables provided by the user matches the arity of the constructor - * - * @param adt the ADT over which the pattern matching is performed - * @return an error message if the pattern matching is invalid, None otherwise - */ - def isValid(adt: ADT[N]): Option[String] = - val constructors = adt.constructors.toSet - val casesConstructors = underlying.keySet.toSet - - val constructorsMinusCases = constructors -- casesConstructors - val casesMinusConstructors = casesConstructors -- constructors - - // STEP 1: Check that all constructors are covered - if !constructorsMinusCases.isEmpty then - Some(s"Case for ${constructorsMinusCases.head.name} is missing.") - // STEP 2: Check that there are no extra cases - else if !casesMinusConstructors.isEmpty then - Some(s"${casesMinusConstructors.head.name} is not a constructor of ${adt.name}.") - else - underlying.keys.foldLeft[Option[String]](None)((acc, c) => - val vars = underlying(c)._1.toSet - // STEP 3: Check that for each case the number of variables provided by the user matches the arity of the constructor - acc.orElse(Some(s"Case ${c.name}: ${vars.size} variables were provided whereas the arity of ${c.name} is ${c.arity}.").filter(_ => vars.size != c.underlying.arity)) - ) - - /** - * Outputs an immutable map out of the underlying mutable one - */ - def build: Map[Constructor[N], (Seq[Variable], T)] = underlying.toMap - } - - /** - * Case of a a pattern matching syntax - * - * @param cons the pattern / constructor - * @param vars variables that are used to represent the arguments of the constructor - */ - case class Case[N <: Arity](cons: Constructor[N], vars: Variable*) { - - /** - * Used in the context of an induction proof. Adds the subproof corresponding to this case into a builder. - * - * @see [[Induction]] - * - * @param proof the outer scope of the induction proof - * @param line the line at which this case is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param file the file in which this case is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param builder the builder of the induction proof - * @param subproof the proof of the case (possibly using the induction hypothesis) - */ - def subproof (using proof: Proof, line: sourcecode.Line, file: sourcecode.File, builder: CaseBuilder[N, proof.ProofStep, (Sequent, Seq[Term], Variable)])(subproof: proof.InnerProof ?=> Unit): Unit = - val (bot, args, adtVar) = builder.comp - val prop = bot.right.head - val consTerm = appSeq(cons.underlying.term(args))(vars) - val subst = adtVar -> consTerm - - val assumptions = - (wellTypedSet(cons.underlying.semanticSignature(vars).map(p => (p._1, p._2.substitute(cons.underlying.typeVariablesSeq.zip(args).map(SubstPair(_, _))*)))) - ++ - cons.underlying.syntacticSignature(vars).filter(_._2 == Self).map((v, _) => prop.substitute(adtVar -> v))) - - //val botWithAssumptions = bot.substitute(subst) ++ ((assumptions ++ proof.getAssumptions) |- ()) - val botWithAssumptions = bot.substitute(subst) ++ (assumptions |- ()) - - - - val iProof: proof.InnerProof = new proof.InnerProof(Some(botWithAssumptions)) - subproof(using iProof) - val proofStep = (new SUBPROOF(using proof)(None)(iProof)).judgement.validate(line, file).asInstanceOf[proof.ProofStep] - - def subproofWithExtraStep: proof.ProofTacticJudgement = TacticSubproof{ ip ?=> - val fullSeq = Tautology(using lisa.SetTheoryLibrary, ip)(proofStep)(botWithAssumptions) - if fullSeq.isValid then - fullSeq.validate(line, file) - else - return proof.InvalidProofTactic(s"Proof of case ${cons.name} is invalid.\nExpected: ${botWithAssumptions}.") - } - - builder += (cons, (vars, subproofWithExtraStep.validate(line, file))) - - /** - * Used in the context of a function definition. Adds the body of the case to a builder. - * - * @param body the body of this case - * @param builder the builder for the function definition - */ - def apply(body : Term)(using builder: CaseBuilder[N, Term, Unit]) = builder += (cons, (vars, body)) - } - - /** - * Defines a function over an ADT - * - * @param adt the domain of this function - * @param returnType the return type of this function - * @param name the name of this functions - * @param cases the definition of the function for each constructor - */ - def fun[N <: Arity](adt: ADT[N], returnType: Term)(using name: sourcecode.Name)(cases: CaseBuilder[N, Term, Unit] ?=> Unit): ADTFunction[N] = { - val builder = CaseBuilder[N, Term, Unit](()) - cases(using builder) - builder.isValid(adt) match - case None => - ADTFunction(SemanticFunction[N](name.value, adt.underlying, builder.build.map((k, v) => (k.underlying, v)), returnType), adt) - case Some(msg) => throw IllegalArgumentException(msg) - } - -} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala deleted file mode 100644 index 01b9d6ca4..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Functions.scala +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Defines set theoretic functions over Algebraic Data Types - */ - -package lisa.maths.settheory.types.adt - -import lisa.maths.settheory.SetTheory.{_, given} -import lisa.maths.settheory.functions.|=> -import lisa.maths.settheory.types.TypeSystem.:: -import lisa.maths.settheory.types.TypeSystem._ - -import ADTDefinitions.* -import Helpers.* -import Helpers.{/\, ===, \/} - -/** - * Set theoretic interpretation of a function over an ADT. - * - * @tparam N the number of type variables of the domain of this function - * @param name the name of this function - * @param adt the domain of this function - * @param cases the body of this function for each constructor - * @param returnType the codomain of this function - * @param line the line at which this function is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param file the file in which this function is defined. Usually fetched automatically by the compiler. - * Used for error reporting - */ -class SemanticFunction[N <: Arity](name: String, adt: SemanticADT[N], cases: Map[SemanticConstructor[N], (Seq[Variable], Term)], returnType: Term)(using line: sourcecode.Line, file: sourcecode.File) { - - /** - * Map binding each constructor to a theorem stating that the case is well typed. - */ - private val checkReturnType: Map[SemanticConstructor[N], THM] = - (for c <- cases.keys yield - val (vars, body) = cases(c) - c -> Lemma(wellTyped(c.semanticSignature(vars)) |- body :: returnType) { - have(thesis) by TypeChecker.prove - } - ).toMap - - /** - * Type variables appearing in this function's domain. - */ - val typeVariables: Variable ** N = adt.typeVariables - - /** - * Sequence of type variables appearing in this function's domain. - */ - val typeVariablesSeq: Seq[Variable] = adt.typeVariablesSeq - - /** - * Number of type variables appearing in this function. - */ - val typeArity: N = adt.typeArity - - /** - * Full name of this function. That is the name of the function prefixed by the name of the ADT. - */ - val fullName = s"$name" - // val fullName = s"${adt.name}/$name" - - val typ = adt.term |=> returnType - - /** - * Definition of this function. - * - * Formally it is the only function whose domain is the ADT and such that for each constructor c f * (c * x1 * ... * xn) = case(c, x1, ..., xn) - */ - private val untypedDefinition = (f :: typ) /\ simplify(/\(cases.map((c, caseDef) => - val (vars, body) = caseDef - forallSeq(vars, wellTypedFormula(c.semanticSignature(vars)) ==> (f * c.appliedTerm(vars) === body)) - ))) - - /** - * Lemma --- Uniqueness of this function. - */ - private val uniqueness = Axiom(existsOne(f, untypedDefinition)) - - /** - * Set theoretic definition of the constructor. - */ - private val classFunction = FunctionDefinition(fullName, line.value, file.value)(typeVariablesSeq, f, untypedDefinition, uniqueness).label - - /** - * Identifier of this function. - */ - val id: Identifier = classFunction.id - - /** - * Function where type variables are instantiated with schematic symbols. - */ - val term = classFunction.applySeq(typeVariablesSeq) - - /** - * Lemma --- The body of this function correpsonds to the cases provided by the user. - * - * `for each constructor c, ∀x1, ..., xn. f * (c * x1 * ... * xn) = case(c, x1, ..., xn)` - */ - val shortDefinition = cases.map((c, caseDef) => - val (vars, body) = caseDef - c -> Lemma(simplify(wellTypedFormula(c.semanticSignature(vars))) ==> (term * c.appliedTerm(vars) === body)) { - have(forall(f, (term === f) <=> untypedDefinition)) by Exact(classFunction.definition) - thenHave((term === term) <=> (term :: typ) /\ (/\(cases.map((c, caseDef) => { - val (vars, body) = caseDef - forallSeq(vars, wellTypedFormula(c.semanticSignature(vars)) ==> (term * c.appliedTerm(vars) === body)) - })))) by InstantiateForall(term) - thenHave(forallSeq(vars, wellTypedFormula(c.semanticSignature(vars)) ==> (term * c.appliedTerm(vars) === body))) by Weakening - vars.foldLeft(lastStep)((l, _) => - lastStep.statement.right.head match - case Forall(v, phi) => thenHave(phi) by InstantiateForall(v) - case _ => throw UnreachableException - ) - } - ) - - /** - * Lemma --- Introduction rule - * - * `f : ADT -> T` - * - * where `T` is the return type of this function - */ - val intro = Lemma(forallSeq(typeVariablesSeq, term :: typ)) { - have(forall(f, (term === f) <=> untypedDefinition)) by Exact(classFunction.definition) - thenHave((term === term) <=> (term :: typ) /\ (/\(cases.map((c, caseDef) => { - val (vars, body) = caseDef - forallSeq(vars, /\(wellTyped(c.semanticSignature(vars))) ==> (term * c.appliedTerm(vars) === body)) - })))) by InstantiateForall(term) - thenHave(term :: typ) by Weakening - thenHave(thesis) by QuantifiersIntro(typeVariablesSeq) - } -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala deleted file mode 100644 index dfc601d29..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala +++ /dev/null @@ -1,1032 +0,0 @@ -package lisa.maths.settheory.types.adt - -/** - * Tactic that proves every goal of the form: - * - * ` ... |- ..., ∀x1, ..., xn. P(x), ...` - * - * ` ..., ∀x1, ..., xn . P(x), ... |- ...` - * - * ` ... |- ..., ∃x1, ..., xn. P(x), ...` - * - * ` ..., ∃x1, ..., xn . P(x), ... |- ...` - * - * given a proof of the sequents without quantification. - */ -object QuantifiersIntro extends lisa.prooflib.ProofTacticLib.ProofTactic { - - import lisa.prooflib.SimpleDeducedSteps.Restate - import lisa.prooflib.BasicStepTactic.* - import lisa.fol.FOL.* - - /** - * Executes the tactic on a specific goal. - * - * @param lib the library that is currently being used - * @param proof the ongoing proof in which the tactic is called - * @param vars the variables that needs to be quantified - * @param fact the proof of the sequent without quantification - * @param bot the statement to prove - */ - def apply(using lib: lisa.prooflib.Library, proof: lib.Proof)(vars: Seq[Variable])(fact: proof.Fact)(bot: Sequent): proof.ProofTacticJudgement = - TacticSubproof { sp ?=> - if vars.isEmpty then - lib.have(bot) by Restate.from(fact) - else - val diff: Sequent = bot -- fact.statement - - diff match - case Sequent(s, _) if s.size == 1 => - val diffRest = bot.left -- s - val f = s.head - val fWithoutQuant = (fact.statement.left -- diffRest).head - f match - case BinderFormula(Forall, _, _) => - vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => - val (accFact, accFormula) = acc - val newFormula = forall(v, accFormula) - (lib.have(diffRest + newFormula |- bot.right) by LeftForall(accFact), newFormula) - ) - case BinderFormula(Exists, _, _) => - vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => - val (accFact, accFormula) = acc - val newFormula = exists(v, accFormula) - (lib.have(diffRest + newFormula |- bot.right) by LeftExists(accFact), newFormula) - ) - case _ => return proof.InvalidProofTactic(s"The formula that changed is not quantified: $f.") - case Sequent(_, s) if s.size == 1 => - val diffRest = bot.right -- s - val f = s.head - val fWithoutQuant = (fact.statement.right -- diffRest).head - f match - case BinderFormula(Forall, _, _) => - vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => - val (accFact, accFormula) = acc - val newFormula = forall(v, accFormula) - (lib.have(bot.left |- diffRest + newFormula) by RightForall(accFact), newFormula) - ) - case BinderFormula(Exists, _, _) => - vars.foldRight[(sp.Fact, Formula)](fact, fWithoutQuant)( (v, acc) => - val (accFact, accFormula) = acc - val newFormula = exists(v, accFormula) - (lib.have(bot.left |- diffRest + newFormula) by RightExists(accFact), newFormula) - ) - case _ => return proof.InvalidProofTactic(s"The formula that changed is not quantified: $f.") - case Sequent(s1, s2) if s1.isEmpty && s2.isEmpty => lib.have(bot) by Restate.from(fact) - case _ => return proof.InvalidProofTactic("Two or more formulas in the sequent have changed.") - - - } -} - -/** - * General purpose helpers. - */ -private [adt] object Helpers { - - import lisa.fol.FOL.{*, given} - - /** - * Benchmarks a block of code. - * - * @param name the name of the benchmark - * @param f the block of code to benchmark - * @return the result of the block of code and prints how long it took to execute - */ - def benchmark[T](name: String)(f: => T): T = { - val before = System.nanoTime - - val res = f - - val totalTime = (System.nanoTime - before) / 1000000 - - println(s"$name time: $totalTime ms") - - res - } - - /** - * Exception thrown when code that should not be accessed is reached. - */ - object UnreachableException extends Exception("This code should not be accessed. If you see this message, please report it to the library maintainers.") - - // ********************* - // * FIRST ORDER LOGIC * - // ********************* - - val a = variable - val b = variable - val c = variable - val d = variable - - val f = variable - val g = variable - val h = variable - - val n = variable - val m = variable - - val p = formulaVariable - val p1 = formulaVariable - val p2 = formulaVariable - val p3 = formulaVariable - val p4 = formulaVariable - - val q1 = formulaVariable - val q2 = formulaVariable - - val r = variable - val s = variable - val t = variable - - val x = variable - val y = variable - val z = variable - - val Q = predicate[1] - val P = predicate[1] - val P2 = predicate[2] - val schemPred = predicate[1] - - /** - * Formula representing whether two sequences of terms are pairwise equal. - * - * @param s2 the sequence to compare with - */ - extension (s1: Seq[Term]) def ===(s2: Seq[Term]): Formula = /\(s1.zip(s2).map(_ === _)) - - /** - * Disjunction of a sequence of formulas. - * - * @param s the formulas to which the disjunction is applied - */ - def \/(s: Iterable[Formula]): Formula = - if s.isEmpty then False - else s.fold(False)(_ \/ _) - - /** - * Conjunction of a sequence of formulas. - * - * @param s the formulas to which the conjunction is applied - */ - def /\(s: Iterable[Formula]): Formula = - if s.isEmpty then True - else s.fold(True)(_ /\ _) - - /** - * Repeats existential quantification over a sequence of variables. - * - * @param vars the variables to quantify over - * @param f the formula to which the quantifiers are applied - * @return the quantified formula - */ - def existsSeq(vars: Seq[Variable], f: Formula): Formula = - vars.foldRight(f)(exists(_, _)) - - /** - * Repeats universal quantification over a sequence of variables. - * - * @param vars the variables to quantify over - * @param f the formula to which the quantifiers are applied - * @return the quantified formula - */ - def forallSeq(vars: Seq[Variable], f: Formula): Formula = - vars.foldRight(f)(forall(_, _)) - - /** - * Simplifies a formula by removing True and False constants. - * - * @param f the formula to simplify - */ - def simplify(f: Formula): Formula = - f match - case Or(False, phi) => simplify(phi) - case Or(phi, False) => simplify(phi) - case Or(phi, psi) => simplify(phi) \/ simplify(psi) - case And(True, phi) => simplify(phi) - case And(phi, True) => simplify(phi) - case And(phi, psi) => simplify(phi) /\ simplify(psi) - case Implies(True, phi) => simplify(phi) - case Implies(phi, psi) => Implies(simplify(phi), simplify(psi)) - case _ => f - - - /** - * Picks fresh variables starting with a given prefix . - * - * @param prefix the prefix of the fresh variables - * @param size the number of fresh variables to output - * @param assigned the variables that are already used - * @param counter the index to append to the prefix - * @param acc the variables that have already been generated by this method - * - */ - def chooseVars(prefix: String, size: Int, assigned: Set[Variable] = Set.empty, counter: Int = 0, acc: Seq[Variable] = Seq.empty): Seq[Variable] = - if size == 0 then - acc - else - val newVar = Variable(s"${prefix}${counter}") - if assigned.contains(newVar) then - chooseVars(prefix, size, assigned, counter + 1, acc) - else - chooseVars(prefix, size - 1, assigned, counter + 1, acc :+ newVar) - - -} - -/** - * Definitions and helper functions for ADT. - */ -private[adt] object ADTDefinitions { - - import lisa.maths.settheory.SetTheory.* - import lisa.maths.settheory.functions.* - import lisa.maths.settheory.types.TypeSystem.* - import Helpers.{/\} - - /** - * The specification of a constructor can either contain terms or a self reference, i.e. a reference to the ADT itself. - */ - trait ConstructorArgument { - /** - * Returns the term associated to a constructor argument, or in case it is a self reference, returns the term associated to the ADT. - * - * @param arg the constructor argument - * @param adt the term representing the ADT - */ - def getOrElse(adt: Term): Term = - this match { - case Self => adt - case GroundType(term) => term - } - - /** - * Substitutes the type variables of a constructor argument. - */ - def substitute(p: SubstPair*): ConstructorArgument = - this match - case Self => Self - case GroundType(t) => GroundType(t.substitute(p*)) - } - - /** - * A symbol for self reference - */ - case object Self extends ConstructorArgument - - /** - * Syntactic represenation of a term - * - * @param t the underlying term - */ - case class GroundType(t: Term) extends ConstructorArgument - - /** - * Shorthand for the union of the range of a function. - * - * @param f the function - */ - def unionRange(f: Term) = union(relationRange(f)) - - /** - * Shorthand for the range of a restricted function. - * - * @param f the function - * @param n the domain to which the function is restricted - */ - def restrRange(f: Term, n: Term) = relationRange(restrictedFunction(f, n)) - - /** - * Applies a sequence of arguments to a function. - * - * @param f the function - * @param args the arguments to apply - */ - def appSeq(f: Term)(args: Seq[Term]): Term = args.foldLeft(f)(_ * _) - - /** - * Converts an integer to the associated ordinal. - * - * @param n the integer to convert - */ - def toTerm(n: Int): Term = - require(n >= 0, "n must be a non-negative integer") - if n == 0 then emptySet - else successor(toTerm(n - 1)) - - /** - * Returns a sequence of formulas asserting that all terms of a sequence are well-typed. - * - * @param s the terms and their respective types - */ - def wellTyped(s: Seq[(Term, Term)]): Seq[Formula] = s.map(_ :: _) - - /** - * Returns a sequence of formulas asserting that all terms of a sequence are well-typed with respect to the - * specification of a constructor. - * - * @param s the terms and their respective type - * @param orElse the term to use in case of a self reference - */ - def wellTyped(s: Seq[(Term, ConstructorArgument)])(orElse: Term): Seq[Formula] = s.map((t, arg) => t :: arg.getOrElse(orElse)) - - /** - * Returns a set of formulas asserting that all terms of a sequence are well-typed. - * - * @param s the terms and their respective types - */ - def wellTypedSet(s: Seq[(Term, Term)]): Set[Formula] = wellTyped(s).toSet - - /** - * Returns a set of formulas asserting that all terms of a sequence are well-typed with respect to the - * specification of a constructor. - * - * @param s the terms and their respective type - * @param orElse the term to use in case of a self reference - */ - def wellTypedSet(s: Seq[(Term, ConstructorArgument)])(orElse: Term): Set[Formula] = wellTyped(s)(orElse).toSet - - /** - * Returns a formula asserting that all terms of a sequence are well-typed. - * - * @param s the terms and their respective types - */ - def wellTypedFormula(s: Seq[(Term, Term)]): Formula = /\ (wellTyped(s)) - - /** - * Returns a formula asserting that all terms of a sequence are well-typed with respect to the - * specification of a constructor. - * - * @param s the terms and their respective type - * @param orElse the term to use in case of a self reference - */ - def wellTypedFormula(s: Seq[(Term, ConstructorArgument)])(orElse: Term): Formula = /\ (wellTyped(s)(orElse)) - -} - - -/** - * List of external set theoretic theorems needed for proofs about ADT. - * Some of these theorems are not yet implemented in the library and - * will be added in the future. - */ -private [adt] object ADTHelperTheorems { - - import lisa.maths.settheory.SetTheory.{*, given} - import lisa.maths.settheory.functions.* - import lisa.maths.Quantifiers.{existentialEquivalenceDistribution, equalityInExistentialQuantifier, - existentialConjunctionWithClosedFormula, equalityTransitivity} - import ADTDefinitions.* - import Helpers.* - //import lisa.maths.Quantifiers.* - - // TODO: Remove - val pair = ConstantFunctionLabel("pair", 2) - addSymbol(pair) - - val pairExtensionality = Lemma((pair(a, b) === pair(c, d)) <=> ((a === c) /\ (b === d))) { - sorry - } - - // ********************* - // * FIRST ORDER LOGIC * - // ********************* - - - /** - * Lemma --- Alternative statement of transitivity of equality. - */ - val altEqualityTransitivity = Lemma((x === y, y === z) |- x === z) { - have(thesis) by Restate.from(equalityTransitivity) - } - - /** - * Lemma --- Transitivity of equivalence. - */ - val equivalenceRewriting = Lemma((p1 <=> p2, p2 <=> p3) |- p1 <=> p3) { - have(thesis) by Tautology - } - - /** - * Lemma --- Modus ponens for equivalence. - */ - val equivalenceApply = Lemma((p1 <=> p2, p1) |- p2) { - have(thesis) by Tautology - } - - /** - * Lemma --- Top level existential quantifiers can be swapped. - */ - val existentialSwap = Lemma(∃(x, ∃(y, P2(x, y))) <=> ∃(y, ∃(x, P2(x, y)))) { - have(thesis) by Tableau - } - - /** - * Lemma --- Modus ponens for reversed equivalence. - */ - val equivalenceRevApply = Lemma((p2 <=> p1, p1) |- p2) { - have(thesis) by Tautology - } - - /** - * Lemma --- If a statement is equivalent to the conjunction of two other statements, and one of them is true, then it can be removed from the equivalence. - */ - val equivalenceAnd = Lemma((p2, p1 <=> (p2 /\ p3)) |- p1 <=> p3) { - have(thesis) by Tautology - } - - /** - * Lemma --- If two formulas are equivalent then adding a disjunction on their right side preserves the equivalence. - */ - val rightAndEquivalence = Lemma(p1 <=> p2 |- (p1 /\ p) <=> (p2 /\ p)) { - have(thesis) by Tautology - } - - /** - * Lemma --- If two formulas are equivalent then adding an implication on their left side preserves the equivalence. - */ - val impliesEquivalence = Lemma((p1 <=> p2, p3 <=> p4) |- (p1 ==> p3) <=> (p2 ==> p4)) { - have(thesis) by Tautology - } - - /** - * Lemma --- If two formulas are equivalent then adding an implication on their left side preserves the equivalence. - */ - val leftImpliesEquivalenceWeak = Lemma(p1 <=> p2 |- (p ==> p1) <=> (p ==> p2)) { - have(thesis) by Tautology - } - - /** - * Lemma --- Implication distributes over equivalence. - */ - val leftImpliesEquivalenceStrong = Lemma(p ==> (p1 <=> p2) |- (p ==> p1) <=> (p ==> p2)) { - have(thesis) by Tautology - } - - /** - * Lemma --- If there exists a unique element satisfying a predicate, then all - * other elements satisfying the predicate are equal to it. - */ - val existsOneUniqueness = Lemma((∃!(x, P(x)), P(x), P(y)) |- x === y) { - sorry - } - - // ******************* - // * NATURAL NUMBERS * - // ******************* - - // Natural numbers - val N = Constant("N") - addSymbol(N) - - /** - * Lemma --- 0 is a natural number. - * - * `0 ∈ N` - */ - val zeroIsNat = Lemma(in(emptySet, N)) { - sorry - } - - /** - * Lemma --- The natural numbers are not empty. - * - * `N != ∅` - */ - val natNotEmpty = Lemma(!(N === emptySet)) { - have(thesis) by Cut(zeroIsNat, setWithElementNonEmpty of (y := emptySet, x := N)) - } - - /** - * Lemma --- There exists a natural number. - * - * `∃n ∈ N` - */ - val existsNat = Lemma(exists(n, in(n, N))) { - have(thesis) by RightExists(zeroIsNat) - } - - /** - * Lemma --- Successor is an injective function. - * - * `n = m <=> n + 1 = m + 1` - */ - val successorInjectivity = Lemma((n === m) <=> (successor(n) === successor(m))) { - sorry - } - - /** - * Lemma --- A term is a natural number if and only if its successor is a natural number. - * - * `n ∈ N <=> n + 1 ∈ N` - */ - val successorIsNat = Lemma(in(n, N) <=> in(successor(n), N)) { - sorry - } - - /** - * Lemma --- Any number is smaller than its successor - * - * `∀n ∈ N. n < n + 1` - */ - val inSuccessor = Lemma(in(n, successor(n))) { - val uniomAxiomForward = have(exists(y, in(y, unorderedPair(n, singleton(n))) /\ in(n, y)) |- in(n, union(unorderedPair(n, singleton(n))))) by Cut( - unionAxiom of (x := unorderedPair(n, singleton(n)), z := n), - equivalenceRevApply of (p1 := exists(y, in(y, unorderedPair(n, singleton(n))) /\ in(n, y)), p2 := in(n, union(unorderedPair(n, singleton(n))))) - ) - have(in(singleton(n), unorderedPair(n, singleton(n))) /\ in(n, singleton(n))) by RightAnd( - secondElemInPair of (x := n, y := singleton(n)), - singletonHasNoExtraElements of (x := n, y := n) - ) - thenHave(exists(y, in(y, unorderedPair(n, singleton(n))) /\ in(n, y))) by RightExists - have(in(n, union(unorderedPair(n, singleton(n))))) by Cut(lastStep, uniomAxiomForward) - thenHave(union(unorderedPair(n, singleton(n))) === successor(n) |- in(n, successor(n))) by RightSubstEq.withParametersSimple( - List((union(unorderedPair(n, singleton(n))), successor(n))), - lambda(s, in(n, s)) - ) - have(thesis) by Cut(successor.shortDefinition of (x := n), lastStep) - } - - /** - * Lemma --- 0 is not the successor of any natural number. - * - * `∀n ∈ N. n + 1 != 0` - */ - val zeroIsNotSucc = Lemma(!(successor(n) === emptySet)) { - have(thesis) by Cut(inSuccessor, setWithElementNonEmpty of (y := n, x := successor(n))) - } - - /** - * Lemma --- A number is smaller or equal than another number if and only if it is strictly smaller than its successor. - * - * `m <= n <=> m < n + 1` - */ - val natSubset = Lemma(in(n, N) |- subset(m, n) <=> in(m, successor(n))) { - sorry - } - - /** - * Lemma --- The intersection of a natural number with the set of natural numbers is the number itself. - * - * `n ∩ N = n` - */ - val intersectionNat = Lemma(in(n, N) |- setIntersection(n, N) === n) { - sorry - } - - /** - * Lemma --- If a number is smaller or equal than a natural number, then it is also a natural number. - * - * `m <= n, n ∈ N |- m ∈ N` - */ - val subsetIsNat = Lemma(subset(a, b) |- in(b, N) ==> in(a, N)) { - sorry - } - - /** - * Lemma --- Induction principle for natural numbers - * - * `P(0), ∀n ∈ N. P(n) => P(n + 1) |- ∀n ∈ N. P(n)` - */ - val natInduction = Lemma((P(emptySet), forall(m, in(m, N) ==> (P(m) ==> P(successor(m))))) |- forall(n, in(n, N) ==> P(n))) { - sorry - } - - /** - * Lemma --- Every number is smaller or equal than its successor. - * - * `n <= n + 1` - */ - val subsetSuccessor = Lemma(subset(n, successor(n))) { - have(setUnion(n, singleton(n)) === union(unorderedPair(n, singleton(n))) |- subset(n, union(unorderedPair(n, singleton(n))))) by RightSubstEq.withParametersSimple( - List((setUnion(n, singleton(n)), union(unorderedPair(n, singleton(n))))), - lambda(s, subset(n, s)) - )(unionSubsetFirst of (a := n, b := singleton(n))) - have(subset(n, union(unorderedPair(n, singleton(n))))) by Cut(setUnion.shortDefinition of (x := n, y := singleton(n)), lastStep) - thenHave(successor(n) === union(unorderedPair(n, singleton(n))) |- subset(n, successor(n))) by RightSubstEq.withParametersSimple( - List((successor(n), union(unorderedPair(n, singleton(n))))), - lambda(s, subset(n, s)) - ) - have(thesis) by Cut(successor.shortDefinition of (x := n), lastStep) - } - - // ************* - // * FUNCTIONS * - // ************* - - /** - * Lemma --- Range introduction and elimination rules. If en element is in the image of a function, then it has a preimage inside its domain. - * - * `functional(f) |- y ⊆ Im(f) <=> ∃x ∈ Dom(f). f(x) = y` - */ - val functionRangeMembership = Lemma(functional(f) |- in(y, relationRange(f)) <=> ∃(x, in(x, relationDomain(f)) /\ (app(f, x) === y))) { - sorry - } - - /** - * Lemma --- The restriction of a function is still a function. - * - * `functional(f) |- functional(f|x)` - */ - val functionalRestrictedFunction = Lemma(functional(f) |- functional(restrictedFunction(f, x))) { - sorry - } - - /** - * Lemma --- If an element is in the image of a restricted function, then it has a preimage inside its domain. - * - * `functional(f) |- y ⊆ Im(f) <=> ∃x ∈ d ∩ Dom(f). f|d(x) = y` - */ - val restrictedFunctionRangeMembership = Lemma(functional(f) |- in(y, relationRange(restrictedFunction(f, d))) <=> ∃(x, in(x, d ∩ relationDomain(f)) /\ (app(restrictedFunction(f, d), x) === y))) { - have(functional(f) |- in(y, relationRange(restrictedFunction(f, d))) <=> ∃(x, in(x, relationDomain(restrictedFunction(f, d))) /\ (app(restrictedFunction(f, d), x) === y))) by Cut( - functionalRestrictedFunction of (x := d), - functionRangeMembership of (f := restrictedFunction(f, d)) - ) - thenHave(functional(f) |- in(y, relationRange(restrictedFunction(f, d))) <=> ∃(x, in(x, d ∩ relationDomain(f)) /\ (app(restrictedFunction(f, d), x) === y))) by Substitution.ApplyRules( - restrictedFunctionDomain of (x := d) - ) - } - - /** - * Lemma --- Characterization of the union of the range of a function. - * - * `∪ Im(h) = {z | ∃n ∈ Dom(h). z ∈ h(n)}` - */ - val unionRangeMembership = Lemma(functional(h) |- in(z, unionRange(h)) <=> exists(n, in(n, relationDomain(h)) /\ in(z, app(h, n)))) { - val iffAfterAnd = have(functional(h) |- (y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y)) /\ z ∈ y) by Cut( - functionRangeMembership of (f := h), - rightAndEquivalence of (p1 := y ∈ relationRange(h), p2 := ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y)), p := z ∈ y) - ) - have(functional(h) |- (y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) by Apply(equivalenceRewriting).on( - iffAfterAnd, - existentialConjunctionWithClosedFormula of (P := lambda(m, m ∈ relationDomain(h) /\ (app(h, m) === y)), p := z ∈ y) - ) - - thenHave(functional(h) |- ∀(y, (y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y))) by RightForall - - val beforeExSwap = have(functional(h) |- ∃(y, y ∈ relationRange(h) /\ z ∈ y) <=> ∃(y, ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y))) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P := lambda(y, y ∈ relationRange(h) /\ z ∈ y), - Q := lambda(y, ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) - ) - ) - - have(∃(y, ∃(m, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) <=> ∃(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) subproof { - - have(m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y <=> m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)) by Restate - thenHave(forall(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y <=> m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) by RightForall - have(∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y) <=> ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P := lambda(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y), - Q := lambda(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)) - ) - ) - thenHave(forall(m, ∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y) <=> ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) by RightForall - have(∃(m, ∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)) <=> ∃(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P := lambda(y, ∃(y, m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y)), - Q := lambda(y, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) - ) - ) - have(thesis) by Apply(equivalenceRewriting).on(lastStep, existentialSwap of (P2 := lambda((y, m), m ∈ relationDomain(h) /\ (app(h, m) === y) /\ z ∈ y))) - } - - val introM = - have(functional(h) |- ∃(y, y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) by Apply(equivalenceRewriting).on(beforeExSwap, lastStep) - - have( - ∀(m, (∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))) <=> (m ∈ relationDomain(h) /\ z ∈ app(h, m))) - ) by RightForall(equalityInExistentialQuantifier of (P := lambda(y, m ∈ relationDomain(h) /\ z ∈ y), y := app(h, m))) - - have( - ∃(m, (∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y)))) <=> ∃(m, m ∈ relationDomain(h) /\ z ∈ app(h, m)) - ) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P := lambda(m, ∃(y, m ∈ relationDomain(h) /\ z ∈ y /\ (app(h, m) === y))), - Q := lambda(m, m ∈ relationDomain(h) /\ z ∈ app(h, m)) - ) - ) - - have(functional(h) |- ∃(y, y ∈ relationRange(h) /\ z ∈ y) <=> ∃(m, m ∈ relationDomain(h) /\ z ∈ app(h, m))) by Apply(equivalenceRewriting).on( - introM, - lastStep - ) - - have(thesis) by Apply(equivalenceRewriting).on( - lastStep, - unionAxiom.asInstanceOf - ) - } - - // ************* - // * EMPTYNESS * - // ************* - - /** - * Lemma --- The union of the empty set is the empty set. - * - * `∪ ∅ = ∅` - */ - val unionEmpty = Lemma(union(emptySet) === emptySet) { - sorry - } - - /** - * Lemma --- Restricting the domain of a function to the empty set yields the empty set. - * - * `h|∅ = ∅` - */ - val restrictedFunctionEmptyDomain = Lemma(restrictedFunction(h, emptySet) === emptySet) { - sorry - } - - /** - * Lemma --- If the domain of a function is non empty, then the function is non empty as well. - * - * `Dom(h) != ∅ |- h != ∅` - */ - val nonEmptyDomain = Lemma(!(relationDomain(h) === emptySet) |- !(h === emptySet)) { - sorry - } - - /** - * Lemma --- A superset of a non empty set is non empty. - * - * `x ⊆ y, x != ∅ |- y != ∅` - */ - val subsetNotEmpty = Lemma((subset(x, y), !(x === emptySet)) |- !(y === emptySet)) { - val subst = have(y === emptySet |- y === emptySet) by Hypothesis - have((subset(x, emptySet), y === emptySet) |- (x === emptySet)) by Apply(equivalenceApply of (p1 := subset(x, emptySet))).on(emptySetIsItsOwnOnlySubset.asInstanceOf) - thenHave((subset(x, y), y === emptySet) |- (x === emptySet)) by Substitution.ApplyRules(subst) - } - - /** - * Theorem --- The range of the empty relation is empty. - * - * `range(∅) = ∅` - * - */ - val rangeEmpty = Theorem(relationRange(emptySet) === emptySet) { - import lisa.maths.settheory.SetTheory - - have(!in(SetTheory.pair(a, t), emptySet)) by Exact(emptySetAxiom) - thenHave(forall(a, !in(SetTheory.pair(a, t), emptySet))) by RightForall - val s0 = thenHave(!exists(a, in(SetTheory.pair(a, t), emptySet))) by Restate - - have(!in(t, emptySet)) by Exact(emptySetAxiom) - have(in(t, emptySet) <=> exists(a, in(SetTheory.pair(a, t), emptySet))) by Tautology.from(lastStep, s0) - val defRHS = thenHave(forall(t, in(t, emptySet) <=> exists(a, in(SetTheory.pair(a, t), emptySet)))) by RightForall - - have((relationRange(emptySet) === emptySet) <=> forall(t, in(t, emptySet) <=> exists(a, in(SetTheory.pair(a, t), emptySet)))) by InstantiateForall(emptySet)( - relationRange.definition of (r := emptySet, z := emptySet) - ) - have(relationRange(emptySet) === emptySet) by Tautology.from(defRHS, lastStep) - } - - - /** - * Lemma --- The range of the empty function is empty. - * - * `Im(∅) = ∅` - */ - val unionRangeEmpty = Lemma(unionRange(emptySet) === emptySet) { - have(unionRange(emptySet) === unionRange(emptySet)) by RightRefl - thenHave(unionRange(emptySet) === union(emptySet)) by Substitution.ApplyRules(rangeEmpty) - thenHave(thesis) by Substitution.ApplyRules(unionEmpty) - } - - /** - * Lemma --- If a function and a domain are non empty, then restricting this function to this - * domain yields a non empty set. - * - * `h != ∅, d != ∅ |- h|d != ∅` - */ - val restrictedFunctionNotEmpty = Lemma((!(h === emptySet), !(d === emptySet)) |- !(restrictedFunction(h, d) === emptySet)) { - sorry - } - - // **************** - // * MONOTONICITY * - // **************** - - /** - * Lemma --- Union is a monotonic operation with respect to set inclusion. - * - * `x ⊆ y |- ∪ x ⊆ ∪ y` - */ - val unionMonotonic = Lemma(subset(x, y) |- subset(union(x), union(y))) { - sorry - } - - /** - * Lemma --- Range is a monotonic operation with respect to set inclusion. - * - * `f ⊆ g |- Im(f) ⊆ Im(g)` - */ - val rangeMonotonic = Lemma(subset(f, g) |- subset(relationRange(f), relationRange(g))) { - sorry - } - - /** - * Lemma --- The union of the range is a monotonic operation with respect to set inclusion. - * - * `f ⊆ g |- ∪ Im(f) ⊆ ∪ Im(g)` - */ - val unionRangeMonotonic = Lemma(subset(f, g) |- subset(unionRange(f), unionRange(g))) { - have(thesis) by Apply(unionMonotonic).on(rangeMonotonic.asInstanceOf) - } - - /** - * Lemma --- If two implications are true then disjuncting on both sides is also a valid implication. - */ - val disjunctionsImplies = Lemma((p1 ==> p2, q1 ==> q2) |- (p1 \/ q1) ==> (p2 \/ q2)) { - - val right = have((p1 ==> p2, q1 ==> q2, p1) |- p2 \/ q2) by Restate - val left = have((p1 ==> p2, q1 ==> q2, q1) |- p2 \/ q2) by Restate - - have((p1 ==> p2, q1 ==> q2, p1 \/ q1) |- p2 \/ q2) by LeftOr(left, right) - } - - /** - * Lemma --- If a class function F (whose representation is P) is monotonic then with respect to set inclusion, then S -> F(S) ∪ S is also - * a monotonic function. - * - * `s ⊆ t, F(s) ⊆ F(t) |- F(s) ∪ s ⊆ F(t) ∪ t` - */ - val unionPreimageMonotonic = Lemma((subset(s, t), P(s) ==> P(t)) |- (P(s) \/ in(x, s)) ==> (P(t) \/ in(x, t))) { - have(subset(s, t) |- forall(z, in(z, s) ==> in(z, t))) by Cut( - subsetAxiom of (x := s, y := t), - equivalenceApply of (p1 := subset(s, t), p2 := forall(z, in(z, s) ==> in(z, t))) - ) - thenHave(subset(s, t) |- in(x, s) ==> in(x, t)) by InstantiateForall(x) - have(thesis) by Cut(lastStep, disjunctionsImplies of (p1 := in(x, s), p2 := in(x, t), q1 := P(s), q2 := P(t))) - } - - /** - * Lemma --- Resticting a function to a smaller domain yields a subset of the original function. - * - * `x ⊆ y |- f|x ⊆ f|y` - */ - val restrictedFunctionDomainMonotonic = Lemma(subset(x, y) |- subset(restrictedFunction(f, x), restrictedFunction(f, y))) { - sorry - } - - // ******************* - // * SPECIFIC LEMMAS * - // ******************* - - /** - * Lemma --- Characterization of the union of the range of a cumulative function restricted to the successor of a natural number. - * - * `cumulative(h) and Dom(h) = N |- ∪ Im(h|n + 1) = h(n)` - */ - val unionRangeCumulativeRestrictedFunction = - Lemma((functional(h), relationDomain(h) === N, in(n, N), ∀(m, subset(m, n) ==> subset(app(h, m), app(h, n)))) |- unionRange(restrictedFunction(h, successor(n))) === app(h, n)) { - - val domainSubset = have(in(n, N) |- setIntersection(successor(n), N) === successor(n)) by Apply(intersectionNat).on(equivalenceApply of (p1 := in(n, N)), successorIsNat.asInstanceOf) - - have(functional(h) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ (successor(n) ∩ relationDomain(h)) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y) by Cut( - restrictedFunctionRangeMembership of (f := h, d := successor(n)), - rightAndEquivalence of (p1 := y ∈ restrRange(h, successor(n)), p2 := ∃(m, m ∈ (successor(n) ∩ relationDomain(h)) /\ (app(restrictedFunction(h, successor(n)), m) === y)), p := z ∈ y) - ) - - thenHave( - (functional(h), relationDomain(h) === N) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ (successor(n) ∩ N) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y - ) by RightSubstEq.withParametersSimple( - List((relationDomain(h), N)), - lambda(s, (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ (successor(n) ∩ s) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y) - ) - - thenHave( - (functional(h), in(n, N), relationDomain(h) === N, successor(n) ∩ N === successor(n)) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( - m, - m ∈ (successor(n) ∩ N) /\ (app(restrictedFunction(h, successor(n)), m) === y) - ) /\ z ∈ y - ) by Weakening - - thenHave( - (functional(h), in(n, N), relationDomain(h) === N, successor(n) ∩ N === successor(n)) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( - m, - m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) - ) /\ z ∈ y - ) by RightSubstEq.withParametersSimple( - List((successor(n) ∩ N, successor(n))), - lambda(s, (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ s /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y) - ) - - have( - (functional(h), in(n, N), relationDomain(h) === N) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y)) /\ z ∈ y - ) by Cut(domainSubset, lastStep) - - have( - (functional(h), in(n, N), relationDomain(h) === N) |- (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y) - ) by Apply(equivalenceRewriting).on( - lastStep, - existentialConjunctionWithClosedFormula of (P := lambda(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y)), p := z ∈ y) - ) - - thenHave( - (functional(h), in(n, N), relationDomain(h) === N) |- ∀( - y, - (y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y) - ) - ) by RightForall - - have( - (functional(h), in(n, N), relationDomain(h) === N) |- ∃(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( - y, - ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y) - ) - ) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P := lambda(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y), - Q := lambda(y, ∃(m, m ∈ successor(n) /\ (app(restrictedFunction(h, successor(n)), m) === y) /\ z ∈ y)) - ) - ) - - val introM = - thenHave( - (functional(h), in(n, N), relationDomain(h) === N) |- ∃(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃( - m, - ∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y)) - ) - ) by Tableau - - have((∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (m ∈ successor(n) /\ z ∈ app(restrictedFunction(h, successor(n)), m))) by Exact( - equalityInExistentialQuantifier of (P := lambda(y, m ∈ successor(n) /\ z ∈ y)) - ) - - thenHave(m ∈ successor(n) |- (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (m ∈ successor(n) /\ z ∈ app(h, m))) by Substitution.ApplyRules( - restrictedFunctionApplication - ) - thenHave((∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (m ∈ successor(n) /\ z ∈ app(h, m))) by Tableau - - thenHave(subset(m, n) <=> m ∈ successor(n) |- (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (subset(m, n) /\ z ∈ app(h, m))) by RightSubstIff - .withParametersSimple( - List((m ∈ successor(n), subset(m, n))), - lambda(p, (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (p /\ z ∈ app(h, m))) - ) - - have(in(n, N) |- (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (subset(m, n) /\ z ∈ app(h, m))) by Cut(natSubset, lastStep) - - thenHave( - in(n, N) |- ∀(m, (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))) <=> (subset(m, n) /\ z ∈ app(h, m))) - ) by RightForall - - have( - in(n, N) |- ∃(m, (∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y)))) <=> ∃(m, subset(m, n) /\ z ∈ app(h, m)) - ) by Cut( - lastStep, - existentialEquivalenceDistribution of ( - P := lambda(m, ∃(y, m ∈ successor(n) /\ z ∈ y /\ (app(restrictedFunction(h, successor(n)), m) === y))), - Q := lambda(m, subset(m, n) /\ z ∈ app(h, m)) - ) - ) - - have((functional(h), in(n, N), relationDomain(h) === N) |- ∃(y, y ∈ restrRange(h, successor(n)) /\ z ∈ y) <=> ∃(m, subset(m, n) /\ z ∈ app(h, m))) by Apply(equivalenceRewriting).on( - introM, - lastStep - ) - - val unionIsExists = - have((functional(h), in(n, N), relationDomain(h) === N) |- z ∈ unionRange(restrictedFunction(h, successor(n))) <=> ∃(m, subset(m, n) /\ z ∈ app(h, m))) by Apply(equivalenceRewriting).on( - lastStep, - unionAxiom.asInstanceOf - ) - - val cumulativeAssumption = ∀(m, subset(m, n) ==> subset(app(h, m), app(h, n))) - - have(cumulativeAssumption |- ∃(m, subset(m, n) /\ z ∈ app(h, m)) <=> z ∈ app(h, n)) subproof { - val seq1 = have(z ∈ app(h, n) |- z ∈ app(h, n)) by Hypothesis - have(z ∈ app(h, n) |- subset(n, n) /\ z ∈ app(h, n)) by RightAnd(seq1, subsetReflexivity of (x := n)) - thenHave(z ∈ app(h, n) |- ∃(m, subset(m, n) /\ z ∈ app(h, m))) by RightExists - val backward = thenHave(cumulativeAssumption |- z ∈ app(h, n) ==> ∃(m, subset(m, n) /\ z ∈ app(h, m))) by Weakening - - have(cumulativeAssumption |- cumulativeAssumption) by Hypothesis - thenHave(cumulativeAssumption |- subset(m, n) ==> subset(app(h, m), app(h, n))) by InstantiateForall(m) - have((cumulativeAssumption, subset(m, n), z ∈ app(h, m)) |- forall(z, z ∈ app(h, m) ==> z ∈ app(h, n))) by Apply(equivalenceApply).on( - lastStep, - subsetAxiom - ) - thenHave((cumulativeAssumption, subset(m, n) /\ z ∈ app(h, m)) |- z ∈ app(h, n)) by InstantiateForall(z) - thenHave((cumulativeAssumption, ∃(m, subset(m, n) /\ z ∈ app(h, m))) |- z ∈ app(h, n)) by LeftExists - val forward = thenHave(cumulativeAssumption |- ∃(m, subset(m, n) /\ z ∈ app(h, m)) ==> z ∈ app(h, n)) by RightImplies - - have(thesis) by RightIff(forward, backward) - } - - have((functional(h), in(n, N), relationDomain(h) === N, cumulativeAssumption) |- (z ∈ unionRange(restrictedFunction(h, successor(n)))) <=> z ∈ app(h, n)) by Apply(equivalenceRewriting).on( - unionIsExists, - lastStep - ) - thenHave((functional(h), in(n, N), relationDomain(h) === N, cumulativeAssumption) |- ∀(z, z ∈ unionRange(restrictedFunction(h, successor(n))) <=> z ∈ app(h, n))) by RightForall - - have(thesis) by Apply(equivalenceApply).on(lastStep, extensionalityAxiom.asInstanceOf) - } - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala deleted file mode 100644 index 7a05b0b49..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Defines a set of tactics to reason on Algebraic Data Types - */ - -package lisa.maths.settheory.types.adt - -import lisa.maths.settheory.SetTheory.{*, given} -import ADTDefinitions.* -import Helpers.* - -/** - * Tactic performing a structural induction proof over an algebraic data type. - * - * ===Usage=== - * {{{ - * have(forall(x, x :: adt => P(x)) /*or*/ x :: adt |- P(x)) by Induction(x, adt) { - * Case(c1, x1, ..., xn) subproof { - * // proof of P(xi) /\ ... P(xj) => P(c1(x1, ..., xn)) - * } - * ... - * Case(cm, x1, ..., xk) subproof { - * // proof of P(xi) /\ ... P(xj) => P(c1(x1, ..., xn')) - * } - * } - * }}} - * - * x and adt are inferred from the context if not provided by the user. - * - * Supports only 1 formula on the right hand side of the sequent. - * @param expectedVar the variable on which the induction is performed - * @param expectedADT the algebraic data type on which the induction is performed - */ -class Induction[M <: Arity](expectedVar: Option[Variable], expectedADT: Option[ADT[M]]) extends lisa.prooflib.ProofTacticLib.ProofTactic { - - /** - * Given a proof of the claim for each case (possibly using the induction hypothesis), - * reassemble them to generate a proof of the claim of the form - * `∀x. x :: adt => P(x)` - * - * @param proof the proof in which the induction is performed - * @param cases the proofs of the claim for each case in addition to the variables used by the user - * @param inductionVariable the variable over which the induction is performed - * @param adt the algebraic data type to perform induction on - * @param prop the property to prove //TODO: Change to a lambda expression (Scala 3.4.2) - */ - private def proveForallPredicate[N <: Arity](using proof: lisa.SetTheoryLibrary.Proof)(cases: Map[Constructor[N], (Seq[Variable], proof.Fact)], inductionVariable: Variable, adt: ADT[N], typeVariablesSubst: Seq[Term], propFun: Term => Formula, context: Set[Formula]): proof.Fact = - - val prop = lambda[Term, Formula](x, propFun(x)) - val typeVariablesSubstPairs = adt.typeVariables.toSeq.zip(typeVariablesSubst).map(SubstPair(_, _)) - val instTerm = adt(typeVariablesSubst*) - - adt.constructors.foldLeft[proof.Fact](adt.induction.of((typeVariablesSubstPairs :+ (P := prop))*)) ( (acc, c) => - val inductiveCaseProof = cases(c)._1.zip(c.underlying.underlying.specification.map(_.substitute(typeVariablesSubstPairs*))).foldRight[proof.Fact](cases(c)._2) ( (el, acc2) => - val (v, ty) = el - val accRight: Formula = acc2.statement.right.head - ty match - case Self => - have((acc2.statement - accRight) by Weakening(acc2) - thenHave((lastStep.statement - (prop(v) ==> accRight)) by Weakening - thenHave(lastStep.statement.left |- forall(v, v :: instTerm ==> (prop(v) ==> accRight))) by RightForall - case GroundType(t)=> - thenHave((acc2.statement - accRight) by Weakening - thenHave(lastStep.statement.left |- forall(v, v :: t ==> accRight)) by RightForall - ) - acc.statement.right.head match - case Implies(trueInd, rest) => - // println(s"Case: ${c.fullName}") - // println(isSame(trueInd, inductiveCaseProof.statement.right.head)) - // println(inductiveCaseProof.statement) - // println(" + ") - // println(acc.statement) - // println(" = ") - // println((acc.statement.left ++ inductiveCaseProof.statement.left) |- rest) - have((acc.statement.left ++ inductiveCaseProof.statement.left) |- rest) by Sorry//Cut(inductiveCaseProof, acc) - case _ => throw UnreachableException - ) - thenHave(context |- forall(inductionVariable, inductionVariable :: instTerm ==> prop(inductionVariable))) by Tautology //Change - - - - /** - * Infers the variable, the ADT and the arguments of the ADT from a formula of the form `x :: ADT(T1, ..., Tn)`. - * - * @param f the formula to infer these elements from - */ - def inferArguments(f: Formula): Option[(Variable, ADT[?], Seq[Term])] = - def checkFoundArguments(foundVar: Variable, foundADT: ADT[?], args: Seq[Term]): Option[(Variable, ADT[?], Seq[Term])] = - (expectedVar, expectedADT) match - case (Some(v), _) if v != foundVar => None - case (_, Some(a)) if a != foundADT => None - case _ => Some((foundVar, foundADT, args)) - - f match - case TypeAssignment(Variable(id), ADT(foundADT, args)) => - checkFoundArguments(Variable(id), foundADT, args) - case AppliedPredicate(in, Seq[Term](Variable(id), ADT(foundADT, args))) => - checkFoundArguments(Variable(id), foundADT, args) - case _ => - None - - /** - * Infers the variable, the ADT and the arguments of the ADT from a set of formula - * containing one is of the form `x :: ADT(T1, ..., Tn)`. - * - * @param s the set of formula to infer these elements from - */ - def inferArguments(s: Set[Formula]): Option[(Variable, ADT[?], Seq[Term])] = - s.foldLeft[Option[(Variable, ADT[?], Seq[Term])]](None)((acc, prem) => - acc.orElse(inferArguments(prem)) - ) - - /** - * Infers the variable, the ADT and the arguments of the ADT from a sequent whose one of the premises - * is of the form `x :: ADT(T1, ..., Tn)`. - * - * @param seq the sequent to infer these elements from - */ - def inferArguments(seq: Sequent): Option[(Variable, ADT[?], Seq[Term], Option[Formula])] = - inferArguments(seq.left).map(p => (p._1, p._2, p._3, None)) - .orElse( - seq.right.head match - case Forall(x, Implies(assignment, prop)) => - inferArguments(assignment).filter(p => p._1 == x).map(p => (p._1, p._2, p._3, Some(prop))) - case _ => None - ) - - /** - * Given a proof of the claim for each case (possibly using the induction hypothesis), - * reassemble the subproofs to generate a proof of the claim for every element of the ADT. - * - * @tparam N the arity of the ADT - * @param proof the scope in which the induction is performed - * @param cases the cases to prove. A [[CaseBuilder]] is a mutable data structure that register every case that - * has been added to the tactic. - * @param bot the claim - */ - def apply[N <: Arity](using proof: lisa.SetTheoryLibrary.Proof)(cases: ADTSyntax.CaseBuilder[N, proof.ProofStep, (Sequent, Seq[Term], Variable)] ?=> Unit)(bot: Sequent): proof.ProofTacticJudgement = - inferArguments(bot) match - case Some((inferedVar, inferedADT, inferedArgs, inferedProp)) => - - val prop = inferedProp.getOrElse(bot.right.head) - val propFunction = (t: Term) => inferedProp.getOrElse(bot.right.head).substitute(inferedVar -> t) - val assignment = inferedVar :: inferedADT(inferedArgs*) - val context = (if inferedProp.isDefined then bot else bot -<< assignment).left - val builder = ADTSyntax.CaseBuilder[N, proof.ProofStep, (Sequent, Seq[Term], Variable)]((context |- prop, inferedArgs, inferedVar)) - cases(using builder) - - builder.isValid(inferedADT.asInstanceOf[ADT[N]]) match - case None => - TacticSubproof { sp ?=> - proveForallPredicate(using sp)(builder.build, inferedVar, inferedADT.asInstanceOf[ADT[N]], inferedArgs, propFunction, context) - if !inferedProp.isDefined then - lastStep.statement.right.head match - case Forall(_, phi) => - thenHave(context |- phi) by InstantiateForall(inferedVar) - case _ => throw UnreachableException - - thenHave(bot) by Tautology - } - case Some(msg) => proof.InvalidProofTactic(msg) - - case None => proof.InvalidProofTactic("No variable typed with the ADT found in the context.") - -} - -/** - * Companion object for the [[Induction]] tactic class. - */ -object Induction { - def apply()(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(None, None) - def apply[N <: Arity](adt: ADT[N])(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(None, Some(adt)) - def apply(v: Variable)(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(Some(v), None) - def apply[N <: Arity](v: Variable, adt: ADT[N])(using proof: lisa.SetTheoryLibrary.Proof) = new Induction(Some(v), Some(adt)) -} \ No newline at end of file diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala deleted file mode 100644 index 4b1ed5713..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Typed.scala +++ /dev/null @@ -1,285 +0,0 @@ -/** - * Gives a type theoretic interpretation to algebraic data types and functions over them. - */ - -package lisa.maths.settheory.types.adt - -import lisa.maths.settheory.SetTheory.{_, given} -import lisa.maths.settheory.types.TypeLib.any -import lisa.maths.settheory.types.TypeSystem.FunctionalClass -import lisa.maths.settheory.types.TypeSystem.TypedConstantFunctional - -/** - * Type theoretic interpretation of a constructor, that is a function whose type is - * - * `c :: ∀X1, ..., Xn. T1 -> ... -> Tn -> ADT - * - * @tparam N the number of type variables appearing in the definition of this constructor's ADT - * @param line the line at which this constructor is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param file the file in which this constructor is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param underlying the set theoretic underlying constructor - */ -class Constructor[N <: Arity] private[adt] (using line: sourcecode.Line, file: sourcecode.File)( - private[adt] val underlying: SemanticConstructor[N] -) extends TypedConstantFunctional[N]( - underlying.fullName, - underlying.typeArity, - FunctionalClass(Seq.fill(underlying.typeArity)(any), underlying.typeVariablesSeq, underlying.typ, underlying.typeArity), - underlying.intro - ) { - - /** - * Name of the constructor - * - * e.g `list/cons` or `list/nil` - */ - val name = underlying.fullName - - /** - * Theorem --- Introduction rule - * - * `c :: ∀X1, ..., Xn. T1 -> ... -> Tn -> ADT - * - * where `c` is this constructor, `ADT` the ADT it belongs to and `T1, ..., Tn` the domains of the constructor's arguments. - * X1, ..., Xn are the type variables of the ADT. - */ - val intro = - THM(underlying.intro.statement, s"${name} introduction rule", line.value, file.value, Theorem) { - have(underlying.intro) - } - - /** - * Theorem --- Injectivity - * - * ` c(x1, ..., xn) = c(y1, ..., yn) <=> x1 = y1 /\ ... /\ xn = yn` - */ - lazy val injectivity = - THM(underlying.injectivity.statement, s"${name} injectivity", line.value, file.value, Theorem) { - have(underlying.injectivity) - } - - /** - * Type variables appearing in the signature of this constructor - */ - val typeVariables: Variable ** N = underlying.typeVariables -} - -/** - * Type theoretic polymorphic inductive datatype. Comes with a structural induction schema, injection and pattern matching. - * - * @tparam N the number of type variables appearing in the definition of this ADT - * @param line the line at which this ADT is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param file the file in which this ADT is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param underlying - * @param constructors - */ -class ADT[N <: Arity] private[adt] (using line: sourcecode.Line, file: sourcecode.File)( - private[adt] val underlying: SemanticADT[N], - private[adt] val constructors: Seq[Constructor[N]] -) { - - /** - * Name of this ADT - */ - val name = underlying.name - - /** - * Identifier of this ADT. - */ - val id: Identifier = underlying.id - ADT.identifiersToADT.addOne(id -> this) - - /** - * Theorem --- Structural induction principle - * - * e.g. `P(nil) => (∀x :: T, l :: list(T). P(l) => P(cons(x, l)))) => ∀l :: list(T). P(l)` - */ - lazy val induction = - THM(underlying.induction.statement, s"${name} structural induction principle", line.value, file.value, Theorem) { - have(underlying.induction) - } - - /** - * Theorem --- Elimination rules (Pattern Matching) - * - * `x :: ADT |- ∃ x1, ..., xn. x = c1 * x1 * ... * xn \/ ... \/ ∃ x1, ..., xn'. x = cm * x1 * ... * xn' - * - * Every term of this ADT is an instance of one of its constructors. - * - * e.g. `∀l :: list(T). l = nil \/ ∃x, xs. l = cons(x, xs)` - */ - lazy val elim = - THM(underlying.elim.statement, s"${name} elimination rule", line.value, file.value, Theorem) { - have(underlying.elim) - } - - /** - * Theorem --- Injectivity - * - * ` c1 * x1 * ... * xn != c2 * y1 * ... * ym` - * - * Instances of different constructors are different. - * - * e.g. `cons(x, l) != nil` - * - * @param c1 the first constructor - * @param c2 the second constructor - */ - def injectivity(c1: Constructor[N], c2: Constructor[N]) = - val injectivityLemma = underlying.injectivity(c1.underlying, c2.underlying) - THM(injectivityLemma.statement, s"${c1.name}-${c2.name} injectivity", line.value, file.value, Theorem) { - have(injectivityLemma) - } - - /** - * Type variables appearing in the signature of this ADT - */ - val typeVariables: Variable ** N = underlying.typeVariables - - /** - * Instantiate the type variables of this ADT with given types. - * Checks the arity at runtime. - * - * @param args the types to instantiate the type variables with - */ - def applyUnsafe(args: Term ** N): Term = underlying.term(args.toSeq) - - /** - * Instantiate the type variables of this ADT with given types. - * Checks the arity at runtime. - * - * @param args the types to instantiate the type variables with - */ - def applySeq(args: Seq[Term]): Term = underlying.term(args) - - /** - * Instantiate the type variables of this ADT with given types. - * Checks the arity at runtime. - * - * TODO: wait Scala 3.4.2 to remove this method and extend Term ** N |-> Term instead - * - * @param args the types to instantiate the type variables with - */ - def apply(args: Term*): Term = underlying.term(args) -} - -private object ADT { - /** - * Global map from object identifiers to ADTs - */ - private val identifiersToADT: scala.collection.mutable.Map[Identifier, ADT[?]] = scala.collection.mutable.Map.empty - - /** - * Checks if a label is an ADT, and returns it if it is the case. - * - * @param l the label to check - */ - def unapply(l: Label[?]): Option[ADT[?]] = getADT(l.id) - - /** - * Checks if a term is an instance of an ADT and if it is the case, returns - * the appropriate instances of the type variables. - * - * @param term the term to check - */ - def unapply(obj: Term): Option[(ADT[?], Seq[Term])] = - obj match - case l: Label[?] => - val lwidened: Label[?] = l - unapply(lwidened).map((_, Seq.empty)) - case AppliedFunctional(l, args) => unapply(l).map((_, args)) - case _ => None - - /** - * Checks if a class is an instance of an ADT and if it is the case, returns - * the appropriate instances of the type variables. - * - * @param c the class to check - */ - def unapply(c: Class): Option[(ADT[?], Seq[Term])] = - c match - case t: Term => unapply(t) - case _ => None - - /** - * Returns the ADT associated with an identifier. - * - * @param id the identifier of the ADT - */ - def getADT(id: Identifier): Option[ADT[?]] = identifiersToADT.get(id) -} - -/** - * Type theoretic function over algebraic data types. Its definition is the direct sum of the definitions of its constructors. - * Comes with introduction and elimination rules. - * - * @constructor gives a type theoretic interpretation to a set theoretic function over an ADT - * @tparam N the number of type variables appearing in the definition of this function's domain - * @param line the line at which this ADT is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param file the file in which this ADT is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param underlying the underlying set theoretic function - * @param adt the domain of this function - */ -private class ADTFunction[N <: Arity](using line: sourcecode.Line, file: sourcecode.File)( - private val underlying: SemanticFunction[N], - private val adt: ADT[N] -) extends TypedConstantFunctional[N]( - underlying.fullName, - underlying.typeArity, - FunctionalClass(Seq.fill(underlying.typeArity)(any), underlying.typeVariablesSeq, underlying.typ, underlying.typeArity), - underlying.intro - ) { - - /** - * Name of the function - * - * e.g. list/length - */ - val name = underlying.fullName - - /** - * Theorem --- Elimination rules - * - * `f * (c * x1 * ... * xn) = case(c, x1, ..., xn)` - * - * That is, when this function is applied to a constructor, it returns the corresponding case. - */ - val elim: Map[Constructor[N], THM] = adt.constructors - .map(c => - ( - c, - THM(underlying.shortDefinition(c.underlying).statement, s"${name} elimination rule: ${c.name} case", line.value, file.value, Theorem) { - have(underlying.shortDefinition(c.underlying)) - } - ) - ) - .toMap - - /** - * Alias for [[this.elim]] - */ - val shortDefinition: Map[Constructor[N], THM] = elim - - /** - * Theorem --- Introduction rule - * - * `∀X1, ..., Xn. f(X1, ..., Xn) : ADT(X1, ..., Xn) -> T` - * - * where `f` is this function, `ADT` the ADT it takes argument and `T` its return type. - */ - val intro: THM = - THM(underlying.intro.statement, s"${name} introduction rule", line.value, file.value, Theorem) { - have(underlying.intro) - } - - /** - * Type variables in the signature of the function - */ - val typeVariables: Variable ** N = underlying.typeVariables -} diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala deleted file mode 100644 index 70087f4c2..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala +++ /dev/null @@ -1,1747 +0,0 @@ -/** - * This file implements tactics to generate polymorphic set theoretic inductive algebraic data types (or ADT) and prove properties about them. - * An algebraic data type is the least set closed under introduction rules, also known as constructors. - * A constructor takes arguments as input that can either belong to other types (non inductive arguments) - * or to the ADT itself (inductive arguments). - * - * An example of algebraic data type is the type of singly linked lists: - * - * list ::= nil() | cons(head: T, tail: list) - */ - -package lisa.maths.settheory.types.adt - -import lisa.maths.settheory.SetTheory.{*, given} -import lisa.maths.settheory.functions.* -import Helpers.* -import Helpers.{/\, \/, ===} -import ADTDefinitions.* -import ADTHelperTheorems as ADTThm -import ADTThm.{N, pair, pairExtensionality} -import lisa.maths.settheory.functions.|=> -import lisa.maths.settheory.types.TypeSystem.{ :: } -import lisa.maths.Quantifiers.{universalEquivalenceDistribution} -import lisa.fol.FOL.Variable - -/** - * Helpers for constructors - */ -private object Constructors { - - /** - * Global counter used to uniquely identify constructors and thereby avoid structural subtyping. - */ - var tagCounter = 0 -} - -/** - * Syntactic set theoretical interpretation of a constructor for an algebraic data type. - * In set theory, a constructor is a tuple containing the arguments it has been applied to, in addition to a tag - * uniquely identifying it. - * - * E.g. `cons(1, nil())` is represented as `(tagcons, (1, ((tagnil, ∅), ∅)))` - * - * Constructors injectivity is proved within this class. - * - * @constructor creates a new constructor out of a user specification - * @param specification types that the constructor takes as arguments - * @param variables1 variables used to represent the arguments of the constructor - * @param variables2 alternative set of variables to avoid capture issues - */ -private class SyntacticConstructor( - val specification: Seq[ConstructorArgument], - val variables1: Seq[Variable], - val variables2: Seq[Variable], - ) { - - /** - * Unique identifier of this constructor - */ - val tag: Int = Constructors.tagCounter - Constructors.tagCounter = Constructors.tagCounter + 1 - - /** - * Term representation of the tag of this constructor - */ - val tagTerm: Term = toTerm(tag) - - /** - * Sequence of variables used to represent the arguments of the constructor - */ - val variables: Seq[Variable] = variables1 - - /** - * Number of arguments that this constructor takes - */ - val arity: Int = specification.length - - /** - * Sequence of variables of the constructor with their respective domains. - */ - val signature1: Seq[(Variable, ConstructorArgument)] = variables1.zip(specification) - - /** - * Alternative sequence of variables of the constructor with their respective domains. - */ - val signature2: Seq[(Variable, ConstructorArgument)] = variables2.zip(specification) - - /** - * Sequence of variables of the constructor with their respective domains. - */ - val signature: Seq[(Variable, ConstructorArgument)] = signature1 - - /** - * Internally, an instance of this constructor is represented as a list. - * The first element of this list is the tag of this constructor. - * The following elements are its arguments. We represent lists as chained - * pairs followed by the empty set. - * - * e.g. cons(1, nil()) --> (tagcons, (1, ((tagnil, ∅), ∅))) - * - * @param args the arguments of this instance of the constructor - */ - def term(args: Seq[Term]): Term = pair(tagTerm, subterm(args)) - - /** - * Internal representation of an instance of this constructor in which arguments are schematic variables. - */ - val term1: Term = term(variables1) - - /** - * Internal representation of an instance of this constructor in which arguments are an alternative set of schematic variables. - */ - val term2: Term = term(variables2) - - /** - * Internal representation of an instance of this constructor in which arguments are schematic variables. - */ - val term: Term = term1 - - /** - * Internal representation of an instance of this constructor without the tag - * - * @param args the arguments of this instance of the constructor - * - * @see [[this.term]] - */ - def subterm(args: Seq[Term]): Term = args.foldRight[Term](emptySet)(pair(_, _)) - - /** - * Internal representation of an instance of this constructor without the tag, in which arguments are schematic variables. - */ - val subterm1: Term = subterm(variables1) - - /** - * Internal representation of an instance of this constructor without the tag, in which arguments are an alternative set - * of schematic variables. - */ - val subterm2: Term = subterm(variables2) - - /** - * Internal representation of an instance of this constructor without the tag, in which arguments are schematic variables. - */ - val subterm: Term = subterm1 - - /** - * Theorem --- Injectivity of constructors. - * - * Two instances of this constructor are equal if and only if all of their arguments are pairwise equal - * - * e.g. cons(head1, tail1) === cons(head2, tail2) <=> head1 === head2 /\ tail1 === tail2 - */ - lazy val injectivity = - if arity == 0 then - Lemma(term1 === term2) { - have(thesis) by RightRefl - } - else - Lemma((term1 === term2) <=> (variables1 === variables2)) { - - // STEP 1: Get rid of the tag using pair extensionality - have((term1 === term2) <=> (subterm1 === subterm2)) by Restate.from(pairExtensionality of (a := tagTerm, b := subterm1, c := tagTerm, d := subterm2)) - - // STEP 2: Repeat pair extensionality until all variables have been pulled out of the term - variables1 - .zip(variables2) - .foldLeft(Seq.empty[Variable], variables1, Seq.empty[Variable], variables2, lastStep)((acc, v) => - - // pulledVars1 are the variables that have been pulled out of the left term - // remainingVars1 are the variables that are still in the left term - // pulledVars2 are the variables that have been pulled out of the right term - // remainingVars2 are the variables that are still in the right term - val (pulledVars1, remainingVars1, pulledVars2, remainingVars2, previousFact) = acc - - // v1 and v2 are the variables that are being pulled out - val (v1, v2) = v - - val updatedPulledVars1 = pulledVars1 :+ v1 - val updatedPulledVars2 = pulledVars2 :+ v2 - val updatedRemainingVars1 = remainingVars1.tail - val updatedRemainingVars2 = remainingVars2.tail - - val subsubterm1 = subterm(updatedRemainingVars1) - val subsubterm2 = subterm(updatedRemainingVars2) - - have( - (pair(v1, subsubterm1) === pair(v2, subsubterm2)) <=> - ((v1 === v2) /\ (subsubterm1 === subsubterm2)) - ) by Restate.from(pairExtensionality of (a := v1, b := subsubterm1, c := v2, d := subsubterm2)) - have( - ((pulledVars1 === pulledVars2) /\ (pair(v1, subsubterm1) === pair(v2, subsubterm2))) <=> - ((pulledVars1 === pulledVars2) /\ (v1 === v2) /\ (subsubterm1 === subsubterm2)) - ) by Cut( - lastStep, - ADTThm.rightAndEquivalence of (p := pulledVars1 === pulledVars2, p1 := pair(v1, subsubterm1) === pair(v2, subsubterm2), p2 := (v1 === v2) /\ (subsubterm1 === subsubterm2)) - ) - val newFact = have( - (term1 === term2) <=> - ((updatedPulledVars1 === updatedPulledVars2) /\ (subsubterm1 === subsubterm2)) - ) by Apply(ADTThm.equivalenceRewriting).on(lastStep, previousFact) - - (updatedPulledVars1, updatedRemainingVars1, updatedPulledVars2, updatedRemainingVars2, newFact) - ) - } - -} - -/** - * Syntactic set theoretical interpretation of an algebraic data type. That is the least set closed under [[SyntacticConstructor]]. - * - * E.g. list is the smallest set containing nil and closed under the syntactic operation cons. - * - * Injectivity between different constructors, introduction rules and structural induction are proved within this class. - * - * @constructor creates a new algebraic data type out of a user specification. - * @param line the line at which the ADT is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param file the file in which the ADT is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param name the name of the ADT - * @param constructors constructors of the ADT - * @param typeVariables type variables used in the definition of this ADT - */ -private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: sourcecode.File)( - val name: String, - val constructors: Seq[SyntacticConstructor], - val typeVariables: Variable ** N, - ) { - - /** - * Sequence of type variables used in the definition of this ADT - */ - val typeVariablesSeq: Seq[Variable] = typeVariables.toSeq - - /** - * Number of type variables used in the definition of this ADT - */ - val typeArity: N = typeVariablesSeq.length.asInstanceOf[N] - - // *************** - // * INJECTIVITY * - // *************** - - /** - * Theorem --- Injectivity of constructors. - * - * Two instances of different construcors are always different. - * - * e.g. Nil != Cons(head, tail) - */ - def injectivity(c1: SyntacticConstructor, c2: SyntacticConstructor) = - require(c1.tag != c2.tag, "The given constructors must be different.") - - Lemma(!(c1.term1 === c2.term2)) { - - // STEP 0: Caching - val tagTerm1: Term = c1.tagTerm - val tagTerm2: Term = c2.tagTerm - - // STEP 1: Prove that the tags are different - val diffTag = have(!(tagTerm1 === tagTerm2)) subproof { - - // STEP 1.1: Order the tags - val minTag: Int = Math.min(c1.tag, c2.tag) - val maxTag: Int = Math.max(c1.tag, c2.tag) - - val start = have(tagTerm1 === tagTerm2 |- toTerm(maxTag) === toTerm(minTag)) by Restate - - // STEP 1.2: Apply successor injectivity to both tags until one becomes 0 - (1 to minTag).foldLeft(start)((fact, i) => - val midMaxTag = toTerm(maxTag - i) - val midMinTag = toTerm(minTag - i) - have(successor(midMaxTag) === successor(midMinTag) |- midMaxTag === midMinTag) by Cut( - ADTThm.successorInjectivity of (n := midMaxTag, m := midMinTag), - ADTThm.equivalenceApply of (p1 := successor(midMaxTag) === successor(midMinTag), p2 := midMaxTag === midMinTag) - ) - have(tagTerm1 === tagTerm2 |- midMaxTag === midMinTag) by Cut(fact, lastStep) - ) - - val chainInjectivity = thenHave(!(toTerm(maxTag - minTag) === emptySet) |- !(tagTerm1 === tagTerm2)) by Restate - - // STEP 1.3: Conclude using the fact that 0 is not the successor of any number - have(!(toTerm(maxTag - minTag) === emptySet)) by Exact(ADTThm.zeroIsNotSucc) - have(thesis) by Cut(lastStep, chainInjectivity) - } - - // STEP 2: Prove that the terms are different if the tags are different - have(c1.term1 === c2.term2 |- (tagTerm1 === tagTerm2) /\ (c1.subterm1 === c2.subterm2)) by Apply(ADTThm.equivalenceRevApply).on( - pairExtensionality of (a := tagTerm1, b := c1.subterm1, c := tagTerm2, d := c2.subterm2) - ) - thenHave(!(tagTerm1 === tagTerm2) |- !(c1.term1 === c2.term2)) by Weakening - - // STEP 3: Conclude - have(!(c1.term1 === c2.term2)) by Cut(diffTag, lastStep) - } - - // ************************* - // * INTRODUCTION FUNCTION * - // ************************* - - /** - * Formula describing whether the variables of a constructor belongs to their respective domain or s when they are self-referencing. - * - * @param c The considered constructor - * @param s The set of elements in which self-referencing variables of the constructor are. - */ - private def constructorVarsInDomain(c: SyntacticConstructor, s: Term): Formula = wellTypedFormula(c.signature)(s) - - /** - * Formula describing whether an element x is an instance of a specific constructor. - * - * @param c The constructor we want to check if x is an instance of - * @param x The element we want to check if it is an instance of c - * @param s The set of elements in which self-referencing arguments of the constructor are. - */ - private def isConstructor(c: SyntacticConstructor, x: Term, s: Term): Formula = - existsSeq(c.variables2, wellTypedFormula(c.signature2)(s) /\ (x === c.term2)) - - /** - * Formula describing whether an element x is an instance of one of this ADT's constructors. - * - * @param x The element we want to check if it is an instance of some constructor. - * @param s The set of elements in which self-referencing arguments of the constructor are. - */ - private def isConstructor(x: Term, s: Term): Formula = \/(constructors.map(c => isConstructor(c, x, s))) - - /** - * The introduction (class) function applies this ADT's constructors to the argument to given to it. - * It then adds to elements of the resulting set to the one given in argument. For example, if all arguments of the - * constructors were self-referencing we would have: - * - * introductionFunction(s) = {y | y = c(x1, ..., xn) for some c ∈ constructors and x1, ..., xn ∈ s} ∪ s - * - * In order to avoid introducing a new symbol in the theory, we describe this function with a predicate. - * - * @param s the argument of this function, i.e. set of elements on which the constructors are applied - * @param y the element we want to check if it is in the image of s under the introduction function. - * - * @return a formula describing whether y ∈ introductionFunction(s) - * - * @note The existence of the image of the introduction function is guaranteed by the union and replacement axioms. Moreover, it is not necessary to compute the union with s. It however simplifies further proofs. See [[this.heightSuccessorStrong]] for a proof of the equivalence of the two definitions. - */ - private def isInIntroductionFunctionImage(s: Term)(y: Term): Formula = isConstructor(y, s) \/ in(y, s) - - - /** - * Lemma --- The introduction function is monotonic with respect to set inclusion. - * - * `s ⊆ t |- introductionFunction(s) ⊆ introductionFunction(t)` - */ - private val introductionFunctionMononotic = Lemma(subset(s, t) |- isInIntroductionFunctionImage(s)(x) ==> isInIntroductionFunctionImage(t)(x)) { - // In the rest of the proof we assume that s ⊆ t - - // STEP 0: Caching predicates that are often used - val subsetST = subset(s, t) - val isConstructorXS = isConstructor(x, s) - val isConstructorXT = isConstructor(x, t) - - // STEP 1: Prove x ∈ s implies x ∈ t - have(subsetST |- forall(z, in(z, s) ==> in(z, t))) by Apply(ADTThm.equivalenceApply of (p1 := subsetST)).on(subsetAxiom.asInstanceOf) - val subsetElimination = thenHave(subsetST |- in(z, s) ==> in(z, t)) by InstantiateForall(z) - - // STEP 2: For each constructor, prove that if x is an instance of that constructor with self referencing arguments in s - // then it is also an instance of some constructor with self referencing arguments in t - val isConstructorXSImpliesT = - for c <- constructors yield - // STEP 2.0: Caching predicates that are often used - // TODO change identifier - val labelEq = x === c.term2 - val isConstructorCXS = isConstructor(c, x, s) - val isConstructorCXT = isConstructor(c, x, t) - val varsWellTypedS = wellTypedFormula(c.signature2)(s) - val varsWellTypedT = wellTypedFormula(c.signature2)(t) - - if c.arity == 0 then have((subsetST, isConstructorCXS) |- isConstructorXT) by Restate - else - // STEP 2.1: Prove that we can expand the domain of the (quantified) variables of the constructor - val andSeq = - for (v, ty) <- c.signature2 yield have((subsetST, varsWellTypedS) |- in(v, ty.getOrElse(t))) by Weakening(subsetElimination of (z := v)) - val expandingDomain = have((subsetST, varsWellTypedS) |- varsWellTypedT) by RightAnd(andSeq*) - val weakeningLabelEq = have(labelEq |- labelEq) by Hypothesis - have((subsetST, varsWellTypedS, labelEq) |- varsWellTypedT /\ labelEq) by RightAnd(expandingDomain, weakeningLabelEq) - - // STEP 2.2: Prove that x stays an instance of this constructor if we expand the domain of the variables - thenHave((subsetST, varsWellTypedS, labelEq) |- isConstructorCXT) by QuantifiersIntro(c.variables2) - thenHave((subsetST, varsWellTypedS /\ labelEq) |- isConstructorCXT) by LeftAnd - thenHave((subsetST, isConstructorCXS) |- isConstructorCXT) by QuantifiersIntro(c.variables2) - - // STEP 2.3: Weaken the conclusion to some constructor instead of a specific one - thenHave((subsetST, isConstructorCXS) |- isConstructorXT) by Weakening - - // STEP 3: Prove that this holds for any constructor - // ? Steps 2 and 3 can be merged and optimized through the repeated use of an external theorem like [[ADTHelperTheorems.unionPreimageMonotonic]] - if constructors.isEmpty then have((subsetST, isConstructorXS) |- isConstructorXT) by Restate - else have((subsetST, isConstructorXS) |- isConstructorXT) by LeftOr(isConstructorXSImpliesT*) - - // STEP 4: Prove the thesis by showing that making the union with the function argument does not change the monotonicity - thenHave(subsetST |- isConstructorXS ==> isConstructorXT) by RightImplies - have(thesis) by Cut(lastStep, ADTThm.unionPreimageMonotonic of (P := lambda(s, isConstructorXS))) - } - - - /** - * Lemma --- Every constructor is in the image of the introduction function. - * - * `For every c ∈ constructors, xi ∈ s, ..., xj ∈ s |- c(x1, ..., xn) ∈ introductionFunction(s)` - */ - private val constructorIsInIntroductionFunction = constructors - .map(c => - // Caching - val constructorVarsInDomainCS = constructorVarsInDomain(c, s) - - c -> Lemma(constructorVarsInDomainCS |- isInIntroductionFunctionImage(s)(c.term)) { - - have(constructorVarsInDomainCS |- constructorVarsInDomainCS /\ (c.term === c.term)) by Restate - - // Replace each variable on the LHS of the equality by a quantified variable and then introduce an existential quantifier - (c.variables2).foldRight((c.variables1, List[Variable]()))((v, acc) => - - // At each step remove a variable and add a quantified one - val oldVariables = acc._1.init - val newVariables = v :: acc._2 - val vars = oldVariables ++ newVariables - - thenHave(constructorVarsInDomainCS |- existsSeq(newVariables, wellTypedFormula(vars.zip(c.specification))(s) /\ (c.term(vars) === c.term))) by RightExists - - (oldVariables, newVariables) - ) - - thenHave(constructorVarsInDomainCS |- isInIntroductionFunctionImage(s)(c.term)) by Weakening - } - ) - .toMap - - // ********************************** - // * EXTENDED INTRODUCTION FUNCTION * - // ********************************** - - /** - * The extended introduction (class) function takes a function f as an argument instead of set. - * - If f is not empty, it calls the introduction function on the union of the ranges of the function. Since f will - * always be cumulative by assumption, this is equivalent as passing as argument the broadest set among the ranges of f. - * - If the function is empty, it returns the empty set. - * - * This class function is in a suited format to be used within the transfinite recursion theorem, which will be called to - * construct the height function. - * - * @see [[this.heightFunctionUniqueness]] - * - * @param f the function given as argument to the extended introduction function - * @param x the element we want to check if it is in the image of f under the extended introduction function - * @return a formula describing whether x ∈ extendedIntroductionFunction(f) - */ - private def isInExtendedIntroductionFunctionImage(f: Term)(x: Term): Formula = !(f === emptySet) /\ isInIntroductionFunctionImage(unionRange(f))(x) - - /** - * Lemma --- The extended introduction function is monotonic with respect to set inclusion. - * - * `f ⊆ g |- extendedIntroductionFunction(f) ⊆ extendedIntroductionFunction(g)` - */ - private val extendedIntroductionFunctionMonotonic = Lemma(subset(f, g) |- isInExtendedIntroductionFunctionImage(f)(x) ==> isInExtendedIntroductionFunctionImage(g)(x)) { - - // STEP 0: Caching - val introFunUnionRangeF = isInIntroductionFunctionImage(unionRange(f))(x) - val introFunUnionRangeG = isInIntroductionFunctionImage(unionRange(g))(x) - - // STEP 1: Instantiate monotonicity of the introduction function for the union of the ranges of f and g - have(subset(f, g) |- introFunUnionRangeF ==> introFunUnionRangeG) by Cut( - ADTThm.unionRangeMonotonic, - introductionFunctionMononotic of (s := unionRange(f), t := unionRange(g)) - ) - val left = thenHave((subset(f, g), introFunUnionRangeF) |- introFunUnionRangeG) by Restate - - // STEP 2: Conclude by applying the conjuction on both sides - have((subset(f, g), !(f === emptySet), introFunUnionRangeF) |- isInExtendedIntroductionFunctionImage(g)(x)) by RightAnd(left, ADTThm.subsetNotEmpty of (x := f, y := g)) - } - - // ******************* - // * HEIGHT FUNCTION * - // ******************* - - /** - * The height function assigns to each natural number the set of elements of the ADT of that height or below. - * The set of terms with height 0 is empty. Non inductive constructors have height one. - * The height of an instance of an inductive constructor is the maximum height of its arguments plus one. - * The height function is guaranteed to exists and is unique. - * - * @see [[this.heightFunctionUniqueness]] - * - * @param g the function we want to know if it is the height function - * - * @return a formula that is true if and only if g is the height function - */ - private def isTheHeightFunction(h: Term): Formula = - functional(h) /\ (relationDomain(h) === N) /\ forall(n, in(n, N) ==> forall(x, in(x, app(h, n)) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x))) - - // Caching - private val fIsTheHeightFunction: Formula = isTheHeightFunction(f) - private val hIsTheHeightFunction: Formula = isTheHeightFunction(h) - - /** - * Lemma --- There exists a unique height function for this ADT. - * - * `∃!h. h = height` - * - * TODO: Prove this using transfinite recursion - */ - private val heightFunUniqueness = Axiom(existsOne(h, hIsTheHeightFunction)) - - /** - * Lemma --- The height function exists. - * - * `∃h. h = height` - */ - private val heightFunctionExistence = Lemma(exists(h, hIsTheHeightFunction)) { - have(thesis) by Apply(lisa.maths.Quantifiers.existsOneImpliesExists of (P := lambda(h, hIsTheHeightFunction))).on(heightFunUniqueness.asInstanceOf) - } - - /** - * Lemma --- If two functions are the height function then they are the same. - * - * `f = height /\ h = height => f = h` - */ - private val heightFunctionUniqueness2 = Lemma((fIsTheHeightFunction, hIsTheHeightFunction) |- f === h) { - have(thesis) by Cut(heightFunUniqueness, ADTThm.existsOneUniqueness of (P := lambda(h, hIsTheHeightFunction), x := f, y := h)) - } - - /** - * Lemma --- The height function is not empty. - * - * `height ≠ ∅` - */ - private val heightFunctionNonEmpty = Lemma(hIsTheHeightFunction |- !(h === emptySet)) { - // The proof goes by contradiction. If the height function is empty then its domain is empty as well. - // This would imply that the set of natural numbers is empty, which is a contradiction. - have(N === emptySet |- ()) by Restate.from(ADTThm.natNotEmpty) - thenHave((relationDomain(h) === emptySet, relationDomain(h) === N, relationDomain(h) === relationDomain(h)) |- ()) by LeftSubstEq.withParametersSimple( - List((relationDomain(h), emptySet), (relationDomain(h), N)), - lambda((x, y), y === x) - ) - thenHave((relationDomain(h) === N, relationDomain(h) === relationDomain(h)) |- !(relationDomain(h) === emptySet)) by RightNot - have(thesis) by Apply(ADTThm.nonEmptyDomain).on(lastStep) - } - - /** - * Lemma --- The set of elements of height n or below is the image of the extended introduction function under the height - * function restricted to n (consequence of transfinite recursion). - * - * `height(n) = extendedIntroductionFunction(height | n)` - */ - private val heightApplication = Lemma((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, n)) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x)) { - - // Caching - val extendedIntroFunRestrictedFunM = isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x) - val heightFunApplicationDef = forall(n, in(n, N) ==> forall(x, in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM)) - - // Nothing fancy, just instantiations and restates - have(heightFunApplicationDef |- heightFunApplicationDef) by Hypothesis - thenHave(heightFunApplicationDef |- in(n, N) ==> forall(x, in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM)) by InstantiateForall(n) - thenHave((heightFunApplicationDef, in(n, N)) |- forall(x, in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM)) by Restate - thenHave((heightFunApplicationDef, in(n, N)) |- in(x, app(h, n)) <=> extendedIntroFunRestrictedFunM) by InstantiateForall(x) - thenHave(thesis) by Weakening - } - - /** - * Lemma --- The height function is monotonic - * - * `n <= m => height(n) ⊆ height(m)` - * - * TODO: Try to pull out - */ - private val heightMonotonic = Lemma((hIsTheHeightFunction, in(n, N), subset(m, n)) |- subset(app(h, m), app(h, n))) { - - // STEP 0: Caching - val extendedIntroFunRestrictedFunM = isInExtendedIntroductionFunctionImage(restrictedFunction(h, m))(x) - - // STEP 1: Unfold the definition of height(m) - have((hIsTheHeightFunction, in(n, N), subset(m, n)) |- in(x, app(h, m)) <=> extendedIntroFunRestrictedFunM) by Apply(heightApplication).on(ADTThm.subsetIsNat.asInstanceOf) - val unfoldHeightApplicationM = have((hIsTheHeightFunction, in(n, N), subset(m, n), in(x, app(h, m))) |- extendedIntroFunRestrictedFunM) by Cut( - lastStep, - ADTThm.equivalenceRevApply of (p1 := in(x, app(h, m)), p2 := extendedIntroFunRestrictedFunM) - ) - - // STEP 2: Use the monotonicity of the extended introduction function - have(subset(m, n) |- extendedIntroFunRestrictedFunM ==> isInExtendedIntroductionFunctionImage(restrictedFunction(h, n))(x)) by Cut( - ADTThm.restrictedFunctionDomainMonotonic of (x := m, y := n, f := h), - extendedIntroductionFunctionMonotonic of (f := restrictedFunction(h, m), g := restrictedFunction(h, n)) - ) - have((hIsTheHeightFunction, in(n, N), subset(m, n), extendedIntroFunRestrictedFunM) |- in(x, app(h, n))) by Apply(ADTThm.equivalenceRevApply).on(lastStep, heightApplication.asInstanceOf) - - // STEP 3: Fold the definition of subset - have((hIsTheHeightFunction, in(n, N), subset(m, n), in(x, app(h, m))) |- in(x, app(h, n))) by Cut(unfoldHeightApplicationM, lastStep) - thenHave((hIsTheHeightFunction, in(n, N), subset(m, n)) |- in(x, app(h, m)) ==> in(x, app(h, n))) by RightImplies - thenHave((hIsTheHeightFunction, in(n, N), subset(m, n)) |- forall(x, in(x, app(h, m)) ==> in(x, app(h, n)))) by RightForall - have(thesis) by Apply(ADTThm.equivalenceRevApply).on(lastStep, subsetAxiom.asInstanceOf) - } - - /** - * Lemma --- There is no element of height 0 in the ADT. - * - * `!∃x ∈ adt. height(x) = 0` - */ - private val heightZero = Lemma(hIsTheHeightFunction |- !in(x, app(h, emptySet))) { - - // This is due to the fact that the extended introduction function is the empty set when the function is empty - // (which happens when the height is set to 0). - have(hIsTheHeightFunction |- in(x, app(h, emptySet)) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, emptySet))(x)) by Cut(ADTThm.zeroIsNat, heightApplication of (n := emptySet)) - thenHave((restrictedFunction(h, emptySet) === emptySet, hIsTheHeightFunction) |- !in(x, app(h, emptySet))) by - RightSubstEq.withParametersSimple( - List((restrictedFunction(h, emptySet), emptySet)), - lambda(s, in(x, app(h, emptySet)) <=> isInExtendedIntroductionFunctionImage(s)(x)) - ) - have(thesis) by Cut(ADTThm.restrictedFunctionEmptyDomain, lastStep) - } - - /** - * Lemma --- The set of elements of height n + 1 is the set of elements of height n to which the introduction function is applied. - * - * `height(n + 1) = introductionFunction(height(n))` - */ - private val heightSuccessorWeak = Lemma((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> isInIntroductionFunctionImage(app(h, n))(x)) { - - // STEP 1: Prove that the restriction of height to n + 1 is not empty - val restrHeightNotEmpty: Formula = !(restrictedFunction(h, successor(n)) === emptySet) - have(!(h === emptySet) |- restrHeightNotEmpty) by Cut(ADTThm.zeroIsNotSucc, ADTThm.restrictedFunctionNotEmpty of (d := successor(n))) - val restrHeightNotEmptyLemma = have(hIsTheHeightFunction |- restrHeightNotEmpty) by Cut(heightFunctionNonEmpty, lastStep) - - // STEP 2: Use the fact that if the function is cumulative then ∪ range(height | n + 1) = height(n) to conclude the proof - have((hIsTheHeightFunction, in(n, N)) |- subset(m, n) ==> subset(app(h, m), app(h, n))) by RightImplies(heightMonotonic) - thenHave((hIsTheHeightFunction, in(n, N)) |- forall(m, subset(m, n) ==> subset(app(h, m), app(h, n)))) by RightForall - val unionRangeRestr = have((hIsTheHeightFunction, in(n, N)) |- unionRange(restrictedFunction(h, successor(n))) === app(h, n)) by Apply(ADTThm.unionRangeCumulativeRestrictedFunction).on(lastStep) - - have((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> isInExtendedIntroductionFunctionImage(restrictedFunction(h, successor(n)))(x)) by Apply(heightApplication).on( - ADTThm.equivalenceApply of (p1 := in(n, N)), - ADTThm.successorIsNat.asInstanceOf - ) - - thenHave( - (hIsTheHeightFunction, in(n, N), unionRange(restrictedFunction(h, successor(n))) === app(h, n)) |- - in(x, app(h, successor(n))) <=> restrHeightNotEmpty /\ isInIntroductionFunctionImage(app(h, n))(x) - ) by - RightSubstEq.withParametersSimple( - List((unionRange(restrictedFunction(h, successor(n))), app(h, n))), - lambda(s, in(x, app(h, successor(n))) <=> (restrHeightNotEmpty /\ isInIntroductionFunctionImage(s)(x))) - ) - - have((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> restrHeightNotEmpty /\ isInIntroductionFunctionImage(app(h, n))(x)) by Cut(unionRangeRestr, lastStep) - - have((hIsTheHeightFunction, in(n, N), restrHeightNotEmpty) |- in(x, app(h, successor(n))) <=> isInIntroductionFunctionImage(app(h, n))(x)) by Apply(ADTThm.equivalenceAnd of (p2 := restrHeightNotEmpty)) - .on(lastStep) - - have(thesis) by Cut(restrHeightNotEmptyLemma, lastStep) - } - - // ******** - // * TERM * - // ******** - - /** - * Formula describing this ADT's term, i.e. the set of all its instances. - * It equal to the union of all the terms that have a height. - * - * `adt = ∪ height(n) = {x | ∃n ∈ N. x ∈ height(n)}` - * - * @param adt the set chracterizing this ADT - */ - private def termDefinition(adt: Term): Formula = forall(t, in(t, adt) <=> forall(h, hIsTheHeightFunction ==> in(t, unionRange(h)))) - - /** - * Lemma --- There exists a unique set satisfying the definition of this ADT - * - * `∃!z. z = ADT - */ - private val termExistence = Lemma(existsOne(z, termDefinition(z))) { - - // STEP 0: Caching - val termDefinitionRight = forall(h, hIsTheHeightFunction ==> in(t, unionRange(h))) - val inUnionRangeF = in(t, unionRange(f)) - - // STEP 1: Prove that there exists a term satisfying the definition of this ADT. - // Specifically, this term is the union of all the terms with a height. - have(exists(z, termDefinition(z))) subproof { - - // STEP 1.1: Prove the forward implication of the definition, using the uniqueness of the height function - have(inUnionRangeF |- inUnionRangeF) by Hypothesis - thenHave((f === h, inUnionRangeF) |- in(t, unionRange(h))) by RightSubstEq.withParametersSimple( - List((f, h)), - lambda(f, inUnionRangeF) - ) - have((fIsTheHeightFunction, hIsTheHeightFunction, inUnionRangeF) |- in(t, unionRange(h))) by Cut(heightFunctionUniqueness2, lastStep) - thenHave((fIsTheHeightFunction, inUnionRangeF) |- hIsTheHeightFunction ==> in(t, unionRange(h))) by RightImplies - thenHave((fIsTheHeightFunction, inUnionRangeF) |- termDefinitionRight) by RightForall - val forward = thenHave(fIsTheHeightFunction |- inUnionRangeF ==> termDefinitionRight) by RightImplies - - // STEP 1.2: Prove the backward implication of the definition - have(termDefinitionRight |- termDefinitionRight) by Hypothesis - thenHave(termDefinitionRight |- fIsTheHeightFunction ==> inUnionRangeF) by InstantiateForall(f) - val backward = thenHave(fIsTheHeightFunction |- termDefinitionRight ==> inUnionRangeF) by Restate - - // STEP 1.3: Use the existence of the height function to prove the existence of this ADT - have(fIsTheHeightFunction |- inUnionRangeF <=> termDefinitionRight) by RightIff(forward, backward) - thenHave(fIsTheHeightFunction |- forall(t, inUnionRangeF <=> termDefinitionRight)) by RightForall - - thenHave(fIsTheHeightFunction |- exists(z, forall(t, in(t, z) <=> termDefinitionRight))) by RightExists - thenHave(exists(f, fIsTheHeightFunction) |- exists(z, forall(t, in(t, z) <=> termDefinitionRight))) by LeftExists - have(thesis) by Cut(heightFunctionExistence, lastStep) - } - - // STEP 2: Conclude using the extension by definition - - have(thesis) by Cut(lastStep, uniqueByExtension of (schemPred := lambda(t, termDefinitionRight))) - } - - /** - * Class function defining the ADT. Takes as parameters the type variables of the ADT and return the set of all its instances. - */ - val polymorphicTerm = FunctionDefinition[N](name, line.value, file.value)(typeVariablesSeq, z, termDefinition(z), termExistence).label - - /** - * The set of all instances of the ADT where the type variables are not instantiated (i.e. are kept variable). - */ - val term = polymorphicTerm.applySeq(typeVariablesSeq) - - /** - * Definition of this ADT's term. - */ - private val termDefinition: Formula = termDefinition(term) - - /** - * Lemma --- This ADT satisfies its definition. - * - * `adt = ∪ height(n)` - */ - private val termSatisfiesDefinition = Lemma(termDefinition) { - have(thesis) by InstantiateForall(term)(polymorphicTerm.definition) - } - - // ************************* - // * TYPING / INTRODUCTION * - // ************************* - - /** - * Lemma --- Every element of this ADT has a height. Conversely, if an element has a height, it is in this ADT. - * - * ` x ∈ ADT <=> ∃n ∈ N. x ∈ height(n)` - * - * TODO: Split into two lemmas - */ - private val termHasHeight = Lemma(hIsTheHeightFunction |- in(x, term) <=> ∃(n, in(n, N) /\ in(x, app(h, n)))) { - - // STEP 0 : Instantiate the definition of this ADT and recover the forward and backward implications - val termDefinition = have(in(x, term) <=> forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) by InstantiateForall(x)(termSatisfiesDefinition) - val termDefinitionForward = have(in(x, term) |- forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) by Cut( - termDefinition, - ADTThm.equivalenceApply of (p1 := in(x, term), p2 := forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) - ) - val termDefinitionBackward = have(forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))) |- in(x, term)) by Cut( - termDefinition, - ADTThm.equivalenceRevApply of (p2 := in(x, term), p1 := forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) - ) - - // STEP 1 : Prove that an element is in this ADT if and only if it is in one of the images of the height function. - have(hIsTheHeightFunction |- in(x, term) <=> in(x, unionRange(h))) subproof { - - // STEP 1.1 : Forward implication - have(forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))) |- forall(h, hIsTheHeightFunction ==> in(x, unionRange(h)))) by Hypothesis - thenHave(forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))) |- hIsTheHeightFunction ==> in(x, unionRange(h))) by InstantiateForall(h) - thenHave((forall(h, hIsTheHeightFunction ==> in(x, unionRange(h))), hIsTheHeightFunction) |- in(x, unionRange(h))) by Restate - - val forward = have(hIsTheHeightFunction |- in(x, term) ==> in(x, unionRange(h))) by Apply(lastStep).on(termDefinitionForward) - - // STEP 1.2 : Backward implication, follows from uniqueness of the height function - have(in(x, unionRange(h)) |- in(x, unionRange(h))) by Hypothesis - thenHave((f === h, in(x, unionRange(h))) |- in(x, unionRange(f))) by RightSubstEq.withParametersSimple(List((f, h)), lambda(h, in(x, unionRange(h)))) - have((fIsTheHeightFunction, hIsTheHeightFunction, in(x, unionRange(h))) |- in(x, unionRange(f))) by Cut(heightFunctionUniqueness2, lastStep) - thenHave((hIsTheHeightFunction, in(x, unionRange(h))) |- fIsTheHeightFunction ==> in(x, unionRange(f))) by RightImplies - thenHave((hIsTheHeightFunction, in(x, unionRange(h))) |- forall(f, fIsTheHeightFunction ==> in(x, unionRange(f)))) by RightForall - have((hIsTheHeightFunction, in(x, unionRange(h))) |- in(x, term)) by Cut(lastStep, termDefinitionBackward) - val backward = thenHave(hIsTheHeightFunction |- in(x, unionRange(h)) ==> in(x, term)) by RightImplies - - have(thesis) by RightIff(forward, backward) - } - - // STEP 2: Conclude by instantiating the union range membership lemma - have(hIsTheHeightFunction |- in(x, term) <=> ∃(n, in(n, relationDomain(h)) /\ in(x, app(h, n)))) by Apply(ADTThm.equivalenceRewriting).on(ADTThm.unionRangeMembership.asInstanceOf, lastStep) - - thenHave((hIsTheHeightFunction, relationDomain(h) === N) |- in(x, term) <=> ∃(n, in(n, N) /\ in(x, app(h, n)))) by RightSubstEq.withParametersSimple( - List((relationDomain(h), N)), - lambda(z, in(x, term) <=> ∃(n, in(n, z) /\ in(x, app(h, n)))) - ) - } - - /** - * Lemma --- Every element of this ADT has a height. Conversely, if an element has a height, it is in this ADT. - * - * ` xi, ..., xj ∈ ADT <=> ∃n ∈ N. xi, ..., xj ∈ height(n)` - * - * TODO: Work this out - * TODO: Split into two lemmas - */ - private val termsHaveHeight = constructors - .map(c => - c -> Lemma(hIsTheHeightFunction |- (constructorVarsInDomain(c, term) <=> ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n))))) { - - if c.variables.isEmpty then have(thesis) by Weakening(ADTThm.existsNat) - else - - // STEP 1: Backward implication - - val backward = have(hIsTheHeightFunction |- ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) ==> constructorVarsInDomain(c, term)) subproof { - val andSeq = for (v, ty) <- c.signature yield ty match - case Self => - val termHasHeightBackward = have((hIsTheHeightFunction, exists(n, in(n, N) /\ in(v, app(h, n)))) |- in(v, term)) by Cut( - termHasHeight of (x := v), - ADTThm.equivalenceRevApply of (p1 := ∃(n, in(n, N) /\ in(v, app(h, n))), p2 := in(v, term)) - ) - - have((in(n, N) /\ in(v, app(h, n))) |- in(n, N) /\ in(v, app(h, n))) by Restate - thenHave((in(n, N) /\ in(v, app(h, n))) |- exists(n, in(n, N) /\ in(v, app(h, n)))) by RightExists - have((hIsTheHeightFunction, in(n, N) /\ in(v, app(h, n))) |- in(v, term)) by Cut(lastStep, termHasHeightBackward) - thenHave((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- in(v, term)) by Weakening - case GroundType(t) => - have((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- in(v, t)) by Restate - - have((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- constructorVarsInDomain(c, term)) by RightAnd(andSeq*) - thenHave((hIsTheHeightFunction, exists(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) |- constructorVarsInDomain(c, term)) by LeftExists - } - - // STEP 2: Forward implication - - val forward = have(hIsTheHeightFunction |- constructorVarsInDomain(c, term) ==> ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) subproof { - val nSeq: Seq[Variable] = (0 until c.variables.size).map(i => Variable(s"n$i")) - val max = if c.arity == 0 then emptySet else nSeq.reduce[Term](setUnion(_, _)) - - val maxInN = have(/\(nSeq.map(n => in(n, N))) |- in(max, N)) by Sorry - - val andSeq = for ((v, ty), ni) <- c.signature.zip(nSeq) yield - val niInMax = have(subset(ni, max)) by Sorry - - ty match - case Self => - have((hIsTheHeightFunction, in(max, N), subset(ni, max)) |- subset(app(h, ni), app(h, max))) by Restate.from(heightMonotonic of (m := ni, n := max)) - have((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N)))) |- subset(app(h, ni), app(h, max))) by Sorry // Apply(lastStep).on(Seq(maxInN, niInMax), excluding = nSeq) - have((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N)))) |- forall(z, in(z, app(h, ni)) ==> in(z, app(h, max)))) by Apply(ADTThm.equivalenceApply) - .on(Seq(lastStep, subsetAxiom), excluding = nSeq) - thenHave((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N)))) |- in(v, app(h, ni)) ==> in(v, app(h, max))) by InstantiateForall(v) - thenHave((hIsTheHeightFunction, /\(nSeq.map(n => in(n, N))), in(v, app(h, ni))) |- in(v, app(h, max))) by Restate - case GroundType(t) => - have((/\(nSeq.map(n => in(n, N))), hIsTheHeightFunction, in(v, t)) |- in(v, t)) by Restate - - have((/\(nSeq.map(n => in(n, N))), hIsTheHeightFunction, in(v, ty.getOrElse(app(h, ni)))) |- in(max, N) /\ in(v, ty.getOrElse(app(h, max)))) by RightAnd(maxInN, lastStep) - thenHave(nSeq.map(n => in(n, N) /\ in(v, ty.getOrElse(app(h, n)))).toSet + hIsTheHeightFunction |- in(max, N) /\ in(v, ty.getOrElse(app(h, max)))) by Weakening - thenHave(nSeq.map(n => in(n, N) /\ in(v, ty.getOrElse(app(h, n)))).toSet + hIsTheHeightFunction |- ∃(n, in(n, N) /\ in(v, ty.getOrElse(app(h, n))))) by RightExists - - sorry - } - - // STEP 3: Conclude - have(thesis) by RightIff(forward, backward) - } - ) - .toMap - - /** - * Lemma --- If all inductive arguments of a constructor have height below n then the instance of - * this constructor has height below n + 1. - * - * ` xi, ..., xj ∈ height(n) |- c(x1, ..., xn) ∈ height(n + 1)` - */ - private val heightConstructor = constructors - .map(c => - c -> Lemma((hIsTheHeightFunction, in(n, N), constructorVarsInDomain(c, app(h, n))) |- in(c.term, app(h, successor(n)))) { - - // Caching - val constructorInIntroFunHeight = isInIntroductionFunctionImage(app(h, n))(c.term) - - // Chaining the lemma on the elements of height n + 1 and the one on constructors being in the image of the introduction function - have((hIsTheHeightFunction, in(n, N), constructorInIntroFunHeight) |- in(c.term, app(h, successor(n)))) by Cut( - heightSuccessorWeak of (x := c.term), - ADTThm.equivalenceRevApply of (p1 := constructorInIntroFunHeight, p2 := in(c.term, app(h, successor(n)))) - ) - have((hIsTheHeightFunction, in(n, N), constructorVarsInDomain(c, app(h, n))) |- in(c.term, app(h, successor(n)))) by Cut(constructorIsInIntroductionFunction(c) of (s := app(h, n)), lastStep) - } - ) - .toMap - - /** - * Lemma --- If all inductive arguments of a constructor are in this ADT, and the non inductive ones in their respective domain, - * then the instance of this constructor is in this ADT as well. Also known as introduction rules. - * - * ` xi, ..., xj ∈ ADT |- c(x1, ..., xn) ∈ ADT` - */ - val intro = constructors - .map(c => { - c -> - Lemma(simplify(constructorVarsInDomain(c, term)) |- simplify(in(c.term, term))) { - // STEP 0: Instantiate the forward direction of termsHaveHeight. - val termsHaveHeightForward = have((hIsTheHeightFunction, constructorVarsInDomain(c, term)) |- ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) by Cut( - termsHaveHeight(c), - ADTThm.equivalenceApply of (p1 := constructorVarsInDomain(c, term), p2 := exists(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) - ) - - // STEP 1: Prove that if an instance of a constructor has height n + 1 then it is in this ADT. - val left = have(in(n, N) |- in(successor(n), N)) by Cut(ADTThm.successorIsNat, ADTThm.equivalenceApply of (p1 := in(n, N), p2 := in(successor(n), N))) - val right = have(in(c.term, app(h, successor(n))) |- in(c.term, app(h, successor(n)))) by Hypothesis - have((in(n, N), in(c.term, app(h, successor(n)))) |- in(successor(n), N) /\ in(c.term, app(h, successor(n)))) by RightAnd(left, right) - thenHave((in(n, N), in(c.term, app(h, successor(n)))) |- exists(m, in(m, N) /\ in(c.term, app(h, m)))) by RightExists - have((hIsTheHeightFunction, in(n, N), in(c.term, app(h, successor(n)))) |- in(c.term, term)) by Apply(ADTThm.equivalenceRevApply).on(lastStep, termHasHeight.asInstanceOf) - - // STEP 2: Prove that if the inductive arguments of the constructor have height then the instance of the constructor is in the ADT. - have((hIsTheHeightFunction, in(n, N), constructorVarsInDomain(c, app(h, n))) |- in(c.term, term)) by Cut(heightConstructor(c), lastStep) - - // STEP 3: Prove that if the inductive arguments of the constructor are in the ADT then they have a height and therefore - // the instance of the constructor is in the ADT. - thenHave((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- in(c.term, term)) by LeftAnd - thenHave((hIsTheHeightFunction, exists(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) |- in(c.term, term)) by LeftExists - have((hIsTheHeightFunction, constructorVarsInDomain(c, term)) |- in(c.term, term)) by Cut(termsHaveHeightForward, lastStep) - - // STEP 4: Remove lingering assumptions - thenHave((exists(h, hIsTheHeightFunction), constructorVarsInDomain(c, term)) |- in(c.term, term)) by LeftExists - have(constructorVarsInDomain(c, term) |- in(c.term, term)) by Cut(heightFunctionExistence, lastStep) - } - }) - .toMap - - // ************************ - // * STRUCTURAL INDUCTION * - // ************************ - - /** - * Lemma --- An element has height n + 1 if and only if it is the instance of some constructor with inductive arguments of height n. - * - * ` x ∈ height(n + 1) <=> x = c(x1, ..., xn) for some c and xi, ..., xj ∈ height(n)` - */ - private lazy val heightSuccessorStrong = Lemma((hIsTheHeightFunction, in(n, N)) |- in(x, app(h, successor(n))) <=> isConstructor(x, app(h, n))) { - val forward = have((hIsTheHeightFunction, in(n, N)) |- isInIntroductionFunctionImage(app(h, n))(x) ==> isConstructor(x, app(h, n))) subproof { - - def inductionFormula(n: Term): Formula = isInIntroductionFunctionImage(app(h, n))(x) ==> isConstructor(x, app(h, n)) - val inductionFormulaN: Formula = inductionFormula(n) - val inductionFormulaSuccN: Formula = inductionFormula(successor(n)) - - // STEP 1.1 : Base case - val isContructorXHEmptySet = isConstructor(x, app(h, emptySet)) - val baseCaseLeft = have(isContructorXHEmptySet |- isContructorXHEmptySet) by Hypothesis - val baseCaseRight = have((hIsTheHeightFunction, in(x, app(h, emptySet))) |- ()) by Restate.from(heightZero) - have((hIsTheHeightFunction, isInIntroductionFunctionImage(app(h, emptySet))(x)) |- isContructorXHEmptySet) by LeftOr(baseCaseLeft, baseCaseRight) - thenHave(hIsTheHeightFunction |- isInIntroductionFunctionImage(app(h, emptySet))(x) ==> isContructorXHEmptySet) by RightImplies - val inductiveCaseRemaining = have((hIsTheHeightFunction, forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormulaSuccN))) |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut( - lastStep, - ADTThm.natInduction of (P := lambda(n, inductionFormulaN)) - ) - - // STEP 1.2: Unfolding the definition of subset - have(subset(app(h, n), app(h, successor(n))) |- forall(z, in(z, app(h, n)) ==> in(z, app(h, successor(n))))) by Cut( - subsetAxiom of (x := app(h, n), y := app(h, successor(n))), - ADTThm.equivalenceApply of (p1 := subset(app(h, n), app(h, successor(n))), p2 := forall(z, in(z, app(h, n)) ==> in(z, app(h, successor(n))))) - ) - val subsetElimination = thenHave(subset(app(h, n), app(h, successor(n))) |- in(y, app(h, n)) ==> in(y, app(h, successor(n)))) by InstantiateForall(y) - - // STEP 1.3 : Use monotonicity to prove that y ∈ height(n) => y ∈ height(n + 1) - have(in(n, N) |- in(successor(n), N)) by Cut(ADTThm.successorIsNat, ADTThm.equivalenceApply of (p1 := in(n, N), p2 := in(successor(n), N))) - have((hIsTheHeightFunction, in(n, N), subset(n, successor(n))) |- subset(app(h, n), app(h, successor(n)))) by Cut(lastStep, heightMonotonic of (n := successor(n), m := n)) - have((hIsTheHeightFunction, in(n, N)) |- subset(app(h, n), app(h, successor(n)))) by Cut(ADTThm.subsetSuccessor, lastStep) - val liftHeight = have((hIsTheHeightFunction, in(n, N)) |- in(y, app(h, n)) ==> in(y, app(h, successor(n)))) by Cut(lastStep, subsetElimination) - - // STEP 1.4 : Generalize the above result to show that if for some c, x = c(x1, ..., xn) with xi, ..., xj ∈ height(n) - // then for some c', x = c'(x1, ..., xn) with xi, ..., xj ∈ height(n + 1). - - // Caching - val isConstructorXHN = isConstructor(x, app(h, n)) - val isConstructorXHSuccN = isConstructor(x, app(h, successor(n))) - val liftConstructorHeight = - if constructors.size == 0 then have((hIsTheHeightFunction, in(n, N), isConstructorXHN) |- isConstructorXHSuccN) by Restate - else - val liftConstructorHeightOrSequence = - for c <- constructors yield - - // Caching - val isConstructorCXHN = isConstructor(c, x, app(h, n)) - val isConstructorCXHSuccN = isConstructor(c, x, app(h, successor(n))) - val constructorVarsInHN = constructorVarsInDomain(c, app(h, n)) - val constructorVarsInHSuccN = constructorVarsInDomain(c, app(h, successor(n))) - - if c.arity == 0 then have((hIsTheHeightFunction, in(n, N), isConstructorCXHN) |- isConstructorCXHSuccN) by Restate - else - val liftHeightAndSequence = - for (v, ty) <- c.signature - yield have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- in(v, ty.getOrElse(app(h, successor(n))))) by Weakening(liftHeight of (y := v)) - - val left = have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInHSuccN) by RightAnd(liftHeightAndSequence*) - val right = have(x === c.term |- x === c.term) by Hypothesis - - have((hIsTheHeightFunction, in(n, N), constructorVarsInHN, (x === c.term)) |- constructorVarsInHSuccN /\ (x === c.term )) by RightAnd( - left, - right - ) - thenHave((hIsTheHeightFunction, in(n, N), constructorVarsInHN /\ (x === c.term)) |- constructorVarsInHSuccN /\ (x === c.term)) by LeftAnd - thenHave((hIsTheHeightFunction, in(n, N), constructorVarsInHN /\ (x === c.term)) |- isConstructorCXHSuccN) by QuantifiersIntro(c.variables) - thenHave((hIsTheHeightFunction, in(n, N), isConstructorCXHN) |- isConstructorCXHSuccN) by QuantifiersIntro(c.variables) - - thenHave((hIsTheHeightFunction, in(n, N), isConstructorCXHN) |- isConstructorXHSuccN) by Weakening - - have((hIsTheHeightFunction, in(n, N), isConstructorXHN) |- isConstructorXHSuccN) by LeftOr(liftConstructorHeightOrSequence*) - - // STEP 1.5: Show that x ∈ introductionFunction(height(n + 1)) => for some c, x = c(x1, ..., xn) - // with xi, ..., xj ∈ height(n + 1). - val heightSuccessorWeakForward = have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n)))) |- isInIntroductionFunctionImage(app(h, n))(x)) by Cut( - heightSuccessorWeak, - ADTThm.equivalenceApply of (p1 := in(x, app(h, successor(n))), p2 := isInIntroductionFunctionImage(app(h, n))(x)) - ) - have((inductionFormulaN, isInIntroductionFunctionImage(app(h, n))(x)) |- isConstructorXHN) by Restate - have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n))), inductionFormulaN) |- isConstructorXHN) by Cut(heightSuccessorWeakForward, lastStep) - val right = have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n))), inductionFormulaN) |- isConstructorXHSuccN) by Cut(lastStep, liftConstructorHeight) - val left = have(isConstructorXHSuccN |- isConstructorXHSuccN) by Hypothesis - have((hIsTheHeightFunction, in(n, N), inductionFormulaN, isInIntroductionFunctionImage(app(h, successor(n)))(x)) |- isConstructorXHSuccN) by LeftOr(left, right) - - // STEP 1.6: Conclude - thenHave((hIsTheHeightFunction, in(n, N), inductionFormulaN) |- inductionFormulaSuccN) by RightImplies - thenHave((hIsTheHeightFunction, in(n, N)) |- inductionFormulaN ==> inductionFormulaSuccN) by RightImplies - thenHave(hIsTheHeightFunction |- in(n, N) ==> (inductionFormulaN ==> inductionFormulaSuccN)) by RightImplies - thenHave(hIsTheHeightFunction |- forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormulaSuccN))) by RightForall - have(hIsTheHeightFunction |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut(lastStep, inductiveCaseRemaining) - thenHave(hIsTheHeightFunction |- in(n, N) ==> inductionFormulaN) by InstantiateForall(n) - } - - // STEP 2: Prove the backward implication - val backward = have((hIsTheHeightFunction, in(n, N)) |- isConstructor(x, app(h, n)) ==> isInIntroductionFunctionImage(app(h, n))(x)) by Restate - - // STEP 3: Conclude - have((hIsTheHeightFunction, in(n, N)) |- isInIntroductionFunctionImage(app(h, n))(x) <=> isConstructor(x, app(h, n))) by RightIff(forward, backward) - have(thesis) by Apply(ADTThm.equivalenceRewriting).on(lastStep, heightSuccessorWeak.asInstanceOf) - } - - /** - * Generates the structural inductive case for a given constructor. - * - * @param c the constructor - */ - lazy val inductiveCase: Map[SyntacticConstructor, Formula] = constructors - .map(c => - c -> c.signature.foldRight[Formula](P(c.term))((el, fc) => - val (v, ty) = el - ty match - case Self => forall(v, in(v, term) ==> (P(v) ==> fc)) - case GroundType(t) => forall(v, in(v, t) ==> fc) - ) - ) - .toMap - - /** - * Lemma --- Structural induction principle for this ADT. - * - * `base cases => inductive cases => ∀x ∈ ADT. P(x)` - */ - lazy val induction = Lemma(constructors.foldRight[Formula](forall(x, in(x, term) ==> P(x)))((c, f) => inductiveCase(c) ==> f)) { - - // List of cases to prove for structural induction to hold - val structuralInductionPreconditions: Formula = /\(constructors.map(inductiveCase)) - - // We want to prove the claim by induction on the height of n, i.e. prove that for any - // n in N, P holds. - def inductionFormula(n: Term): Formula = forall(x, in(x, app(h, n)) ==> P(x)) - val inductionFormulaN: Formula = inductionFormula(n) - - // STEP 1: Prove the base case - have(hIsTheHeightFunction |- in(x, app(h, emptySet)) ==> P(x)) by Weakening(heightZero) - val zeroCase = thenHave(hIsTheHeightFunction |- inductionFormula(emptySet)) by RightForall - - val inductiveCaseRemaining = have((hIsTheHeightFunction, forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormula(successor(n))))) |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut( - zeroCase, - ADTThm.natInduction of (P := lambda(n, inductionFormulaN)) - ) - - // STEP 2: Prove the inductive case - val succCase = have((hIsTheHeightFunction, structuralInductionPreconditions) |- forall(n, in(n, N) ==> (inductionFormulaN ==> inductionFormula(successor(n))))) subproof { - - // STEP 2.1 : Prove that if the x = c(x1, ..., xn) for some c and xi, ..., xj ∈ height(n) then P(x) holds. - val isConstructorImpliesP = have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(x, app(h, n))) |- P(x)) subproof { - - if constructors.isEmpty then have(thesis) by Restate - else - val orSeq = - (for c <- constructors yield - - // Caching - val constructorPrecondition = inductiveCase(c) - val constructorVarsInHN = constructorVarsInDomain(c, app(h, n)) - val constructorVarsInHNEx = ∃(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) - val constructorVarsInTerm = constructorVarsInDomain(c, term) - - // STEP 2.1.1: Prove that if xi, ..., xj ∈ height(n) then xi, ..., xj ∈ ADT. - val constructorQuantVarsInHNToTerm = have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInTerm) subproof { - have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- in(n, N) /\ constructorVarsInHN) by Restate - val consVarL = thenHave((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInHNEx) by RightExists - have((constructorVarsInTerm <=> constructorVarsInHNEx, constructorVarsInHNEx) |- constructorVarsInTerm) by Restate.from( - ADTThm.equivalenceRevApply of (p1 := constructorVarsInTerm, p2 := constructorVarsInHNEx) - ) - have((hIsTheHeightFunction, constructorVarsInHNEx) |- constructorVarsInTerm) by Cut( - termsHaveHeight(c), - lastStep - ) - have(thesis) by Cut(consVarL, lastStep) - } - - - // STEP 2.1.2: Prove that if xi, ..., xj ∈ height(n) then P(c(x1, ..., xn)). - have((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- constructorPrecondition) by Restate - - c.signature - .foldLeft(lastStep)((fact, el) => - val (v, ty) = el - - fact.statement.right.head match - case Forall(_, factCclWithoutForall) => - thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- factCclWithoutForall) by InstantiateForall(v) - - factCclWithoutForall match - case Implies(membership, subformula) => - ty match - case Self => - subformula match - case Implies(hypothesis, subSubFormula) => - val proofSubSubFormula = thenHave( - (hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInTerm, constructorVarsInHN, P(v)) |- subSubFormula - ) by Weakening - - have(inductionFormulaN |- inductionFormulaN) by Hypothesis - thenHave(inductionFormulaN |- in(v, app(h, n)) ==> P(v)) by InstantiateForall(v) - thenHave((inductionFormulaN, constructorVarsInHN) |- P(v)) by Weakening - - have((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInTerm, constructorVarsInHN) |- subSubFormula) by Cut( - lastStep, - proofSubSubFormula - ) - have((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- subSubFormula) by Cut( - constructorQuantVarsInHNToTerm, - lastStep - ) - - case _ => throw UnreachableException - - case GroundType(t) => - thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- subformula) by Restate - case _ => throw UnreachableException - case _ => throw UnreachableException - ) - - thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN) |- P(c.term)) by Restate - - // STEP 2.1.3: Prove that if xi, ..., xj ∈ height(n) then P(x). - thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN, x === c.term) |- P(x)) by RightSubstEq - .withParametersSimple(List((x, c.term)), lambda(x, P(x))) - - thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, constructorVarsInHN /\ (x === c.term)) |- P(x)) by LeftAnd - - thenHave((hIsTheHeightFunction, constructorPrecondition, in(n, N), inductionFormulaN, isConstructor(c, x, app(h, n))) |- P(x)) by QuantifiersIntro(c.variables) - thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(c, x, app(h, n))) |- P(x)) by Weakening - ).toSeq - - - have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(x, app(h, n))) |- P(x)) by LeftOr(orSeq*) - } - - // STEP 2.2: Prove that if x ∈ height(n + 1) then P(x) holds. - have((hIsTheHeightFunction, in(n, N), in(x, app(h, successor(n)))) |- isConstructor(x, app(h, n))) by Cut( - heightSuccessorStrong, - ADTThm.equivalenceApply of (p1 := in(x, app(h, successor(n))), p2 := isConstructor(x, app(h, n))) - ) - have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, in(x, app(h, successor(n)))) |- P(x)) by Cut(lastStep, isConstructorImpliesP) - - // STEP 2.3: Conclude - thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN) |- in(x, app(h, successor(n))) ==> P(x)) by RightImplies - - thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN) |- inductionFormula(successor(n))) by RightForall - thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N)) |- inductionFormulaN ==> inductionFormula(successor(n))) by RightImplies - thenHave((hIsTheHeightFunction, structuralInductionPreconditions) |- in(n, N) ==> (inductionFormulaN ==> inductionFormula(successor(n)))) by RightImplies - thenHave(thesis) by RightForall - } - - // STEP 3: Conclude - - have((hIsTheHeightFunction, structuralInductionPreconditions) |- forall(n, in(n, N) ==> inductionFormulaN)) by Cut(lastStep, inductiveCaseRemaining) - thenHave((hIsTheHeightFunction, structuralInductionPreconditions) |- in(n, N) ==> inductionFormulaN) by InstantiateForall(n) - thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N)) |- inductionFormulaN) by Restate - thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N)) |- in(x, app(h, n)) ==> P(x)) by InstantiateForall(x) - thenHave((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N) /\ in(x, app(h, n))) |- P(x)) by Restate - val exImpliesP = thenHave((hIsTheHeightFunction, structuralInductionPreconditions, exists(n, in(n, N) /\ in(x, app(h, n)))) |- P(x)) by LeftExists - have((hIsTheHeightFunction, in(x, term)) |- exists(n, in(n, N) /\ in(x, app(h, n)))) by Cut(termHasHeight, ADTThm.equivalenceApply of (p1 := in(x, term), p2 := exists(n, in(n, N) /\ in(x, app(h, n))))) - - have((hIsTheHeightFunction, structuralInductionPreconditions, in(x, term)) |- P(x)) by Cut(lastStep, exImpliesP) - thenHave((exists(h, hIsTheHeightFunction), structuralInductionPreconditions, in(x, term)) |- P(x)) by LeftExists - have((structuralInductionPreconditions, in(x, term)) |- P(x)) by Cut(heightFunctionExistence, lastStep) - thenHave(structuralInductionPreconditions |- in(x, term) ==> P(x)) by RightImplies - thenHave(structuralInductionPreconditions |- forall(x, in(x, term) ==> P(x))) by RightForall - } - -} - -/** - * Semantic set theoretical interpretation of a constructor for an algebraic data type. - * That is a function from the arguments' domains to the set of instances of the algebraic data type. - * - * `c : T1 -> ... -> Tn -> ADT` - * - * Since polymorphism is supported, this function is parametrized by the type variables appearing inside - * the specification of the ADT. In this sense, a constructor is a class function whose parameters are - * type variables and whose body is the set theoretic function detailed above. With polymorphism, the signature - * thus becomes: - * - * `c(X1, ..., Xn) : T1(X1, ..., Xn) -> ... -> Tn(X1, ..., Xn) -> ADT(X1, ..., Xn)` - * - * Injectivity and introduction rule are proven within this class. - * - * @constructor generates a class function for this constructor - * @param line the line at which this constructor is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param file the file in which this constructor is defined. Usually fetched automatically by the compiler. - * Used for error reporting - * @param name the name of this constructor - * @param underlying the syntactic constructor - * @param adt the algebraic data type to which this constructor belongs - */ -private class SemanticConstructor[N <: Arity](using line: sourcecode.Line, file: sourcecode.File)( - val name: String, - val underlying: SyntacticConstructor, - val adt: SyntacticADT[N], -) { - /** - * Full name of this constructor, i.e. concatenation of the ADT name and this constructor name. - */ - val fullName: String = s"${adt.name}/${name}" - - /** - * Type variables that may appear in the signature of this constructor. - */ - val typeVariables: Variable ** N = adt.typeVariables - - /** - * Sequence of type variables that may appear in the signature of this constructor. - */ - val typeVariablesSeq: Seq[Variable] = adt.typeVariablesSeq - - /** - * Number of type variables in the signature of this constructor. - */ - val typeArity: N = adt.typeArity - - /** - * Variables used for constructor arguments. - */ - val variables: Seq[Variable] = underlying.variables - - /** - * Variables used for constructor arguments. - */ - val variables1: Seq[Variable] = underlying.variables1 - - /** - * Alternative set of variables used for constructor arguments. - */ - val variables2: Seq[Variable] = underlying.variables2 - - /** - * Set of variables for this constructor with their respective domain or a - * special symbol in case the domain is the ADT. - * - * @param vars variables - */ - def syntacticSignature(vars: Seq[Variable]): Seq[(Variable, ConstructorArgument)] = - vars.zip(underlying.specification) - - /** - * Variables of this constructor with their respective domain or a special symbol in case the domain is the ADT. - */ - val syntacticSignature: Seq[(Variable, ConstructorArgument)] = underlying.signature - - /** - * Constructor arguments with their respective domains. - * - * @param vars this constructor arguments - */ - def semanticSignature(vars: Seq[Variable]): Seq[(Variable, Term)] = vars.zip(underlying.specification.map(_.getOrElse(adt.term))) - - /** - * Variables of this constructor with their respective domains. - */ - val semanticSignature: Seq[(Variable, Term)] = semanticSignature(variables) - - /** - * Variables of this constructor with their respective domains. - */ - val semanticSignature1: Seq[(Variable, Term)] = semanticSignature - - /** - * Alternative set of variables of this constructor with their respective domain. - */ - val semanticSignature2: Seq[(Variable, Term)] = semanticSignature(variables2) - - /** - * Type of this constructor. - */ - val typ: Term = semanticSignature.unzip._2.foldRight[Term](adt.term)((a, b) => a |=> b) - - /** - * Arity of this constructor. - */ - val arity: Int = variables.size - - /** - * Internal representation of this constructor (i.e. as a tuple). - */ - val structuralTerm: Term = underlying.term - /** - * Internal representation of this constructor (i.e. as a tuple). - */ - val structuralTerm1: Term = underlying.term1 - /** - * Internal representation of this constructor (i.e. as a tuple) with an alternative set of variables. - */ - val structuralTerm2: Term = underlying.term2 - - /** - * Definition of this constructor. - * - * Formally it is the only function whose codomain is the ADT such that for all variables x1 :: S1, ...,xn :: Sn - * c * x1 * ... * xn = (tagc, (x1, (..., (xn, ∅)...)) - */ - private val untypedDefinition = (c :: typ) /\ forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appSeq(c)(variables) === structuralTerm)) - - /** - * Lemma --- Uniqueness of this constructor. - * - * ` ∃!c. c ∈ T1 -> ... -> Tn -> ADT /\ ∀x1, ..., xn. c * x1 * ...* xn = (tagc, (x1, (..., (xn, ∅)...))` - */ - private val uniqueness = Axiom(existsOne(c, untypedDefinition)) - - /** - * Class function representing this constructor - */ - private val classFunction = FunctionDefinition[N](fullName, line.value, file.value)(typeVariablesSeq, c, untypedDefinition, uniqueness).label - - /** - * Identifier of this constructor. - */ - val id: Identifier = classFunction.id - - /** - * This constructor in which type variables are instantiated. - * - * @param args the instances of this constructor's type variables - */ - def term(args: Seq[Term]): Term = classFunction.applySeq(args) - - /** - * Constructor where type variables are instantiated with schematic variables. - */ - private val term: Term = term(typeVariablesSeq) - - /** - * Constructor where type variables are instantiated with schematic variables and arguments instantiated. - * - * @param args the instances of this constructor arguments - */ - def appliedTerm(args: Seq[Term]): Term = appSeq(term)(args) - - /** - * Constructor where type variables and arguments are instantiated with schematic variables. - */ - val appliedTerm: Term = appliedTerm(variables) - - /** - * Constructor where type variables and arguments are instantiated with schematic variables. - */ - val appliedTerm1: Term = appliedTerm - - /** - * Constructor where type variables and arguments are instantiated with schematic variables. - * Arguments variables are however drawn from an alternative set of variables. - */ - val appliedTerm2: Term = appliedTerm(variables2) - - /** - * Lemma --- This constructor is equal to its internal representation. - * - * `∀x1, ..., xn. c * x1 * ... * xn = (tagc, (x1, (..., (xn, ∅)...))` - */ - val shortDefinition = Lemma(forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appliedTerm === structuralTerm))) { - have(forall(c, (term === c) <=> untypedDefinition)) by Exact(classFunction.definition) - thenHave((term === term) <=> ((term :: typ) /\ forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appliedTerm === structuralTerm)))) by InstantiateForall(term) - thenHave(thesis) by Weakening - } - - /** - * Lemma --- Introduction rule for this constructor. - * - * `∀A1, ..., Am. c(X1, ..., Xm) ∈ T1(X1, ..., Xm) -> ... -> Tn(X1, ..., Xm) -> ADT(X1, ..., Xm)` - * - * where Ai are the type variables of the ADT and Ti are domains of this constructor arguments. - * - * e.g. `∀T. nil(T) ∈ list(T)` and `∀T. cons(T) ∈ T -> list(T) -> list(T)` - */ - val intro = Lemma(forallSeq(typeVariablesSeq, term :: typ)) { - have(forall(c, (term === c) <=> untypedDefinition)) by Exact(classFunction.definition) - thenHave((term === term) <=> ((term :: typ) /\ forallSeq(variables, wellTypedFormula(semanticSignature) ==> (appliedTerm === structuralTerm)))) by InstantiateForall(term) - thenHave(term :: typ) by Weakening - thenHave(thesis) by QuantifiersIntro(typeVariablesSeq) - } - - /** - * Theorem --- Injectivity of constructors. - * - * Two instances of this constructor are equal if and only if all of their arguments are pairwise equal - * - * e.g. Cons(head1, tail1) === Cons(head2, tail2) <=> head1 === head2 /\ tail1 === tail2 - */ - lazy val injectivity = - val vars1WellTyped: Set[Formula] = wellTypedSet(semanticSignature1) - val vars2WellTyped: Set[Formula] = wellTypedSet(semanticSignature2) - - if arity == 0 then - Lemma(appliedTerm1 === appliedTerm2) { - have(thesis) by RightRefl - } - else - Lemma(vars1WellTyped ++ vars2WellTyped |- simplify((appliedTerm1 === appliedTerm2) <=> (variables1 === variables2))) { - - have(forallSeq(variables1, wellTypedFormula(semanticSignature1) ==> (appliedTerm1 === structuralTerm1))) by Restate.from(shortDefinition) - - variables1.foldLeft(lastStep)((fact, v) => - fact.statement.right.head match - case Forall(_, phi) => thenHave(phi) by InstantiateForall(v) - case _ => throw UnreachableException - ) - val tappTerm1Def = thenHave(vars1WellTyped |- appliedTerm1 === structuralTerm1) by Restate - - // println(forallSeq(variables1, wellTypedFormula(semanticSignature1) ==> (appliedTerm1 === structuralTerm1))) - // println(forallSeq(variables2, wellTypedFormula(semanticSignature2) ==> (appliedTerm2 === structuralTerm))) - have(forallSeq(variables2, wellTypedFormula(semanticSignature2) ==> (appliedTerm2 === structuralTerm2))) by Restate.from(shortDefinition) - - variables2.foldLeft(lastStep)((fact, v) => - fact.statement.right.head match - case Forall(_, phi) => thenHave(phi) by InstantiateForall(v) - case _ => throw UnreachableException - ) - val tappTerm2Def = thenHave(vars2WellTyped |- appliedTerm2 === structuralTerm2) by Restate - - - val s0 = have(vars2WellTyped + (appliedTerm1 === appliedTerm2) |- appliedTerm1 === structuralTerm2) by Cut(tappTerm2Def, - ADTThm.altEqualityTransitivity of (x := appliedTerm1, y := appliedTerm2, z := structuralTerm2)) - have(vars1WellTyped + (appliedTerm1 === structuralTerm2) |- structuralTerm1 === structuralTerm2) by Cut(tappTerm1Def, - ADTThm.altEqualityTransitivity of (x := structuralTerm1, y := appliedTerm1, z := structuralTerm2)) - have((vars1WellTyped ++ vars2WellTyped) + (appliedTerm1 === appliedTerm2) |- structuralTerm1 === structuralTerm2) by Cut(s0, lastStep) - val forward = thenHave(vars1WellTyped ++ vars2WellTyped |- (appliedTerm1 === appliedTerm2) ==> (structuralTerm1 === structuralTerm2)) by RightImplies - - val s1 = have(vars1WellTyped + (structuralTerm1 === structuralTerm2) |- appliedTerm1 === structuralTerm2) by Cut(tappTerm1Def, - ADTThm.altEqualityTransitivity of (x := appliedTerm1, y := structuralTerm1, z := structuralTerm2)) - have(vars2WellTyped + (appliedTerm1 === structuralTerm2) |- appliedTerm1 === appliedTerm2) by Cut(tappTerm2Def, - ADTThm.altEqualityTransitivity of (x := appliedTerm1, y := structuralTerm2, z := appliedTerm2)) - have((vars1WellTyped ++ vars2WellTyped) + (structuralTerm1 === structuralTerm2) |- appliedTerm1 === appliedTerm2) by Cut(s1, lastStep) - val backward = thenHave(vars1WellTyped ++ vars2WellTyped |- (structuralTerm1 === structuralTerm2) ==> (appliedTerm1 === appliedTerm2)) by RightImplies - - val definitionUnfolding = have(vars1WellTyped ++ vars2WellTyped |- (appliedTerm1 === appliedTerm2) <=> (structuralTerm1 === structuralTerm2)) by RightIff(forward, backward) - have((appliedTerm1 === appliedTerm2) <=> (structuralTerm1 === structuralTerm2) |- (appliedTerm1 === appliedTerm2) <=> /\(variables1.zip(variables2).map(_ === _))) by Sorry - Cut( - underlying.injectivity, - ADTThm.equivalenceRewriting of (p1 := (appliedTerm1 === appliedTerm2), p2 := (structuralTerm1 === structuralTerm2), p3 := /\(variables1.zip(variables2).map(_ === _))) - ) - have(thesis) by Cut(definitionUnfolding, lastStep) - } - - - /** - * Case generated by this constructor when performing a proof by induction - */ - lazy val inductiveCase: Formula = - syntacticSignature.foldRight[Formula](P(appliedTerm1)) - ((el, fc) => - val (v, typ) = el - typ match - case Self => forall(v, v :: adt.term ==> (P(v) ==> fc)) - case GroundType(t) => forall(v, v :: t ==> fc) - ) -} - -/** - * Semantic set theoretical interpretation of an algebraic data type. That is the least set closed under [[SemanticConstructor]]. - * - * E.g. list is the smallest set containing nil and closed under the cons function. - * - * Injectivity between different constructors, structural induction and elimination rule are proved within this class. - * - * @constructor generates a semantic interpretation for this ADT out of a syntactic one - * @param underlying the syntactic representation of this ADT - * @param constructors constructors of this ADT - */ - private class SemanticADT[N <: Arity]( - val underlying: SyntacticADT[N], - val constructors: Seq[SemanticConstructor[N]] - ) { - - /** - * Name of this ADT. - */ - val name: String = underlying.name - - /** - * Identifier of this ADT. - */ - val id: Identifier = underlying.polymorphicTerm.id - - /** - * Type variables of this ADT. - */ - val typeVariables: Variable ** N = underlying.typeVariables - - /** - * Sequence of type variables of this ADT. - */ - val typeVariablesSeq: Seq[Variable] = underlying.typeVariablesSeq - - /** - * Number of type variables in this ADT. - */ - val typeArity: N = underlying.typeArity - - /** - * Term representing this ADT where type variables are instantiated with given arguments. - * - * @param args the instances of this ADT type variables - */ - def term(args: Seq[Term]) = underlying.polymorphicTerm.applySeq(args) - - /** - * Term representing this ADT where type variables are instantiated with schematic variables. - */ - val term: Term = underlying.term - - /** - * Theorem --- Injectivity of constructors. - * - * Two instances of different construcors are always different. - * - * e.g. Nil != Cons(head, tail) - */ - def injectivity(c1: SemanticConstructor[N], c2: SemanticConstructor[N]) = - - val vars1WellTyped: Set[Formula] = wellTypedSet(c1.semanticSignature1) - val vars2WellTyped: Set[Formula] = wellTypedSet(c2.semanticSignature2) - - Lemma(vars1WellTyped ++ vars2WellTyped |- !(c1.appliedTerm1 === c2.appliedTerm2)) { - - val defUnfolding = have((vars1WellTyped ++ vars2WellTyped) + (c1.appliedTerm1 === c2.appliedTerm2) |- c1.structuralTerm1 === c2.structuralTerm2) subproof { - have(forallSeq(c1.variables1, wellTypedFormula(c1.semanticSignature1) ==> (c1.appliedTerm1 === c1.structuralTerm1))) by Restate.from(c1.shortDefinition) - - c1.variables1.foldLeft(lastStep)((fact, v) => - fact.statement.right.head match - case Forall(_, phi) => thenHave(phi.substitute(v := x)) by InstantiateForall(x) - case _ => throw UnreachableException - ) - val tappTerm1Def = thenHave(vars1WellTyped |- c1.structuralTerm1 === c1.appliedTerm1) by Restate - - have(forallSeq(c2.variables2, wellTypedFormula(c2.semanticSignature2) ==> (c2.appliedTerm2 === c2.structuralTerm2))) by Restate.from(c2.shortDefinition) - - c2.variables2.foldLeft(lastStep)((fact, v) => - fact.statement.right.head match - case Forall(_, phi) => thenHave(phi) by InstantiateForall(v) - case _ => throw UnreachableException - ) - val tappTerm2Def = thenHave(vars2WellTyped |- c2.appliedTerm2 === c2.structuralTerm2) by Restate - - val s0 = have(vars2WellTyped + (c1.appliedTerm1 === c2.appliedTerm2) |- c1.appliedTerm1 === c2.structuralTerm2) by Cut( - tappTerm2Def, - ADTThm.altEqualityTransitivity of (x := c1.appliedTerm1, y := c2.appliedTerm2, z := c2.structuralTerm2) - ) - have(vars1WellTyped + (c1.appliedTerm1 === c2.structuralTerm2) |- c1.structuralTerm1 === c2.structuralTerm2) by Cut( - tappTerm1Def, - ADTThm.altEqualityTransitivity of (x := c1.structuralTerm1, y := c1.appliedTerm1, z := c2.structuralTerm2) - ) - have(thesis) by Cut(s0, lastStep) - } - - have(!(c1.structuralTerm1 === c2.structuralTerm2)) by Restate.from(underlying.injectivity(c1.underlying, c2.underlying)) - thenHave(c1.structuralTerm1 === c2.structuralTerm2 |- ()) by Restate - - have((vars1WellTyped ++ vars2WellTyped) + (c1.appliedTerm1 === c2.appliedTerm2) |- ()) by Cut(defUnfolding, lastStep) - } - - /** - * Theorem --- Structural induction principle for this ADT. - * - * `base cases => inductive cases => ∀x ∈ ADT. P(x)` - */ - lazy val induction = Lemma(constructors.foldRight[Formula](forall(x, x :: term ==> P(x)))((c, f) => c.inductiveCase ==> f)) { sp ?=> - constructors.foldRight[(Formula, Formula, sp.Fact)] { - val prop = forall(x, x :: term ==> P(x)) - (prop, prop, have(prop <=> prop) by Restate) - }((c, acc) => - val (oldBefore, oldAfter, fact) = acc - val newBefore = underlying.inductiveCase(c.underlying) ==> oldBefore - val newAfter = c.inductiveCase ==> oldAfter - - have(underlying.inductiveCase(c.underlying) <=> c.inductiveCase) subproof { - val wellTypedVars: Seq[Formula] = wellTyped(c.semanticSignature) - val wellTypedVarsSet = wellTypedVars.toSet - - - have(forallSeq(c.variables, wellTypedFormula(c.semanticSignature) ==> (c.appliedTerm === c.structuralTerm))) by Restate.from(c.shortDefinition) - if c.arity > 0 then - c.variables1.foldLeft(lastStep)((l, _) => - lastStep.statement.right.head match - case Forall(v, phi) => thenHave(phi) by InstantiateForall(v) - case _ => throw UnreachableException - ) - - val eq = thenHave(wellTypedVarsSet |- c.appliedTerm === c.structuralTerm) by Restate - have(P(c.appliedTerm) <=> P(c.appliedTerm)) by Restate - thenHave(c.structuralTerm === c.appliedTerm |- P(c.structuralTerm) <=> P(c.appliedTerm)) by RightSubstEq.withParametersSimple( - List((c.structuralTerm, c.appliedTerm)), - lambda(s, P(c.structuralTerm) <=> P(s)) - ) - have(wellTypedVarsSet |- P(c.structuralTerm) <=> P(c.appliedTerm)) by Cut(eq, lastStep) - - c.syntacticSignature - .foldRight[(Formula, Formula, Seq[Formula])]((P(c.structuralTerm), P(c.appliedTerm), wellTypedVars))((el, fc) => - val (v, ty) = el - val (fc1, fc2, wellTypedVars) = fc - ty match - case Self => - val wellTypedV: Formula = v :: term - have(wellTypedVars |- (P(v) ==> fc1) <=> (P(v) ==> fc2)) by Cut(lastStep, ADTThm.leftImpliesEquivalenceWeak of (p := P(v), p1 := fc1, p2 := fc2)) - thenHave(wellTypedVars.init |- wellTypedV ==> ((P(v) ==> fc1) <=> (P(v) ==> fc2))) by RightImplies - have(wellTypedVars.init |- (wellTypedV ==> (P(v) ==> fc1)) <=> (wellTypedV ==> (P(v) ==> fc2))) by Cut( - lastStep, - ADTThm.leftImpliesEquivalenceStrong of (p := wellTypedV, p1 := P(v) ==> fc1, p2 := P(v) ==> fc2) - ) - thenHave(wellTypedVars.init |- forall(v, (wellTypedV ==> (P(v) ==> fc1)) <=> (wellTypedV ==> (P(v) ==> fc2)))) by RightForall - have(wellTypedVars.init |- forall(v, (wellTypedV ==> (P(v) ==> fc1))) <=> forall(v, (wellTypedV ==> (P(v) ==> fc2)))) by Cut( - lastStep, - universalEquivalenceDistribution of (P := lambda(v, wellTypedV ==> (P(v) ==> fc1)), Q := lambda(v, wellTypedV ==> (P(v) ==> fc2))) - ) - (forall(v, wellTypedV ==> (P(v) ==> fc1)), forall(v, wellTypedV ==> (P(v) ==> fc2)), wellTypedVars.init) - case GroundType(t) => - thenHave(wellTypedVars.init |- v :: t ==> (fc1 <=> fc2)) by RightImplies - have(wellTypedVars.init |- (in(v, t) ==> fc1) <=> (v :: t ==> fc2)) by Cut(lastStep, ADTThm.leftImpliesEquivalenceStrong of (p := in(v, t), p1 := fc1, p2 := fc2)) - thenHave(wellTypedVars.init |- forall(v, (in(v, t) ==> fc1) <=> (v :: t ==> fc2))) by RightForall - have(wellTypedVars.init |- forall(v, (in(v, t) ==> fc1)) <=> forall(v, (v :: t ==> fc2))) by Cut( - lastStep, - universalEquivalenceDistribution of (P := lambda(v, in(v, t) ==> fc1), Q := lambda(v, v :: t ==> fc2)) - ) - (forall(v, (in(v, t) ==> fc1)), forall(v, (v :: t ==> fc2)), wellTypedVars.init) - ) - } - (newBefore, newAfter, have(newBefore <=> newAfter) by Apply(ADTThm.impliesEquivalence).on(lastStep, fact)) - ) - have(underlying.induction.statement.right.head |- thesis.right.head) by Cut( - lastStep, - ADTThm.equivalenceApply of ( - p1 := underlying.induction.statement.right.head, p2 := thesis.right.head - ) - ) - have(thesis) by Cut(underlying.induction, lastStep) - } - - /** - * Returns a map binding each constructor to formula describing whether x is an instance of it. - */ - private lazy val isConstructorMap: Map[SemanticConstructor[N], Formula] = - constructors.map(c => c -> existsSeq(c.variables, wellTypedFormula(c.semanticSignature) /\ (x === c.appliedTerm))).toMap - - /** - * Returns a formula describing whether x is an instance of one of this ADT's constructors. - */ - private lazy val isConstructor = - \/(constructors.map(c => isConstructorMap(c))) - - /** - * Theorem --- Pattern matching principle (also known as elimination rule) for this ADT. - * - * `x ∈ ADT |- x = c * x1 * ... * xn for some constructor c and xi, ..., xj ∈ ADT` - */ - lazy val elim = Lemma(x :: term |- simplify(isConstructor)) { - - // Induction preconditions with P(z) = z != x - val inductionPreconditionIneq = constructors.map(c => c -> c.inductiveCase.substitute((P -> lambda(z, !(x === z))))).toMap - val inductionPreconditionsIneq = /\(inductionPreconditionIneq.map(_._2)) - - // Weakening of the negation of the induction preconditions - val weakNegInductionPreconditionIneq: Map[SemanticConstructor[N], Formula] = constructors - .map(c => - c -> - c.semanticSignature - .foldRight[Formula](x === c.appliedTerm)((el, fc) => - val (v, t) = el - exists(v, (v :: t) /\ fc) - ) - ) - .toMap - - // STEP 1: Prove that if the induction preconditions with P(z) = z != x do not hold then x is the instance of some constructor - val strengtheningOfInductionPreconditions = have(!inductionPreconditionsIneq |- isConstructor) subproof { - if constructors.isEmpty then have(thesis) by Restate - else - - // STEP 1.1: Prove the claim for each constructor - val negInductionPreconditionsOrSequence = - for c <- constructors yield - - // STEP 1.1.1: Prove the strengthening of the negations of the induction preconditions - val conditionStrenghtening = have(!inductionPreconditionIneq(c) |- weakNegInductionPreconditionIneq(c)) subproof { - have(x === c.appliedTerm |- x === c.appliedTerm) by Hypothesis - - c.syntacticSignature - .foldRight(lastStep)((el, fact) => - val (v, ty) = el - val left = fact.statement.left.head - val right = fact.statement.right.head - - ty match - case Self => - thenHave(!(x === v) /\ left |- right) by Weakening - case _ => () - - val weakr = thenHave(in(v, ty.getOrElse(term)) /\ left |- right) by Weakening - val weakl = have(in(v, ty.getOrElse(term)) /\ left |- in(v, ty.getOrElse(term))) by Restate - - have((v :: ty.getOrElse(term)) /\ left |- (v :: ty.getOrElse(term)) /\ right) by RightAnd(weakl, weakr) - thenHave((v :: ty.getOrElse(term)) /\ left |- exists(v, (v :: ty.getOrElse(term)) /\ right)) by RightExists - thenHave(exists(v, (v :: ty.getOrElse(term)) /\ left) |- exists(v, (v :: ty.getOrElse(term)) /\ right)) by LeftExists - ) - - } - - // STEP 1.1.2: Conclude - // TODO: Change to a more efficient way of proving this - have(weakNegInductionPreconditionIneq(c) |- isConstructorMap(c)) by Tableau - have(!inductionPreconditionIneq(c) |- isConstructorMap(c)) by Cut(conditionStrenghtening, lastStep) - thenHave(!inductionPreconditionIneq(c) |- isConstructor) by Weakening - - have(thesis) by LeftOr(negInductionPreconditionsOrSequence*) - } - - // STEP 2: Conclude - have(inductionPreconditionsIneq |- forall(z, z :: term ==> !(x === z))) by Restate.from(induction of (P := lambda(z, !(x === z)))) - thenHave(inductionPreconditionsIneq |- x :: term ==> !(x === x)) by InstantiateForall(x) - val ind = thenHave(x :: term |- !inductionPreconditionsIneq) by Restate - have(x :: term |- isConstructor) by Cut(lastStep, strengtheningOfInductionPreconditions) - } - } diff --git a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala b/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala deleted file mode 100644 index 7af2af24c..000000000 --- a/lisa-topology/src/main/scala/lisa/maths/settheory/types/adt/package.scala +++ /dev/null @@ -1,200 +0,0 @@ -package lisa.maths.settheory.types - -/** - * Provides definitional mechanisms for Algebraic Data Types (ADTs) and tactics for reasoning about them. - * - * ===Usage=== - * - * In order to use ADTs, you need to import - * {{{ - * lisa.maths.settheory.types.adt.{*, given} - * }}} - * - * ====Defining an ADT==== - * - * ADTs can be defined using the following syntax: - * {{{ - * val define(adtName: ADT[N], constructors(c1, ..., cn)) = (X1, .., Xk) --> (T1, ..., Tn) | ... | (S1, ..., Sm) - * }}} - * - * where: - * - `adtName` is the identifier of the ADT - * - `N` is the number of poylmorphic variables - * - `c1 ... cn` are the identifiers of the different constructors of the ADT, - * - `X1, ..., Xk` are the polymorphic variables - * - `T1, ..., Tn, S1, ..., Sm` are the types defining the signature of each constructor. - * Note that `adtName` and `c1 ... cn` are scala identifiers, respectively referring to the ADT and the constructors. - * In addition, you cannot define two ADT with the same name, even in different scopes, as they introduce a new global definition. - * Type variables must be declarated as variables before being used. - * Here are some examples of ADT definitions: - * {{{ - * - * //variables declaration - * val A = variable - * - * // Definition of booleans with two constructors tru and fals. Each of them take no argument so their signature is () - * val define(bool: ADT, constructors(tru, fals)) = () | () - * - * // Definition of options with two constructors none and some. Options are polymorphic over a type variable A. - * // Their second constructor, some, has signature A has it takes only a value of type A. - * val define(option: ADT, constructors(none, some)) = A -> () | A - * - * // Recursive definition of lists with two constructors nil and cons. Lists are polymorphic over a type variable A. - * // The constructor cons takes two arguments of respectively type A and list(A) and append an element to - * // a sublist. - * val define(list: ADT, constructors(nil, cons)) = A -> () | (A, list) - * - * //Definition of natural numbers. - * val define(nat: ADT, constructors(zero, succ)) = () | nat - * }}} - * - * ====Using an ADT and its theorem==== - * - * Each constructor of an ADT comes with an introduction rule and an injectivity theorem. - * The introduction rule gives the type of the constructor and is of the form - * - * `∀ X1, ..., Xk. c(X1, ..., Xk) :: T1 -> ... Tn -> ADT(X1, ..., Xk)` - * - * The injection theorem stipulates that two instances of the same constructor are equal if and only if all their arguments are equal. - * - * For examples for lists we have - * - * nil introduction: `∀ A. nil(A) :: list(A)` - * - * cons introduction: `∀ A. cons(A) :: A -> list(A) -> list(A)` - * - * nil injectivity: `nil(A) = nil(A)` - * - * cons injectivity: `cons(A) * x0 * x1 = cons(A) * y0 * y1 <=> x0 = x1 /\ y0 = y1` - * - * Each ADT comes with an structural induction schema, an elimination rule (or pattern matching principle) and an injectivity theorem. - * - * Structural induction states that if a statement is true for each constructor assuming that the proposition is true for the constructor's - * arguments, then it is true for every element of the ADT. - * - * e.g. list induction: ` P(nil(A)) => (∀ x0 :: A. ∀ x1 :: list(A). P(x1) => P(cons * x0 * x1)) => ∀ x :: list(A). P(x)` - * - * Elimination rule is a direct consequence of induction and states that each instance of an ADT is an instance of one of its constructors - * - * e.g. list elimination: ` x :: list(A) => x = nil(A) \/ ∃ x0, x1. x = cons(A) * x0 * x1` - * - * Finally injectivity tells us that instances of different constructors are different - * - * e.g. nil/cons injectivity: `nil(A) != cons(A) * x0 * x1` - * - * ====Type Checking==== - * - * We provide a tactic to automatically type check instances of algebraic data types. - * It can be called using `TypeChecker.prove`. For examples instances of lists can be - * typechecked as follow. - * - * {{{ - * have(nil(A) :: list(A)) by TypeChecker.prove - * have((x :: A, l :: list(A)) |- cons * x * l :: list(A)) by TypeChecker.prove - * }}} - * - * ====Induction Tactic==== - * - * We provide a tactic for performing proofs by induction. - * The tactic can prove goals of the form - * - * ` ∀ x :: adt. P(x)` - * - * To work the user needs to provide a proof of P(c * x0 * ... * xn) for each constructor. - * The syntax of the tactic is of the following form: - * {{{ - * have(∀ x :: adt. P(x)) by Induction(adt) { - * Case(c1, v1, ..., vn) { - * // proof of (v1 :: T1, ..., vn :: Tn, P(vi), ..., P(vj)) |- P(c1 * v0 * ... * vn) - * } - * ... - * Case(ck, v1, ..., vm) { - * // proof of (v1 :: S1, ..., vm :: Sm, P(vi), ..., P(vj)) |- P(ck * v0 * ... * vn) - * } - * } - * }}} - * - * For lists it would look like: - * {{{ - * have(∀ x :: list(A). P(x)) by Induction(list) { - * Case(nil) { - * // ... - * have(P(nil)) by //Tactic to conclude the subcase - * } - * Case(cons, x, l) { - * // ... - * have((x :: A, l :: list(A), P(l)) |- P(cons * x * l)) by //Tactic to conclude the subcase - * } - * } - * }}} - * - * ====Defining functions==== - * - * Functions over ADT can also be defined via the following syntax: - * - * {{{ - * val myFunction = fun(adt, returnType) { - * Case(c1, v1, ..., vn) { - * body1 - * } - * ... - * Case(ck, v1, ..., vm) { - * bodyk - * } - * } - * }}} - * - * The body of each case is automatically typechecked at runtime. - * For example let's define the predecessor function over natural numbers - * - * {{{ - * val pred = fun(nat, nat) { - * Case(zero) { - * zero - * } - * Case(succ, n) { - * n - * } - * } - * }}} - * - * Recursive functions are not yet supported. - * - * ====Using functions==== - * - * Functions come with an introduction and elimination rules. - * Their introduction rule is of the form - * - * `∀ X1, ..., Xk. f(X1, ..., Xk) :: ADT(X1, ..., Xk) -> T` - * - * where `T` is the return type of the function - * - * On the other hand, we have an elimination rule for each constructor, giving the result of the function when applied to some - * constructor - * - * ` f * (ck * v1 * ... * vn) = body(ck) ` - * - * For example, for the `pred` function defined above the introduction rule would be - * - * `pred :: nat -> nat` - * - * and its elimination rules - * - * ` pred * zero = zero` - * - * ` pred * (succ * n) = n` - * - * Functions are also fully compatible with the typechecking tactic. We can for instance write: - * - * {{{ - * have(n :: nat |- pred * succ * n :: nat) - * }}} - * - * - */ -package object adt { - export ADTSyntax.{ |, define, constructors, adt_to_term, fun_to_term, constructor_to_term, Case, fun, -->} - export ADTSyntax.ADTBuilder.| - export lisa.maths.settheory.types.TypeSystem.* -} - diff --git a/lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala b/lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala deleted file mode 100644 index 34bc77eee..000000000 --- a/lisa-topology/src/test/scala/lisa/automation/CongruenceTest.scala +++ /dev/null @@ -1,913 +0,0 @@ -package lisa.automation -import lisa.fol.FOL.{*, given} -import lisa.automation.Congruence.* -import lisa.automation.Congruence -import org.scalatest.funsuite.AnyFunSuite - - -class CongruenceTest extends AnyFunSuite with lisa.TestMain { - - - given lib: lisa.SetTheoryLibrary.type = lisa.SetTheoryLibrary - - val a = variable - val b = variable - val c = variable - val d = variable - val e = variable - val f = variable - val g = variable - val h = variable - val i = variable - val j = variable - val k = variable - val l = variable - val m = variable - val n = variable - val o = variable - - val x = variable - - val F = function[1] - - val one = variable - val two = variable - val * = SchematicFunctionLabel("*", 2) - val << = SchematicFunctionLabel("<<", 2) - val / = SchematicFunctionLabel("/", 2) - - - val af = formulaVariable - val bf = formulaVariable - val cf = formulaVariable - val df = formulaVariable - val ef = formulaVariable - val ff = formulaVariable - val gf = formulaVariable - val hf = formulaVariable - val if_ = formulaVariable - val jf = formulaVariable - val kf = formulaVariable - val lf = formulaVariable - val mf = formulaVariable - val nf = formulaVariable - val of = formulaVariable - - val xf = formulaVariable - - val Ff = SchematicConnectorLabel("Ff", 1) - val Fp = SchematicPredicateLabel("Fp", 1) - - val onef = formulaVariable - val twof = formulaVariable - val `*f` = SchematicConnectorLabel("*f", 2) - val `< cf, ef <=> ff, if_ <=> kf, mf <=> nf, af <=> bf, - of <=> mf, if_ <=> mf, gf <=> hf, lf <=> kf, bf <=> cf, ff <=> ef, of <=> if_, gf <=> ef, if_ <=> jf) - - val test1 = Theorem(base |- bf <=> cf) { - egraph.proveInnerFormula(bf, cf, base |- ()) - } - - val test2 = Theorem(base |- ff <=> hf) { - egraph.proveInnerFormula(ff, hf, base |- ()) - } - - val test3 = Theorem(base |- if_ <=> of) { - egraph.proveInnerFormula(if_, of, base |- ()) - } - - val test4 = Theorem(base |- of <=> if_) { - egraph.proveInnerFormula(of, if_, base |- ()) - } - - } - - test("4 formulas with congruence test with proofs") { - val egraph = new EGraphTerms() - egraph.add(Ff(af)) - egraph.add(Ff(bf)) - egraph.merge(af, bf) - val test5 = Theorem(af <=> bf |- Ff(af) <=> Ff(bf)) { - egraph.proveInnerFormula(Ff(af), Ff(bf), (af <=> bf) |- ()) - } - } - - test("divide-mult-shift by 2 in formulas egraph test with proofs") { - val egraph = new EGraphTerms() - egraph.add(onef) - egraph.add(twof) - egraph.add(af) - val ax2 = egraph.add(`*f`(af, twof)) - val ax2_d2 = egraph.add(`/f`(`*f`(af, twof), twof)) - val `2d2` = egraph.add(`/f`(twof, twof)) - val ax_2d2 = egraph.add(`*f`(af, `/f`(twof, twof))) - val ax1 = egraph.add(`*f`(af, onef)) - val as1 = egraph.add(`< as1, ax2_d2 <=> ax_2d2, `2d2` <=> onef, ax1 <=> af) - - val one_2d2 = Theorem(base |- onef <=> `2d2`) { - egraph.proveInnerFormula(onef, `2d2`, base |- ()) - } - - val ax2_as1 = Theorem(base |- ax2 <=> as1) { - egraph.proveInnerFormula(ax2, as1, base |- ()) - } - - val ax2_d2_ax_2d2 = Theorem(base |- ax2_d2 <=> ax_2d2) { - egraph.proveInnerFormula(ax2_d2, ax_2d2, base |- ()) - } - - val ax_2d2_ax1 = Theorem(base |- ax_2d2 <=> ax1) { - egraph.proveInnerFormula(ax_2d2, ax1, base |- ()) - } - - val ax_2d2_a = Theorem(base |- ax_2d2 <=> af) { - egraph.proveInnerFormula(ax_2d2, af, base |- ()) - } - - } - - test("long chain of formulas congruence eGraph with proofs") { - val egraph = new EGraphTerms() - egraph.add(xf) - val fx = egraph.add(Ff(xf)) - val ffx = egraph.add(Ff(fx)) - val fffx = egraph.add(Ff(ffx)) - val ffffx = egraph.add(Ff(fffx)) - val fffffx = egraph.add(Ff(ffffx)) - val ffffffx = egraph.add(Ff(fffffx)) - val fffffffx = egraph.add(Ff(ffffffx)) - val ffffffffx = egraph.add(Ff(fffffffx)) - - egraph.merge(ffffffffx, xf) - egraph.merge(fffffx, xf) - - val base = List(ffffffffx <=> xf, fffffx <=> xf) - - val test2 = Theorem(base |- fffx <=> xf) { - egraph.proveInnerFormula(fffx, xf, base |- ()) - } - val test3 = Theorem(base |- ffx <=> xf) { - egraph.proveInnerFormula(ffx, xf, base |- ()) - } - val test4 = Theorem(base |- fx <=> xf) { - egraph.proveInnerFormula(fx, xf, base |- ()) - } - } - - - test("2 terms 6 predicates with congruence egraph test with proofs") { - val egraph = new EGraphTerms() - egraph.add(Ff(Ff(Fp(a)))) - egraph.add(Ff(Ff(Fp(b)))) - egraph.merge(a, b) - - val test5 = Theorem((a === b) |- Fp(a) <=> Fp(b)) { - egraph.proveInnerFormula(Fp(a), Fp(b), (a === b) |- ()) - } - - val test6 = Theorem((a === b) |- Ff(Fp(a)) <=> Ff(Fp(b))) { - egraph.proveInnerFormula(Ff(Fp(a)), Ff(Fp(b)), (a === b) |- ()) - } - - val test7 = Theorem((a === b) |- Ff(Ff(Fp(a))) <=> Ff(Ff(Fp(b))) ) { - egraph.proveInnerFormula(Ff(Ff(Fp(a))), Ff(Ff(Fp(b))), (a === b) |- ()) - } - - } - - test("6 terms 6 predicates with congruence egraph test with proofs") { - val egraph = new EGraphTerms() - egraph.add(Ff(Ff(Fp(F(F(a)))))) - egraph.add(Ff(Ff(Fp(F(F(b)))))) - egraph.merge(a, b) - - val test5 = Theorem((a === b) |- (F(a) === F(b))) { - egraph.proveInnerTerm(F(a), F(b), (a === b) |- ()) - } - - val test6 = Theorem((a === b) |- Fp(F(F(a))) <=> Fp(F(F(b))) ) { - egraph.proveInnerFormula(Fp(F(F(a))), Fp(F(F(b))), (a === b) |- ()) - } - - val test7 = Theorem((a === b) |- Ff(Ff(Fp(F(F(a))))) <=> Ff(Ff(Fp(F(F(b))))) ) { - egraph.proveInnerFormula(Ff(Ff(Fp(F(F(a))))), Ff(Ff(Fp(F(F(b))))), (a === b) |- ()) - } - - egraph.merge(Fp(F(F(b))), Ff(Fp(F(F(a))))) - - val test8 = Theorem(((a === b), Fp(F(F(b))) <=> Ff(Fp(F(F(a)))) ) |- Ff(Ff(Fp(F(F(a))))) <=> Ff(Ff(Fp(F(F(b))))) ) { - egraph.proveInnerFormula(Ff(Ff(Fp(F(F(a))))), Ff(Ff(Fp(F(F(b))))), (a === b, Fp(F(F(b))) <=> Ff(Fp(F(F(a)))) ) |- ()) - } - - } - - test("Full congruence tactic tests") { - println("Full congruence tactic tests\n") - - val base1 = List(a === c, e === f, i === k, m === n, a === b, o === m, i === m, g === h, l === k, b === c, f === e, o === i, g === e, i === j) - - val test1 = Theorem(base1 |- (b === c)) { - have(thesis) by Congruence - } - - val test2 = Theorem(base1 |- (f === h)) { - have(thesis) by Congruence - } - - val test3 = Theorem(base1 |- (i === o)) { - have(thesis) by Congruence - } - - - val ax2 = `*`(a, two) - val ax2_d2 = `/`(`*`(a, two), two) - val `2d2` = `/`(two, two) - val ax_2d2 = `*`(a, `/`(two, two)) - val ax1 = `*`(a, one) - val as1 = `<<`(a, one) - - val base2 = List[Formula](ax2 === as1, ax2_d2 === ax_2d2, `2d2` === one, ax1 === a) - - - val one_2d2 = Theorem(base2 |- (one === `2d2`)) { - have(thesis) by Congruence - } - - val ax2_as1 = Theorem(base2 |- (ax2 === as1)) { - have(thesis) by Congruence - } - - val ax2_d2_ax_2d2 = Theorem(base2 |- (ax2_d2 === ax_2d2)) { - have(thesis) by Congruence - } - - val ax_2d2_ax1 = Theorem(base2 |- (ax_2d2 === ax1)) { - have(thesis) by Congruence - } - - val ax_2d2_a = Theorem(base2 |- (ax_2d2 === a)) { - have(thesis) by Congruence - } - - val ax_2d2_a_2 = Theorem(base2 |- (Fp(ax_2d2) <=> Fp(a))) { - have(thesis) by Congruence - } - - val ax_2d2_a_1 = Theorem((Fp(a) :: base2) |- Fp(ax_2d2)) { - have(thesis) by Congruence - } - - val ax_2d2_a_3 = Theorem((base2 :+ Fp(ax_2d2) :+ !Fp(a)) |- () ) { - have(thesis) by Congruence - } - - val test5 = Theorem(a===b |- F(a) === F(b)) { - have(thesis) by Congruence - } - - val test6 = Theorem(a === b |- F(a) === F(b)) { - have(thesis) by Congruence - } - - val test7 = Theorem((Ff(Ff(Ff(Ff(Ff(Ff(Ff(xf))))))) <=> xf, Ff(Ff(Ff(Ff(Ff(xf))))) <=> xf) |- Ff(Ff(Ff(xf))) <=> xf) { - have(thesis) by Congruence - } - - val test8 = Theorem((Ff(Ff(Ff(Ff(Ff(Ff(Ff(xf))))))) <=> xf, Ff(Ff(Ff(Ff(Ff(xf))))) <=> xf) |- Ff(xf) <=> xf) { - have(thesis) by Congruence - } - - val test9 = Theorem((a === b) |- (Fp(F(F(a))), !Fp(F(F(b)))) ) { - have(thesis) by Congruence - } - - val test10 = Theorem((a === b) |- Fp(F(F(a))) <=> Fp(F(F(b))) ) { - have(thesis) by Congruence - } - - - val test11 = Theorem((a === b) |- Ff(Ff(Fp(F(F(a))))) <=> Ff(Ff(Fp(F(F(b))))) ) { - have(thesis) by Congruence - } - - val test12 = Theorem(((a === b), Fp(F(F(b))) <=> Ff(Fp(F(F(a)))), Ff(Ff(Fp(F(F(a))))) ) |- Ff(Ff(Fp(F(F(b))))) ) { - have(thesis) by Congruence - } - - - } - - -} \ No newline at end of file diff --git a/lisa-topology/src/test/scala/lisa/automation/TableauTest.scala b/lisa-topology/src/test/scala/lisa/automation/TableauTest.scala deleted file mode 100644 index 69a8d2507..000000000 --- a/lisa-topology/src/test/scala/lisa/automation/TableauTest.scala +++ /dev/null @@ -1,158 +0,0 @@ -package lisa.test.automation - -import lisa.SetTheoryLibrary.{_, given} -import lisa.automation.Substitution.* -import lisa.automation.Tableau.* -import lisa.fol.FOL.* -import lisa.prooflib.Exports.* -import lisa.utils.K.SCProofChecker.checkSCProof -import lisa.utils.parsing.FOLPrinter.prettyFormula -import lisa.utils.parsing.FOLPrinter.prettySCProof -import lisa.utils.parsing.FOLPrinter.prettyTerm -import org.scalatest.funsuite.AnyFunSuite - -class TableauTest extends AnyFunSuite { - import TableauTest.* - - test(s"Propositional Positive cases (${posi.size})") { - assert(posi.forall(_._3), posi.map((i, f, b, proof, judg) => s"$i $b" + (if !b then s" $f" else "")).mkString("\n")) - if posi.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then - fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) - } - - test(s"First Order Quantifier Free Positive cases (${posqf.size})") { - assert(posqf.forall(_._3), posqf.map((i, f, b, proof, judg) => (s"$i $b" + (if !b then s" $f" else ""))).mkString("\n")) - if posqf.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then - fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) - } - - test(s"First Order Easy Positive cases (${poseasy.size})") { - assert(poseasy.forall(_._3), poseasy.map((i, f, b, proof, judg) => (s"$i $b" + (if !b then s" $f" else ""))).mkString("\n")) - if poseasy.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then - fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) - } - - test(s"First Order Hard Positive cases (${poshard.size})") { - assert(poshard.forall(_._3), poshard.map((i, f, b, proof, judg) => (s"$i $b" + (if !b then s" $f" else ""))).mkString("\n")) - if poshard.exists(tup => tup._5.nonEmpty & !tup._5.get.isValid) then - fail("A proof is wrong: " + posi.map(tup => if tup._5.nonEmpty & !tup._5.get.isValid then prettySCProof(tup._5.get) + "\n").mkString("\n")) - } - -} -object TableauTest { - - val u = variable - val w = variable - val x = variable - val y = variable - val z = variable - - val a = formulaVariable - val b = formulaVariable - val c = formulaVariable - val d = formulaVariable - val e = formulaVariable - - val f = function[1] - val g = function[1] - val h = function[2] - - val D = predicate[1] - val E = predicate[2] - val P = predicate[1] - val Q = predicate[1] - val R = predicate[2] - - var doprint: Boolean = false - def printif(s: Any) = if doprint then println(s) else () - - val posi = List( - a <=> a, - a \/ !a, - ((a ==> b) /\ (b ==> c)) ==> (a ==> c), - (a <=> b) <=> ((a /\ b) \/ (!a /\ !b)), - ((a ==> c) /\ (b ==> c)) <=> ((a \/ b) ==> c), - ((a ==> b) /\ (c ==> d)) ==> ((a \/ c) ==> (b \/ d)) - ).zipWithIndex.map(f => - val res = solve(() |- f._1) - (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) - ) - - // Quantifier Free - - val posqf = List( - posi.map(fo => fo._2.substitute(a := P(h(x, y)), b := P(x), c := R(x, h(x, y)))), - posi.map(fo => fo._2.substitute(a := P(h(x, y)), b := P(h(x, y)), c := R(x, h(x, f(x))))), - posi.map(fo => fo._2.substitute(a := R(y, y), b := P(h(y, y)), c := R(h(x, y), h(z, y)))) - ).flatten.zipWithIndex.map(f => - val res = solve(() |- f._1) - (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) - ) - - // First Order Easy - - val poseasy = List( - posi.map(fo => fo._2.substitute(a := forall(x, P(x)), b := forall(x, P(y)), c := exists(x, P(x)))), - posi.map(fo => fo._2.substitute(a := forall(x, P(x) /\ Q(f(x))), b := forall(x, P(x) \/ R(y, x)), c := exists(x, Q(x) ==> R(x, y)))), - posi.map(fo => fo._2.substitute(a := exists(y, forall(x, P(x) /\ Q(f(y)))), b := forall(y, exists(x, P(x) \/ R(y, x))), c := forall(y, exists(x, Q(x) ==> R(x, y))))) - ).flatten.zipWithIndex.map(f => - val res = solve(() |- f._1) - (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) - ) - - // First Order Hard, from https://isabelle.in.tum.de/library/FOL/FOL-ex/Quantifiers_Cla.html - - val a1 = forall(x, forall(y, forall(z, ((E(x, y) /\ E(y, z)) ==> E(x, z))))) - val a2 = forall(x, forall(y, (E(x, y) ==> E(f(x), f(y))))) - val a3 = forall(x, E(f(g(x)), g(f(x)))) - val biga = forall( - x, - forall( - y, - forall( - z, - ((E(x, y) /\ E(y, z)) ==> E(x, z)) /\ - (E(x, y) ==> E(f(x), f(y))) /\ - E(f(g(x)), g(f(x))) - ) - ) - ) - - val poshard = List( - forall(x, P(x) ==> Q(x)) ==> (forall(x, P(x)) ==> forall(x, Q(x))), - forall(x, forall(y, R(x, y))) ==> forall(y, forall(x, R(x, y))), - forall(x, forall(y, R(x, y))) ==> forall(y, forall(x, R(y, x))), - exists(x, exists(y, R(x, y))) ==> exists(y, exists(x, R(x, y))), - exists(x, exists(y, R(x, y))) ==> exists(y, exists(x, R(y, x))), - (forall(x, P(x)) \/ forall(x, Q(x))) ==> forall(x, P(x) \/ Q(x)), - forall(x, a ==> Q(x)) <=> (a ==> forall(x, Q(x))), - forall(x, P(x) ==> a) <=> (exists(x, P(x)) ==> a), - exists(x, P(x) \/ Q(x)) <=> (exists(x, P(x)) \/ exists(x, Q(x))), - exists(x, P(x) /\ Q(x)) ==> (exists(x, P(x)) /\ exists(x, Q(x))), - exists(y, forall(x, R(x, y))) ==> forall(x, exists(y, R(x, y))), - forall(x, Q(x)) ==> exists(x, Q(x)), - (forall(x, P(x) ==> Q(x)) /\ exists(x, P(x))) ==> exists(x, Q(x)), - ((a ==> exists(x, Q(x))) /\ a) ==> exists(x, Q(x)), - forall(x, P(x) ==> Q(f(x))) /\ forall(x, Q(x) ==> R(g(x), x)) ==> (P(y) ==> R(g(f(y)), f(y))), - forall(x, forall(y, P(x) ==> Q(y))) ==> (exists(x, P(x)) ==> forall(y, Q(y))), - (exists(x, P(x)) ==> forall(y, Q(y))) ==> forall(x, forall(y, P(x) ==> Q(y))), - forall(x, forall(y, P(x) ==> Q(y))) <=> (exists(x, P(x)) ==> forall(y, Q(y))), - exists(x, exists(y, P(x) /\ R(x, y))) ==> (exists(x, P(x) /\ exists(y, R(x, y)))), - (exists(x, P(x) /\ exists(y, R(x, y)))) ==> exists(x, exists(y, P(x) /\ R(x, y))), - exists(x, exists(y, P(x) /\ R(x, y))) <=> (exists(x, P(x) /\ exists(y, R(x, y)))), - exists(y, forall(x, P(x) ==> R(x, y))) ==> (forall(x, P(x)) ==> exists(y, R(x, y))), - forall(x, P(x)) ==> P(y), - !forall(x, D(x) /\ !D(f(x))), - !forall(x, (D(x) /\ !D(f(x))) \/ (D(x) /\ !D(x))), - forall(x, forall(y, (E(x, y) ==> E(f(x), f(y))) /\ E(f(g(x)), g(f(x))))) ==> E(f(f(g(u))), f(g(f(u)))), - !(forall(x, !((E(f(x), x)))) /\ forall(x, forall(y, !(E(x, y)) /\ E(f(x), g(x))))), - a1 /\ a2 /\ a3 ==> E(f(f(g(u))), f(g(f(u)))), - a1 /\ a2 /\ a3 ==> E(f(g(f(u))), g(f(f(u)))), - biga ==> E(f(f(g(u))), f(g(f(u)))), - biga ==> E(f(g(f(u))), g(f(f(u)))) - ).zipWithIndex.map(f => - val res = solve(() |- f._1) - (f._2, f._1, res.nonEmpty, res, res.map(checkSCProof)) - ) - -} diff --git a/lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala b/lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala deleted file mode 100644 index 2f01882f7..000000000 --- a/lisa-topology/src/test/scala/lisa/examples/peano_example/Peano.scala +++ /dev/null @@ -1,344 +0,0 @@ -package lisa.examples.peano_example - -import lisa.kernel.fol.FOL.* -import lisa.kernel.proof.RunningTheory -import lisa.kernel.proof.SCProof -import lisa.kernel.proof.SequentCalculus.* -import lisa.prooflib.Library -import lisa.prooflib.OutputManager -import lisa.utils.KernelHelpers.{_, given} -import lisa.utils.Printer - -object Peano { /* - export PeanoArithmeticsLibrary.{_, given} - - /////////////////////////// OUTPUT CONTROL ////////////////////////// - given om:OutputManager = new OutputManager { - override val output: String => Unit = println - override val finishOutput: Throwable => Nothing = e => throw e - } - - def main(args: Array[String]): Unit = {} - ///////////////////////////////////////////////////////////////////// - - def instantiateForallImport(proofImport: Sequent, t: Peano.Term): SCProof = { - require(proofImport.right.size == 1) - val formula = proofImport.right.head - require(formula.isInstanceOf[BinderFormula] && formula.asInstanceOf[BinderFormula].label == Forall) - val forall = formula.asInstanceOf[BinderFormula] - instantiateForall(SCProof(IndexedSeq(), IndexedSeq(proofImport))) - val tempVar = VariableLabel(freshId(formula.freeVariables.map(_.id), "x")) - val instantiated = instantiateBinder(forall, t) - val p1 = SC.Hypothesis(instantiated |- instantiated, instantiated) - val p2 = SC.LeftForall(formula |- instantiated, 0, instantiateBinder(forall, tempVar), tempVar, t) - val p3 = SC.Cut(() |- instantiated, -1, 1, formula) - SCProof(IndexedSeq(p1, p2, p3), IndexedSeq(proofImport)) - } - - def applyInduction(baseProof: SC.SCSubproof, inductionStepProof: SC.SCSubproof, inductionInstance: SCProofStep): IndexedSeq[SCProofStep] = { - require(baseProof.bot.right.size == 1, s"baseProof should prove exactly one formula, got ${FOLPrinter.prettySequent(baseProof.bot)}") - require(inductionStepProof.bot.right.size == 1, s"inductionStepProof should prove exactly one formula, got ${FOLPrinter.prettySequent(inductionStepProof.bot)}") - require( - inductionInstance.bot.left.isEmpty && inductionInstance.bot.right.size == 1, - s"induction instance step should have nothing on the left and exactly one formula on the right, got ${FOLPrinter.prettySequent(inductionInstance.bot)}" - ) - 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 ${FOLPrinter.prettyFormula(inductionInstance.bot.right.head)}") - }): @unchecked - val baseFormula = baseProof.bot.right.head - val stepFormula = inductionStepProof.bot.right.head - require( - isSame(baseFormula /\ stepFormula, premise), - "induction instance premise should be of the form base /\\ step, got " + - s"premise: ${FOLPrinter.prettyFormula(premise)}, base: ${FOLPrinter.prettyFormula(baseFormula)}, step: ${FOLPrinter.prettyFormula(stepFormula)}" - ) - - val lhs: Set[Formula] = baseProof.bot.left ++ inductionStepProof.bot.left - val base0 = baseProof - val step1 = inductionStepProof - val instance2 = inductionInstance - val inductionPremise3 = SC.RightAnd(lhs |- baseFormula /\ stepFormula, Seq(0, 1), Seq(baseFormula, stepFormula)) - val hypConclusion4 = hypothesis(conclusion) - val inductionInstanceOnTheLeft5 = SC.LeftImplies(lhs + (premise ==> conclusion) |- conclusion, 3, 4, premise, conclusion) - val cutInductionInstance6 = SC.Cut(lhs |- conclusion, 2, 5, premise ==> conclusion) - IndexedSeq(base0, step1, instance2, inductionPremise3, hypConclusion4, inductionInstanceOnTheLeft5, cutInductionInstance6) - } - - val (y1, z1) = - (VariableLabel("y1"), VariableLabel("z1")) - - THEOREM("x + 0 = 0 + x") of "∀'x. +('x, 0) = +(0, 'x)" PROOF2 { - val refl0: SCProofStep = SC.RightRefl(() |- s(x) === s(x), s(x) === s(x)) - val subst1 = SC.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 = SC.RightImplies(() |- (x === plus(zero, x)) ==> (s(x) === s(plus(zero, x))), 1, x === plus(zero, x), s(x) === s(plus(zero, x))) - val transform3 = SC.RightSubstEq( - (plus(zero, s(x)) === s(plus(zero, x))) |- (x === plus(zero, x)) ==> (s(x) === plus(zero, s(x))), - 2, - (plus(zero, s(x)), s(plus(zero, x))) :: Nil, - LambdaTermFormula(Seq(y), (x === plus(zero, x)) ==> (s(x) === y)) - ) - - // result: ax4plusSuccessor |- 0+Sx = S(0 + x) - val instanceAx4_4 = SC.SCSubproof( - instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", zero), x), - Seq(-1) - ) - val cut5 = SC.Cut(() |- (x === plus(zero, x)) ==> (s(x) === plus(zero, s(x))), 4, 3, plus(zero, s(x)) === s(plus(zero, x))) - - val transform6 = SC.RightSubstEq( - Set(plus(x, zero) === x, plus(s(x), zero) === s(x)) |- (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), - 5, - (plus(x, zero), x) :: (plus(s(x), zero), s(x)) :: Nil, - LambdaTermFormula(Seq(y, z), (y === plus(zero, x)) ==> (z === plus(zero, s(x)))) - ) - val leftAnd7 = SC.LeftAnd( - (plus(x, zero) === x) /\ (plus(s(x), zero) === s(x)) |- (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), - 6, - plus(x, zero) === x, - plus(s(x), zero) === s(x) - ) - - val instancePlusZero8 = SC.SCSubproof( - instantiateForallImport(ax"ax3neutral", x), - Seq(-2) - ) - val instancePlusZero9 = SC.SCSubproof( - instantiateForallImport(ax"ax3neutral", s(x)), - Seq(-2) - ) - val rightAnd10 = SC.RightAnd(() |- (plus(x, zero) === x) /\ (plus(s(x), zero) === s(x)), Seq(8, 9), Seq(plus(x, zero) === x, plus(s(x), zero) === s(x))) - - val cut11 = SC.Cut( - () |- (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), - 10, - 7, - (plus(x, zero) === x) /\ (plus(s(x), zero) === s(x)) - ) - - val forall12 = SC.RightForall( - cut11.bot.left |- forall(x, (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x)))), - 11, - (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))), - x - ) - - val inductionInstance: SCProofStep = SC.InstPredSchema( - () |- ((plus(zero, zero) === plus(zero, zero)) /\ forall(x, (plus(x, zero) === plus(zero, x)) ==> (plus(s(x), zero) === plus(zero, s(x))))) ==> forall( - x, - plus(x, zero) === plus(zero, x) - ), - -3, - Map(sPhi -> LambdaTermFormula(Seq(x), plus(x, zero) === plus(zero, x))) - ) - - SCProof( - applyInduction( - SC.SCSubproof(SCProof(SC.RightRefl(() |- zero === zero, zero === zero))), - SC.SCSubproof( - SCProof( - IndexedSeq(refl0, subst1, implies2, transform3, instanceAx4_4, cut5, transform6, leftAnd7, instancePlusZero8, instancePlusZero9, rightAnd10, cut11, forall12), - IndexedSeq(ax"ax4plusSuccessor", ax"ax3neutral") - ), - Seq(-1, -2) - ), - inductionInstance - ), - IndexedSeq(ax"ax4plusSuccessor", ax"ax3neutral", ax"ax7induction") - ) - } using (ax"ax4plusSuccessor", ax"ax3neutral", ax"ax7induction") - show - - THEOREM("switch successor") of "∀'x. ∀'y. +('x, S('y)) = +(S('x), 'y)" PROOF2 { - //////////////////////////////////// Base: x + S0 = Sx + 0 /////////////////////////////////////////////// - val base0 = { - // x + 0 = x - val xEqXPlusZero0 = SC.SCSubproof(instantiateForallImport(ax"ax3neutral", x), IndexedSeq(-1)) - // Sx + 0 = Sx - val succXEqSuccXPlusZero1 = SC.SCSubproof(instantiateForallImport(ax"ax3neutral", s(x)), IndexedSeq(-1)) - // x + S0 = S(x + 0) - val xPlusSuccZero2 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", x), zero), IndexedSeq(-2)) - - // ------------------- x + 0 = x, Sx + 0 = Sx, x + S0 = S(x + 0) |- Sx + 0 = x + S0 --------------------- - val succX3 = SC.RightRefl(() |- s(x) === s(x), s(x) === s(x)) - val substEq4 = SC.RightSubstEq( - Set(s(x) === plus(s(x), zero), x === plus(x, zero)) |- plus(s(x), zero) === s(plus(x, zero)), - 3, - (s(x), plus(s(x), zero)) :: (VariableTerm(x), plus(x, zero)) :: Nil, - LambdaTermFormula(Seq(y, z), y === s(z)) - ) - val substEq5 = SC.RightSubstEq( - Set(s(x) === plus(s(x), zero), x === plus(x, zero), s(plus(x, zero)) === plus(x, s(zero))) |- plus(s(x), zero) === plus(x, s(zero)), - 4, - (s(plus(x, zero)), plus(x, s(zero))) :: Nil, - LambdaTermFormula(Seq(z), plus(s(x), zero) === z) - ) - // ------------------------------------------------------------------------------------------------------- - val cut6 = SC.Cut(Set(s(x) === plus(s(x), zero), x === plus(x, zero)) |- plus(s(x), zero) === plus(x, s(zero)), 2, 5, s(plus(x, zero)) === plus(x, s(zero))) - val cut7 = SC.Cut(x === plus(x, zero) |- plus(s(x), zero) === plus(x, s(zero)), 1, 6, s(x) === plus(s(x), zero)) - val cut8 = SC.Cut(() |- plus(s(x), zero) === plus(x, s(zero)), 0, 7, x === plus(x, zero)) - SC.SCSubproof( - SCProof( - IndexedSeq(xEqXPlusZero0, succXEqSuccXPlusZero1, xPlusSuccZero2, succX3, substEq4, substEq5, cut6, cut7, cut8), - IndexedSeq(ax"ax3neutral", ax"ax4plusSuccessor") - ), - IndexedSeq(-1, -2) - ) - } - - /////////////// Induction step: ?y. (x + Sy === Sx + y) ==> (x + SSy === Sx + Sy) //////////////////// - val inductionStep1 = { - // x + SSy = S(x + Sy) - val moveSuccessor0 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", x), s(y)), IndexedSeq(-2)) - - // Sx + Sy = S(Sx + y) - val moveSuccessor1 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", s(x)), y), IndexedSeq(-2)) - - // ----------- x + SSy = S(x + Sy), x + Sy = Sx + y, S(Sx + y) = Sx + Sy |- x + SSy = Sx + Sy ------------ - val middleEq2 = SC.RightRefl(() |- s(plus(x, s(y))) === s(plus(x, s(y))), s(plus(x, s(y))) === s(plus(x, s(y)))) - val substEq3 = - SC.RightSubstEq( - Set(plus(x, s(y)) === plus(s(x), y)) |- s(plus(x, s(y))) === s(plus(s(x), y)), - 2, - (plus(x, s(y)), plus(s(x), y)) :: Nil, - LambdaTermFormula(Seq(z1), s(plus(x, s(y))) === s(z1)) - ) - val substEq4 = - SC.RightSubstEq( - Set(plus(x, s(y)) === plus(s(x), y), plus(x, s(s(y))) === s(plus(x, s(y)))) |- plus(x, s(s(y))) === s(plus(s(x), y)), - 3, - (plus(x, s(s(y))), s(plus(x, s(y)))) :: Nil, - LambdaTermFormula(Seq(z1), z1 === s(plus(s(x), y))) - ) - val substEq5 = - SC.RightSubstEq( - Set(plus(x, s(s(y))) === s(plus(x, s(y))), plus(x, s(y)) === plus(s(x), y), s(plus(s(x), y)) === plus(s(x), s(y))) |- plus(x, s(s(y))) === plus(s(x), s(y)), - 4, - (s(plus(s(x), y)), plus(s(x), s(y))) :: Nil, - LambdaTermFormula(Seq(z1), plus(x, s(s(y))) === z1) - ) - // ------------------------------------------------------------------------------------------------------- - val cut6 = SC.Cut(Set(plus(x, s(y)) === plus(s(x), y), s(plus(s(x), y)) === plus(s(x), s(y))) |- plus(x, s(s(y))) === plus(s(x), s(y)), 0, 5, plus(x, s(s(y))) === s(plus(x, s(y)))) - val cut7 = SC.Cut(plus(x, s(y)) === plus(s(x), y) |- plus(x, s(s(y))) === plus(s(x), s(y)), 1, 6, s(plus(s(x), y)) === plus(s(x), s(y))) - val implies8 = SC.RightImplies(() |- (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y))), 7, plus(x, s(y)) === plus(s(x), y), plus(x, s(s(y))) === plus(s(x), s(y))) - val forall9 = SC.RightForall( - () |- forall(y, (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y)))), - 8, - (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y))), - y - ) - SC.SCSubproof( - SCProof( - IndexedSeq(moveSuccessor0, moveSuccessor1, middleEq2, substEq3, substEq4, substEq5, cut6, cut7, implies8, forall9), - IndexedSeq(ax"ax3neutral", ax"ax4plusSuccessor") - ), - IndexedSeq(-1, -2) - ) - } - - val inductionInstance = { - val inductionOnY0 = SC.Rewrite(() |- (sPhi(zero) /\ forall(y, sPhi(y) ==> sPhi(s(y)))) ==> forall(y, sPhi(y)), -1) - val inductionInstance1 = SC.InstPredSchema( - () |- - ((plus(s(x), zero) === plus(x, s(zero))) /\ - forall(y, (plus(x, s(y)) === plus(s(x), y)) ==> (plus(x, s(s(y))) === plus(s(x), s(y))))) ==> - forall(y, plus(x, s(y)) === plus(s(x), y)), - 0, - Map(sPhi -> LambdaTermFormula(Seq(y), plus(x, s(y)) === plus(s(x), y))) - ) - SC.SCSubproof(SCProof(IndexedSeq(inductionOnY0, inductionInstance1), IndexedSeq(ax"ax7induction")), Seq(-3)) - } - val inductionApplication = applyInduction(base0, inductionStep1, inductionInstance) - val addForall = SC.RightForall(() |- forall(x, forall(y, plus(x, s(y)) === plus(s(x), y))), inductionApplication.size - 1, forall(y, plus(x, s(y)) === plus(s(x), y)), x) - val proof: SCProof = SCProof( - inductionApplication :+ addForall, - IndexedSeq(ax"ax3neutral", ax"ax4plusSuccessor", ax"ax7induction") - ) - proof - } using (ax"ax3neutral", ax"ax4plusSuccessor", ax"ax7induction") - show - - THEOREM("additivity of addition") of "" PROOF2 { - val base0 = SC.SCSubproof(instantiateForallImport(thm"x + 0 = 0 + x", x), Seq(-3)) - val inductionStep1 = { - val start0 = SC.RightRefl(() |- plus(x, s(y)) === plus(x, s(y)), plus(x, s(y)) === plus(x, s(y))) - val applyPlusSuccAx1 = - SC.RightSubstEq(plus(x, s(y)) === s(plus(x, y)) |- plus(x, s(y)) === s(plus(x, y)), 0, (plus(x, s(y)), s(plus(x, y))) :: Nil, LambdaTermFormula(Seq(z1), plus(x, s(y)) === z1)) - val applyInductionPremise2 = - SC.RightSubstEq( - Set(plus(x, s(y)) === s(plus(x, y)), plus(x, y) === plus(y, x)) |- plus(x, s(y)) === s(plus(y, x)), - 1, - (plus(x, y), plus(y, x)) :: Nil, - LambdaTermFormula(Seq(z1), plus(x, s(y)) === s(z1)) - ) - val applyPlusSuccAx3 = - SC.RightSubstEq( - Set(plus(x, s(y)) === s(plus(x, y)), plus(x, y) === plus(y, x), s(plus(y, x)) === plus(y, s(x))) |- plus(x, s(y)) === plus(y, s(x)), - 2, - (s(plus(y, x)), plus(y, s(x))) :: Nil, - LambdaTermFormula(Seq(z1), plus(x, s(y)) === z1) - ) - val applySwitchSuccessor4 = - SC.RightSubstEq( - Set(plus(x, s(y)) === s(plus(x, y)), plus(x, y) === plus(y, x), s(plus(y, x)) === plus(y, s(x)), plus(y, s(x)) === plus(s(y), x)) |- plus(x, s(y)) === plus(s(y), x), - 3, - (plus(y, s(x)), plus(s(y), x)) :: Nil, - LambdaTermFormula(Seq(z1), plus(x, s(y)) === z1) - ) - - val xPlusSYInstance5 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", x), y), Seq(-1)) - val cutXPlusSY6 = - SC.Cut(Set(plus(x, y) === plus(y, x), s(plus(y, x)) === plus(y, s(x)), plus(y, s(x)) === plus(s(y), x)) |- plus(x, s(y)) === plus(s(y), x), 5, 4, plus(x, s(y)) === s(plus(x, y))) - val yPlusSXInstance7 = SC.SCSubproof(instantiateForall(instantiateForallImport(ax"ax4plusSuccessor", y), x), Seq(-1)) - val cutYPlusSX8 = SC.Cut(Set(plus(x, y) === plus(y, x), plus(y, s(x)) === plus(s(y), x)) |- plus(x, s(y)) === plus(s(y), x), 7, 6, s(plus(y, x)) === plus(y, s(x))) - val swichSuccessorInstance9 = SC.SCSubproof(instantiateForall(instantiateForallImport(thm"switch successor", y), x), Seq(-2)) - val cutSwitchSuccessor10 = SC.Cut(plus(x, y) === plus(y, x) |- plus(x, s(y)) === plus(s(y), x), 9, 8, plus(y, s(x)) === plus(s(y), x)) - val rightImplies11 = SC.RightImplies(() |- (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x)), 10, plus(x, y) === plus(y, x), plus(x, s(y)) === plus(s(y), x)) - val forall12 = SC.RightForall(() |- forall(y, (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x))), 11, (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x)), y) - SC.SCSubproof( - SCProof( - IndexedSeq( - start0, - applyPlusSuccAx1, - applyInductionPremise2, - applyPlusSuccAx3, - applySwitchSuccessor4, - xPlusSYInstance5, - cutXPlusSY6, - yPlusSXInstance7, - cutYPlusSX8, - swichSuccessorInstance9, - cutSwitchSuccessor10, - rightImplies11, - forall12 - ), - IndexedSeq(ax"ax4plusSuccessor", thm"switch successor") - ), - IndexedSeq(-1, -4) - ) - } - - val inductionInstance = { - val inductionOnY0 = SC.Rewrite(() |- (sPhi(zero) /\ forall(y, sPhi(y) ==> sPhi(s(y)))) ==> forall(y, sPhi(y)), -1) - val inductionInstance1 = SC.InstPredSchema( - () |- - ((plus(x, zero) === plus(zero, x)) /\ - forall(y, (plus(x, y) === plus(y, x)) ==> (plus(x, s(y)) === plus(s(y), x)))) ==> - forall(y, plus(x, y) === plus(y, x)), - 0, - Map(sPhi -> LambdaTermFormula(Seq(y), plus(x, y) === plus(y, x))) - ) - SC.SCSubproof(SCProof(IndexedSeq(inductionOnY0, inductionInstance1), IndexedSeq(ax"ax7induction")), Seq(-2)) - } - val inductionApplication = applyInduction(base0, inductionStep1, inductionInstance) - val addForall = SC.RightForall(() |- forall(x, forall(y, plus(x, y) === plus(y, x))), inductionApplication.size - 1, forall(y, plus(x, y) === plus(y, x)), x) - val proof: SCProof = SCProof( - inductionApplication :+ addForall, - IndexedSeq(ax"ax4plusSuccessor", ax"ax7induction", thm"x + 0 = 0 + x", thm"switch successor") - ) - proof - } using (ax"ax4plusSuccessor", ax"ax7induction", thm"x + 0 = 0 + x", thm"switch successor") - show - - */ -} diff --git a/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala b/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala deleted file mode 100644 index e422b6f15..000000000 --- a/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmetics.scala +++ /dev/null @@ -1,39 +0,0 @@ -package lisa.examples.peano_example - -import lisa.kernel.fol.FOL.* -import lisa.kernel.proof.RunningTheory -import lisa.utils.KernelHelpers.{_, given} - -object PeanoArithmetics extends lisa.prooflib.Library { - export lisa.fol.FOL.{*, given} - final val (x, y, z) = - (variable, variable, variable) - - final val zero = Constant("0") - final val s = ConstantFunctionLabel("S", 1) - final val plus = ConstantFunctionLabel("+", 2) - final val times = ConstantFunctionLabel("*", 2) - final val sPhi = predicate[1] - val theory: RunningTheory = new RunningTheory() - final val ax1ZeroSuccessor: Formula = forall(x, !(s(x) === zero)) - final val ax2Injectivity: Formula = forall(x, forall(y, (s(x) === s(y)) ==> (x === y))) - final val ax3neutral: Formula = forall(x, plus(x, zero) === x) - final val ax4plusSuccessor: Formula = forall(x, forall(y, plus(x, s(y)) === s(plus(x, y)))) - final val ax5timesZero: Formula = forall(x, times(x, zero) === zero) - final val ax6timesDistrib: Formula = forall(x, forall(y, times(x, s(y)) === plus(times(x, y), x))) - final val ax7induction: Formula = (sPhi(zero) /\ forall(x, sPhi(x) ==> sPhi(s(x)))) ==> forall(x, sPhi(x)) - - final val functions: Set[ConstantTermLabel[?]] = Set(ConstantFunctionLabel("0", 0), s, plus, times) - functions.foreach(l => theory.addSymbol(l.underlyingLabel)) - - private val peanoAxioms: Set[(String, Formula)] = Set( - ("ax1ZeroSuccessor", ax1ZeroSuccessor), - ("ax2Injectivity", ax2Injectivity), - ("ax3neutral", ax3neutral), - ("ax4plusSuccessor", ax4plusSuccessor), - ("ax5timesZero", ax5timesZero), - ("ax6timesDistrib", ax6timesDistrib), - ("ax7induction", ax7induction) - ) - peanoAxioms.foreach(a => theory.addAxiom(a._1, a._2.underlying)) -} diff --git a/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala b/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala deleted file mode 100644 index 3f9d423fe..000000000 --- a/lisa-topology/src/test/scala/lisa/examples/peano_example/PeanoArithmeticsLibrary.scala +++ /dev/null @@ -1,7 +0,0 @@ -package lisa.examples.peano_example - -import lisa.examples.peano_example - -trait PeanoArithmeticsLibrary extends lisa.prooflib.BasicMain { - export PeanoArithmetics.* -} diff --git a/lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala b/lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala deleted file mode 100644 index 9800e4fe9..000000000 --- a/lisa-topology/src/test/scala/lisa/proven/InitialProofsTests.scala +++ /dev/null @@ -1,20 +0,0 @@ -package lisa.proven - -import lisa.test.ProofCheckerSuite -import lisa.utils.Printer - -class InitialProofsTests extends ProofCheckerSuite { - import lisa.SetTheoryLibrary.* - - /* - test("File SetTheory initialize well") { - lisa.proven.mathematics.SetTheory - succeed - } - - test("File Mapping initialize well") { - lisa.proven.mathematics.Mapping - succeed - } - */ -} diff --git a/lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala b/lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala deleted file mode 100644 index 7b7a67c38..000000000 --- a/lisa-topology/src/test/scala/lisa/utilities/ComprehensionsTests.scala +++ /dev/null @@ -1,83 +0,0 @@ -package lisa.utilities - -import org.scalatest.funsuite.AnyFunSuite - -class ComprehensionsTests extends AnyFunSuite { - - object ComprehensionsTests extends lisa.Main { - import lisa.maths.settheory.SetTheory.* - import lisa.maths.settheory.Comprehensions.* - - private val x = variable - private val x_1 = variable - private val y = variable - private val z = variable - private val s = variable - private val t = variable - private val A = variable - private val B = variable - private val C = variable - private val P = predicate[2] - private val Q = predicate[1] - private val Filter = predicate[1] - private val Map = function[1] - private val f = function[1] - - val singletonMap = Lemma( - ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) <=> (x === f(∅)) - ) { - val s1 = have(∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1))) ==> (x === f(∅))) subproof { - have(x === f(∅) |- x === f(∅)) by Restate - thenHave((x_1 === ∅, x === f(x_1)) |- x === f(∅)) by Substitution.ApplyRules(x_1 === ∅) - thenHave((x_1 === ∅) /\ (x === f(x_1)) |- x === f(∅)) by Restate - thenHave((in(x_1, singleton(∅))) /\ ((x === f(x_1))) |- x === f(∅)) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) - thenHave(∃(x_1, in(x_1, singleton(∅)) /\ ((x === f(x_1)))) |- x === f(∅)) by LeftExists - - } - - val s2 = have((x === f(∅)) ==> ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) subproof { - have(x === f(∅) |- (∅ === ∅) /\ (x === f(∅))) by Restate - thenHave(x === f(∅) |- in(∅, singleton(∅)) /\ (x === f(∅))) by Substitution.ApplyRules(singletonHasNoExtraElements of (y := x_1, x := ∅)) - thenHave(x === f(∅) |- ∃(x_1, in(x_1, singleton(∅)) /\ (x === f(x_1)))) by RightExists - thenHave(thesis) by Restate.from - - } - - have(thesis) by Tautology.from(s1, s2) - } - - val testCollector = Theorem( - ∃(s, ∀(x, in(x, s) <=> (x === f(∅)))) - ) { - val r = singleton(∅).collect(lambda(x, top), f) - - have(in(x, r) <=> (x === f(∅))) by Substitution.ApplyRules(singletonMap)(r.elim(x)) - thenHave(∀(x, in(x, r) <=> (x === f(∅)))) by RightForall - thenHave(thesis) by RightExists - } - - val testMap = Theorem( - ∃(s, ∀(x, in(x, s) <=> (x === f(∅)))) - ) { - val r = singleton(∅).map(f) - have(in(x, r) <=> (x === f(∅))) by Substitution.ApplyRules(singletonMap)(r.elim(x)) - thenHave(∀(x, in(x, r) <=> (x === f(∅)))) by RightForall - thenHave(thesis) by RightExists - } - - val testFilter = Theorem( - ∃(x, Q(x)) |- ∃(z, ∀(t, in(t, z) <=> ∀(y, Q(y) ==> in(t, y)))) - ) { - val s1 = assume(∃(x_1, Q(x_1))) - val x = witness(s1) - val z = x.filter(lambda(t, ∀(y, Q(y) ==> in(t, y)))) - have(∀(y, Q(y) ==> in(t, y)) <=> ((t ∈ x) /\ ∀(y, Q(y) ==> in(t, y)))) by Tableau - have(in(t, z) <=> ∀(y, Q(y) ==> (t ∈ y))) by Substitution.ApplyRules(lastStep)(z.elim(t)) - thenHave(∀(t, in(t, z) <=> ∀(y, Q(y) ==> in(t, y)))) by RightForall - thenHave(thesis) by RightExists - - } - } - assert(ComprehensionsTests.theory.getTheorem("testFilter").nonEmpty) - -} diff --git a/lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala b/lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala deleted file mode 100644 index 29826e761..000000000 --- a/lisa-topology/src/test/scala/lisa/utilities/SerializationTest.scala +++ /dev/null @@ -1,188 +0,0 @@ -package lisa.test.utils - -import lisa.automation.Tautology -import lisa.test.automation.TableauTest -import lisa.utils.K -import lisa.utils.K.getJustification -import lisa.utils.K.|- -import lisa.utils.Serialization.* -import org.scalatest.funsuite.AnyFunSuite - -import java.io._ - -//import lisa.automation.TableauTest - -class SerializationTest extends AnyFunSuite { - - val theory = K.RunningTheory() - - val testfile = "SerializationTestuioavrebvtaevslbxfgh" // chances of collision with an existing file is quite low - - def test(proof: K.SCProof, name: String, theory: K.RunningTheory, justs: List[theory.Justification]) = { - thmsToFile(testfile, theory, List((name, proof, justs.map(("test", _))))) - val thm = thmsFromFile(testfile, theory) - File(testfile + ".trees").delete() - File(testfile + ".proof").delete() - thm.head - } - - def testMulti(theory: K.RunningTheory, thms: List[(String, K.SCProof, List[theory.Justification])]) = { - thmsToFile(testfile, theory, thms.map(thm => (thm._1, thm._2, thm._3.map(("test", _))))) - val thm = thmsFromFile(testfile, theory) - File(testfile + ".trees").delete() - File(testfile + ".proof").delete() - thm - } - - test("exporting a proof to a file and back should work, propositional tableau") { - val proofs = TableauTest.posi - proofs.foreach(p => - try { - val proof = p._4.get - val no = p._1 - test(proof, "posi" + no, theory, Nil) - } catch { - case e: Exception => - println("Exception thrown for test case posi" + p._1) - throw e - } - ) - } - - test("exporting a proof to a file and back should work, propositional OL tautology") { - val proofs = TableauTest.posi - proofs.foreach(p => - try { - val formula = p._2 - val no = p._1 - val proof = Tautology.solveSequent(() |- formula.underlying) match { - case Left(proof) => proof - case Right(_) => throw new Exception("OLPropositionalSolver failed to prove a tautology") - } - test(proof, "posiOL" + no, theory, Nil) - } catch { - case e: Exception => - println("Exception thrown for test case posi" + p._1) - throw e - } - ) - } - - test("exporting a proof to a file and back should work, first order quantifier free tableau") { - val proofs = TableauTest.posqf - proofs.foreach(p => - try { - val proof = p._4.get - val no = p._1 - test(proof, "posqf" + no, theory, Nil) - } catch { - case e: Exception => - println("Exception thrown for test case posqf" + p._1) - throw e - } - ) - } - - test("exporting a proof to a file and back should work, first order quantifier free OL tautology") { - val proofs = TableauTest.posqf - proofs.foreach(p => - try { - val formula = p._2 - val no = p._1 - val proof = Tautology.solveSequent(() |- formula.underlying) match { - case Left(proof) => proof - case Right(_) => throw new Exception("OLPropositionalSolver failed to prove a tautology") - } - test(proof, "posqfOL" + no, theory, Nil) - } catch { - case e: Exception => - println("Exception thrown for test case posqf" + p._1) - throw e - } - ) - } - - test("exporting a proof to a file and back should work, first order easy tableau") { - val proofs = TableauTest.poseasy - proofs.foreach(p => - try { - val proof = p._4.get - val no = p._1 - test(proof, "poseasy" + no, theory, Nil) - } catch { - case e: Exception => - println("Exception thrown for test case poseasy" + p._1) - throw e - } - ) - } - - test("exporting a proof to a file and back should work, first order hard tableau") { - val proofs = TableauTest.poshard - proofs.foreach(p => - try { - val proof = p._4.get - val no = p._1 - test(proof, "poshard" + no, theory, Nil) - } catch { - case e: Exception => - println("Exception thrown for test case poshard" + p._1) - throw e - } - ) - } - - test("exporting a proof to a file and back should work, with imports") { - import lisa.maths.settheory.SetTheory as ST - val thms = List( - // ("russelsParadox", ST.russelsParadox), - ("setUnionMembership", ST.setUnionMembership), - ("inductiveSetExists", ST.inductiveSetExists), - ("setWithNoElementsIsEmpty", ST.setWithNoElementsIsEmpty), - ("emptySetIsItsOwnOnlySubset", ST.emptySetIsItsOwnOnlySubset) - ) - thms.foreach(thm => - try { - val proof = thm._2.kernelProof.get - val justifs = thm._2.highProof.get.justifications.map(_.innerJustification) - - test(proof, thm._1 + "_test", ST.theory, justifs) - } catch { - case e: Exception => - println("Exception thrown for string encoding-decoding of theorem " + thm._1) - throw e - } - ) - } - - test("exporting multiple theorems at once to a file and back should work") { - import lisa.maths.settheory.SetTheory as ST - val thms = List( - // ("russelsParadox", ST.russelsParadox), - ("setUnionMembership", ST.setUnionMembership), - ("inductiveSetExists", ST.inductiveSetExists), - ("setWithNoElementsIsEmpty", ST.setWithNoElementsIsEmpty), - ("emptySetIsItsOwnOnlySubset", ST.emptySetIsItsOwnOnlySubset) - ) - - val thmBack = testMulti( - ST.theory, - thms.map(thm => - val proof = thm._2.kernelProof.get - val justifs = thm._2.highProof.get.justifications.map(_.innerJustification) - - (thm._1, proof, justifs) - ) - ) - assert(thmBack.length == thms.length) - thmBack - .zip(thms) - .foreach(pair => { - val thm = pair._1 - val thmOrig = pair._2 - assert(thm._1.name == thmOrig._1) - assert(thm._1.proposition == thmOrig._2.innerJustification.proposition) - }) - } - -} diff --git a/lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala b/lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala deleted file mode 100644 index b69b8e4ef..000000000 --- a/lisa-topology/src/test/scala/lisa/utilities/SubstitutionTacticTest.scala +++ /dev/null @@ -1,119 +0,0 @@ -package lisa.automation - -import lisa.automation.Substitution -import lisa.kernel.proof.RunningTheory -import lisa.kernel.proof.SequentCalculus as SC -import lisa.test.ProofTacticTestLib -import lisa.utils.parsing.FOLParser.* -import lisa.utils.parsing.FOLPrinter.* -import org.scalatest.funsuite.AnyFunSuite - -class SubstitutionTacticTest extends ProofTacticTestLib { - /* - // subst with formula list - test("Tactic Tests: Substitution - From theorems and formulas (LR)") { - val correct = List( - // ( - // "sequent before substitution", "sequent after substitution", - // List("substSequent1", "|- p <=> q", ...), List("substFormula1", "p <=> q", "p = q",...) - // ), - ( - "'P('x) |- 'P('x)", - "'P('x); 'x = 'y |- 'P('y)", - List(), - List("'x = 'y") - ), - ( - "'P('x) |- 'P('x)", - "'P('x); 'x = 'y |- 'P('x)", - List(), - List("'x = 'y") - ), - ( - "'P('x) |- 'P('x)", - "'P('x) |- 'P('y)", - List("|- 'x = 'y"), - List() - ), - ( - "'P('x) |- 'P('x)", - "'P('x) |- 'P('x)", - List("|- 'x = 'y"), - List() - ), - ( - "'P('x) |- 'P('x) \\/ 'R('y)", - "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'Q('x)", - List("|- 'x = 'y"), - List("'R('y) <=> 'Q('x)") - ), - ( - "'P('x) |- 'P('x) \\/ 'R('y)", - "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'Q('x)", - List("|- 'x = 'y", "|- 'R('y) <=> 'Q('x)"), - List() - ), - ( - "'P('x) |- 'P('x) \\/ 'R('y)", - "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'R('y)", - List("|- 'x = 'y"), - List("'R('y) <=> 'Q('x)") - ), - ( - "'P('x) |- 'P('x) \\/ 'R('y)", - "'P('x); 'R('y) <=> 'Q('x) |- 'P('x) \\/ 'R('y)", - List("|- 'x = 'y", "|- 'R('y) <=> 'Q('x)"), - List() - ) - ) - - val incorrect = List( - // ( - // "sequent before substitution", "sequent after substitution", - // List("substSequent1", "|- p <=> q", ...), List("substFormula1", "p <=> q", "p = q",...) - // ), - ( - "'P('x); 'Q('z) |- 'P('x)", - "'P('x); 'Q('z); 'x = 'y |- 'P('y)", - List(), - List("'z = 'y") - ), - ( - "'P('x) |- 'P('x)", - "'P('x); 'x = 'y |- 'P('z)", - List(), - List("'x = 'y") - ), - ( - "'P('x); 'Q('z) |- 'P('x)", - "'P('x); 'Q('z) |- 'P('y)", - List("|- 'x = 'z"), - List() - ), - ( - "'P('x); 'Q('y) |- 'P('x)", - "'P('x); 'Q('y) |- 'P('z)", - List("|- 'x = 'y"), - List() - ), - ( - "'P('x) |- 'P('x) \\/ 'R('y)", - "'P('x) |- 'P('z) \\/ 'Q('x)", - List("|- 'x = 'y"), - List("'R('y) <=> 'Q('x)") - ) - ) - - testTacticCases(using testProof)(correct, incorrect) { (stmt1, stmt2, premiseSequents, formSubsts) => - val prem = introduceSequent(using testProof)(stmt1) - val substPrem: Seq[testProof.Fact | Formula | RunningTheory#Justification] = premiseSequents.map(introduceSequent(using testProof)(_)) - val substForm: Seq[testProof.Fact | Formula | RunningTheory#Justification] = formSubsts.map(parseFormula(_)) - val substJust: Seq[testProof.Fact | Formula | RunningTheory#Justification] = Nil - Substitution - .ApplyRules(using lisa.test.TestTheoryLibrary, testProof)( - (substPrem ++ substForm ++ substJust).asInstanceOf[Seq[testProof.Fact | Formula | RunningTheory#Justification]]: _* - )(prem)(lisa.utils.parsing.FOLParser.parseSequent(stmt2)) - } - } - */ -} diff --git a/lisa-topology/src/test/scala/lisa/utilities/TestMain.scala b/lisa-topology/src/test/scala/lisa/utilities/TestMain.scala deleted file mode 100644 index 6d93e5d05..000000000 --- a/lisa-topology/src/test/scala/lisa/utilities/TestMain.scala +++ /dev/null @@ -1,16 +0,0 @@ -package lisa - -import lisa.prooflib.* - -trait TestMain extends lisa.Main { - - override val om: OutputManager = new OutputManager { - def finishOutput(exception: Exception): Nothing = { - log(exception) - main(Array[String]()) - throw exception - } - val stringWriter: java.io.StringWriter = new java.io.StringWriter() - } - -} diff --git a/project/project/project/metals.sbt b/project/project/project/metals.sbt new file mode 100644 index 000000000..be029c0a4 --- /dev/null +++ b/project/project/project/metals.sbt @@ -0,0 +1,8 @@ +// format: off +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.3") + +// format: on From 8bd799167c0ae643bcb9bc0aebe814c92ffb9af8 Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Sat, 16 Nov 2024 15:52:21 +0100 Subject: [PATCH 03/38] tried definition of topology --- build.sbt | 35 +++++++------- lisa-topology/src/main/scala/lisa/Main.scala | 11 ++++- .../src/main/scala/lisa/Topology.scala | 48 +++++++++++++++++++ .../scala/lisa/maths/topology/Topology.scala | 25 ++++++++++ 4 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 lisa-topology/src/main/scala/lisa/Topology.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala diff --git a/build.sbt b/build.sbt index cb09c4f39..9f3699142 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / version := "0.7" +ThisBuild / version := "0.7" ThisBuild / homepage := Some(url("https://github.com/epfl-lara/lisa")) ThisBuild / startYear := Some(2021) ThisBuild / organization := "ch.epfl.lara" @@ -7,10 +7,10 @@ ThisBuild / organizationHomepage := Some(url("https://lara.epfl.ch")) ThisBuild / licenses := Seq("Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0.html")) ThisBuild / versionScheme := Some("semver-spec") ThisBuild / scalacOptions ++= Seq( - "-feature", - "-deprecation", - "-unchecked", - ) + "-feature", + "-deprecation", + "-unchecked" +) ThisBuild / javacOptions ++= Seq("-encoding", "UTF-8") ThisBuild / semanticdbEnabled := true ThisBuild / semanticdbVersion := scalafixSemanticdb.revision @@ -19,7 +19,6 @@ val scala2 = "2.13.8" val scala3 = "3.5.1" val commonSettings = Seq( crossScalaVersions := Seq(scala3), - run / fork := true ) @@ -31,9 +30,8 @@ val commonSettings3 = commonSettings ++ Seq( scalaVersion := scala3, scalacOptions ++= Seq( "-language:implicitConversions", - //"-rewrite", "-source", "3.4-migration", + // "-rewrite", "-source", "3.4-migration", "-Wconf:msg=.*will never be selected.*:silent" - ), javaOptions += "-Xmx10G", libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.18" % "test", @@ -50,9 +48,9 @@ def githubProject(repo: String, commitHash: String) = RootProject(uri(s"$repo#$c lazy val customTstpParser = githubProject("https://github.com/SimonGuilloud/scala-tptp-parser.git", "eae9c1b7a9546f74779d77ff50fa6e8a1654cfa0") lazy val root = Project( - id = "lisa", - base = file("."), - ) + id = "lisa", + base = file(".") +) .settings(commonSettings3) .dependsOn(kernel, withTests(utils), withTests(sets)) // Everything but `examples` .aggregate(utils) // To run tests on all modules @@ -80,19 +78,21 @@ lazy val topologies = Project( base = file("lisa-topology") ) .settings(commonSettings3) - .dependsOn(kernel, withTests(utils)) + .dependsOn(kernel, sets, withTests(utils)) lazy val utils = Project( id = "lisa-utils", base = file("lisa-utils") ) - .settings(commonSettings3 ++ Seq( - libraryDependencies += "ch.epfl.lara" %% "scallion" % "0.6" from "https://github.com/epfl-lara/scallion/releases/download/v0.6/scallion_3-0.6.jar", - libraryDependencies += "ch.epfl.lara" %% "silex" % "0.6" from "https://github.com/epfl-lara/silex/releases/download/v0.6/silex_3-0.6.jar", - )) + .settings( + commonSettings3 ++ Seq( + libraryDependencies += "ch.epfl.lara" %% "scallion" % "0.6" from "https://github.com/epfl-lara/scallion/releases/download/v0.6/scallion_3-0.6.jar", + libraryDependencies += "ch.epfl.lara" %% "silex" % "0.6" from "https://github.com/epfl-lara/silex/releases/download/v0.6/silex_3-0.6.jar" + ) + ) .dependsOn(kernel) .dependsOn(customTstpParser) - //.settings(libraryDependencies += "io.github.leoprover" % "scala-tptp-parser_2.13" % "1.4") +//.settings(libraryDependencies += "io.github.leoprover" % "scala-tptp-parser_2.13" % "1.4") ThisBuild / assemblyMergeStrategy := { case PathList("module-info.class") => MergeStrategy.discard @@ -104,7 +104,6 @@ ThisBuild / assemblyMergeStrategy := { oldStrategy(x) } - lazy val examples = Project( id = "lisa-examples", base = file("lisa-examples") diff --git a/lisa-topology/src/main/scala/lisa/Main.scala b/lisa-topology/src/main/scala/lisa/Main.scala index 25dcf2513..471f7a0f3 100644 --- a/lisa-topology/src/main/scala/lisa/Main.scala +++ b/lisa-topology/src/main/scala/lisa/Main.scala @@ -1,3 +1,12 @@ package lisa -import lisa.TopologyLibrary \ No newline at end of file +import lisa.TopologyLibrary +import lisa.Topology +import lisa.prooflib.BasicMain + +trait Main extends BasicMain { + + export TopologyLibrary.{given, _} + export Topology.{given, _} + +} diff --git a/lisa-topology/src/main/scala/lisa/Topology.scala b/lisa-topology/src/main/scala/lisa/Topology.scala new file mode 100644 index 000000000..c8f79cfbf --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/Topology.scala @@ -0,0 +1,48 @@ +package lisa + +object Topology extends lisa.Main { + + private val X, T = variable + private val S, A, B, Y = variable + + /** + * Bounded quantifiers --- These express the usual `∀x ∈ G ϕ` and `∃x ∈ G ϕ`, for some variables (sets) `x` and `G`, which + * are shorthands for `∀x (x ∈ G ==> ϕ)` and `∃x (x ∈ G ==> ϕ)`, respectively. + */ + def ∀(x: Variable, range: Variable, inner: Formula): Formula = forall(x, in(x, range) ==> inner) + + def ∃(x: Variable, range: Variable, inner: Formula): Formula = exists(x, in(x, range) ==> inner) + + def ∃!(x: Variable, range: Variable, inner: Formula): Formula = existsOne(x, in(x, range) ==> inner) + + /** + * The ground set X cannot be empty + */ + val nonEmpty = DEF(X) --> !(X === ∅) + + /** + * All elements of T are subsets of X + */ + val setOfSubsets = DEF(X, T) --> ∀(S, T, S ⊆ X) + + /** + * X and the emtpy set ∅ belong to T + */ + val containsExtremes = DEF(X, T) --> ∅ ∈ T /\ X ∈ T + + /** + * The union of any (finite or infinite) number of sets in T belongs to T + */ + val containsUnion = DEF(T) --> ∀(A, T, ∀(B, T, union(unorderedPair(A, B)) ∈ T)) + + /** + * The intersection of two sets in T belongs to T + */ + val containsIntersection = DEF(T) --> forall(Y, in(Y, powerSet(T)) ==> union(Y) ∈ T) + + /** + * By Definition 1.1.1 from the book the pair (X, T) is called a topological space. + * Or T is a topology on X + */ + val topology = DEF(X, T) --> nonEmpty(X) /\ setOfSubsets(X, T) /\ containsExtremes(X, T) /\ containsUnion(T) /\ containsIntersection(T) +} diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala new file mode 100644 index 000000000..6e23149bc --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala @@ -0,0 +1,25 @@ +package lisa.maths.topology + +import lisa.kernel.Definition + +object TopologyMath extends lisa.Main { + + // var defs + private val X, T = variable + private val S, A, B, Y = variable + + val discreteTopology = DEF(X, T) --> T === powerSet(X) + val indiscreteTopology = DEF(X, T) --> ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> (S === X \/ S === ∅)) + + val discreteIsTopology = Theorem( + discreteTopology(X, T) ==> topology(X, T) + ) { + sorry + } + + val indiscreteIsTopology = Theorem( + indiscreteTopology(X, T) ==> topology(X, T) + ) { + sorry + } +} From 1a1903e05c53b0c5d1806d0cb4e9c46525c06b5c Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Sat, 16 Nov 2024 18:29:11 +0100 Subject: [PATCH 04/38] Make it compile --- lisa-topology/src/main/scala/lisa/Main.scala | 11 +--- .../src/main/scala/lisa/Topology.scala | 48 ---------------- .../src/main/scala/lisa/TopologyLibrary.scala | 28 ---------- .../scala/lisa/maths/topology/Instances.scala | 25 +++++++++ .../scala/lisa/maths/topology/Topology.scala | 55 ++++++++++++++----- 5 files changed, 66 insertions(+), 101 deletions(-) delete mode 100644 lisa-topology/src/main/scala/lisa/Topology.scala delete mode 100644 lisa-topology/src/main/scala/lisa/TopologyLibrary.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala diff --git a/lisa-topology/src/main/scala/lisa/Main.scala b/lisa-topology/src/main/scala/lisa/Main.scala index 471f7a0f3..3492c4857 100644 --- a/lisa-topology/src/main/scala/lisa/Main.scala +++ b/lisa-topology/src/main/scala/lisa/Main.scala @@ -1,12 +1,3 @@ package lisa -import lisa.TopologyLibrary -import lisa.Topology -import lisa.prooflib.BasicMain - -trait Main extends BasicMain { - - export TopologyLibrary.{given, _} - export Topology.{given, _} - -} +import lisa.maths.topology diff --git a/lisa-topology/src/main/scala/lisa/Topology.scala b/lisa-topology/src/main/scala/lisa/Topology.scala deleted file mode 100644 index c8f79cfbf..000000000 --- a/lisa-topology/src/main/scala/lisa/Topology.scala +++ /dev/null @@ -1,48 +0,0 @@ -package lisa - -object Topology extends lisa.Main { - - private val X, T = variable - private val S, A, B, Y = variable - - /** - * Bounded quantifiers --- These express the usual `∀x ∈ G ϕ` and `∃x ∈ G ϕ`, for some variables (sets) `x` and `G`, which - * are shorthands for `∀x (x ∈ G ==> ϕ)` and `∃x (x ∈ G ==> ϕ)`, respectively. - */ - def ∀(x: Variable, range: Variable, inner: Formula): Formula = forall(x, in(x, range) ==> inner) - - def ∃(x: Variable, range: Variable, inner: Formula): Formula = exists(x, in(x, range) ==> inner) - - def ∃!(x: Variable, range: Variable, inner: Formula): Formula = existsOne(x, in(x, range) ==> inner) - - /** - * The ground set X cannot be empty - */ - val nonEmpty = DEF(X) --> !(X === ∅) - - /** - * All elements of T are subsets of X - */ - val setOfSubsets = DEF(X, T) --> ∀(S, T, S ⊆ X) - - /** - * X and the emtpy set ∅ belong to T - */ - val containsExtremes = DEF(X, T) --> ∅ ∈ T /\ X ∈ T - - /** - * The union of any (finite or infinite) number of sets in T belongs to T - */ - val containsUnion = DEF(T) --> ∀(A, T, ∀(B, T, union(unorderedPair(A, B)) ∈ T)) - - /** - * The intersection of two sets in T belongs to T - */ - val containsIntersection = DEF(T) --> forall(Y, in(Y, powerSet(T)) ==> union(Y) ∈ T) - - /** - * By Definition 1.1.1 from the book the pair (X, T) is called a topological space. - * Or T is a topology on X - */ - val topology = DEF(X, T) --> nonEmpty(X) /\ setOfSubsets(X, T) /\ containsExtremes(X, T) /\ containsUnion(T) /\ containsIntersection(T) -} diff --git a/lisa-topology/src/main/scala/lisa/TopologyLibrary.scala b/lisa-topology/src/main/scala/lisa/TopologyLibrary.scala deleted file mode 100644 index 6c920ae76..000000000 --- a/lisa-topology/src/main/scala/lisa/TopologyLibrary.scala +++ /dev/null @@ -1,28 +0,0 @@ -package lisa - -import lisa.fol.FOL.{_, given} -import lisa.kernel.proof.RunningTheory -import lisa.prooflib.Library - -/** - * Specific implementation of [[utilities.Library]] for Set Theory, with a RunningTheory that is supposed to be used by the standard library. - */ -object TopologyLibrary extends lisa.prooflib.Library { - - val theory = new RunningTheory() - final val T = variable - - // Predicates - /** - * The symbol for the set membership predicate. - */ - final val isTopology = ConstantPredicateLabel("isTopology", 1) - - final val WhatTopologyIs: Formula = forall(T, isTopology(T)) - - private val peanoAxioms: Set[(String, Formula)] = Set( - ("ax1ZeroSuccessor", WhatTopologyIs), - ) - peanoAxioms.foreach(a => theory.addAxiom(a._1, a._2.underlying)) - -} diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala new file mode 100644 index 000000000..c29207767 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -0,0 +1,25 @@ +package Topology + +import lisa.maths.topology.* +import lisa.maths.settheory.* + +object Instances extends lisa.Main { + // var defs + private val X, T = variable + private val S, A, B, Y = variable + + val discreteTopology = DEF(X, T) --> (T === powerSet(X)) + val indiscreteTopology = DEF(X, T) --> ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> ((S === X) \/ (S === ∅))) + + val discreteIsTopology = Theorem( + discreteTopology(X, T) ==> Topology.topology(X, T) + ) { + sorry + } + + val indiscreteIsTopology = Theorem( + indiscreteTopology(X, T) ==> Topology.topology(X, T) + ) { + sorry + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala index 6e23149bc..f8b19e9f4 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala @@ -1,25 +1,50 @@ package lisa.maths.topology -import lisa.kernel.Definition +import lisa.maths.settheory.* -object TopologyMath extends lisa.Main { +object Topology extends lisa.Main { - // var defs private val X, T = variable private val S, A, B, Y = variable - val discreteTopology = DEF(X, T) --> T === powerSet(X) - val indiscreteTopology = DEF(X, T) --> ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> (S === X \/ S === ∅)) + /** + * Bounded quantifiers --- These express the usual `∀x ∈ G ϕ` and `∃x ∈ G ϕ`, for some variables (sets) `x` and `G`, which + * are shorthands for `∀x (x ∈ G ==> ϕ)` and `∃x (x ∈ G ==> ϕ)`, respectively. + */ + def ∀(x: Variable, range: Variable, inner: Formula): Formula = forall(x, in(x, range) ==> inner) - val discreteIsTopology = Theorem( - discreteTopology(X, T) ==> topology(X, T) - ) { - sorry - } + def ∃(x: Variable, range: Variable, inner: Formula): Formula = exists(x, in(x, range) ==> inner) - val indiscreteIsTopology = Theorem( - indiscreteTopology(X, T) ==> topology(X, T) - ) { - sorry - } + def ∃!(x: Variable, range: Variable, inner: Formula): Formula = existsOne(x, in(x, range) ==> inner) + + /** + * The ground set X cannot be empty + */ + val nonEmpty = DEF(X) --> !(X === ∅) + + /** + * All elements of T are subsets of X + */ + val setOfSubsets = DEF(X, T) --> ∀(S, T, S ⊆ X) + + /** + * X and the empty set ∅ belong to T + */ + val containsExtremes = DEF(X, T) --> ∅ ∈ T /\ X ∈ T + + /** + * The union of any (finite or infinite) number of sets in T belongs to T + */ + val containsUnion = DEF(T) --> ∀(A, T, ∀(B, T, union(unorderedPair(A, B)) ∈ T)) + + /** + * The intersection of two sets in T belongs to T + */ + val containsIntersection = DEF(T) --> forall(Y, in(Y, powerSet(T)) ==> union(Y) ∈ T) + + /** + * By Definition 1.1.1 from the book the pair (X, T) is called a topological space. + * Or T is a topology on X + */ + val topology = DEF(X, T) --> nonEmpty(X) /\ setOfSubsets(X, T) /\ containsExtremes(X, T) /\ containsUnion(T) /\ containsIntersection(T) } From 11ba35748d294e8902706d0b140c1f66742ae5d3 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Sat, 16 Nov 2024 18:31:32 +0100 Subject: [PATCH 05/38] Even better --- .../src/main/scala/lisa/maths/topology/Instances.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index c29207767..f499934a4 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -1,6 +1,6 @@ package Topology -import lisa.maths.topology.* +import lisa.maths.topology.Topology.* import lisa.maths.settheory.* object Instances extends lisa.Main { @@ -12,13 +12,13 @@ object Instances extends lisa.Main { val indiscreteTopology = DEF(X, T) --> ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> ((S === X) \/ (S === ∅))) val discreteIsTopology = Theorem( - discreteTopology(X, T) ==> Topology.topology(X, T) + discreteTopology(X, T) ==> topology(X, T) ) { sorry } val indiscreteIsTopology = Theorem( - indiscreteTopology(X, T) ==> Topology.topology(X, T) + indiscreteTopology(X, T) ==> topology(X, T) ) { sorry } From 9041e04d48ab642ab15450b51fd21b6b0bf53fb5 Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Thu, 21 Nov 2024 22:44:49 +0100 Subject: [PATCH 06/38] started with proof of discrete Topology --- .../scala/lisa/maths/topology/Instances.scala | 44 +++++++++++++++++-- .../scala/lisa/maths/topology/Topology.scala | 5 +-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index f499934a4..211a0f3d7 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -1,20 +1,56 @@ -package Topology +package lisa.maths.topology + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* + +import lisa.automation.kernel.CommonTactics.Definition import lisa.maths.topology.Topology.* import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory._ object Instances extends lisa.Main { // var defs + private val x, y, z = variable private val X, T = variable private val S, A, B, Y = variable - val discreteTopology = DEF(X, T) --> (T === powerSet(X)) - val indiscreteTopology = DEF(X, T) --> ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> ((S === X) \/ (S === ∅))) + val discreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === powerSet(X)) + val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> ((S === X) \/ (S === ∅))) val discreteIsTopology = Theorem( - discreteTopology(X, T) ==> topology(X, T) + discreteTopology(X, T) |- topology(X, T) ) { + assume(discreteTopology(X, T)) + val discreteDef = have(nonEmpty(X) /\ (T === powerSet(X))) by Tautology.from(discreteTopology.definition) + val ext = have(forall(z, in(z, T) <=> in(z, powerSet(X)))) by Tautology.from(extensionalityAxiom of (x := T, y := powerSet(X)), discreteDef) + + val isSub = have(setOfSubsets(X, T)) subproof { + val a = have(in(S, T) <=> in(S, powerSet(X))) by InstantiateForall(S)(ext) + val b = have(in(S, T) ==> subset(S, X)) by Tautology.from(a, powerAxiom of (x := S, y := X)) + val c = thenHave(forall(S, in(S, T) ==> subset(S, X))) by RightForall + have(thesis) by Tautology.from(c, setOfSubsets.definition) + } + val contEx = have(containsExtremes(X, T)) subproof { + val a1 = have(in(∅, T) <=> in(∅, powerSet(X))) by InstantiateForall(∅)(ext) + val a2 = have(∅ ∈ powerSet(X)) by Tautology.from(powerAxiom of (x := ∅, y := X), emptySetIsASubset of (x := X)) + val hasEmpty = have(∅ ∈ T) by Tautology.from(a1, a2) + + val b1 = have(in(X, T) <=> in(X, powerSet(X))) by InstantiateForall(X)(ext) + val b2 = have(in(X, T) <=> forall(z, in(z, X) ==> in(z, X))) by Tautology.from(b1, powerAxiom of (x := X, y := X), subsetAxiom of (x := X, y := X)) + val b3 = have(forall(z, in(z, X) ==> in(z, X))) subproof { + val c1 = have(in(z, X) ==> in(z, X)) by Tautology + thenHave(thesis) by RightForall + } + val hasFull = have(X ∈ T) by Tautology.from(b2, b3) + have(thesis) by Tautology.from(hasEmpty, hasFull, containsExtremes.definition) + } sorry + /* + val contUn = have(containsUnion(T)) subproof {} + val contInt = have(containsIntersection(T)) + have(thesis) by Tautology.from(nonE, subset, contEx, contUn, contInt) + */ } val indiscreteIsTopology = Theorem( diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala index f8b19e9f4..bf8ac18a1 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala @@ -1,5 +1,4 @@ package lisa.maths.topology - import lisa.maths.settheory.* object Topology extends lisa.Main { @@ -35,12 +34,12 @@ object Topology extends lisa.Main { /** * The union of any (finite or infinite) number of sets in T belongs to T */ - val containsUnion = DEF(T) --> ∀(A, T, ∀(B, T, union(unorderedPair(A, B)) ∈ T)) + val containsUnion = DEF(T) --> ∀(Y, powerSet(T), union(Y) ∈ T) /** * The intersection of two sets in T belongs to T */ - val containsIntersection = DEF(T) --> forall(Y, in(Y, powerSet(T)) ==> union(Y) ∈ T) + val containsIntersection = DEF(T) --> ∀(A, T, ∀(B, T, union(A, B) ∈ T)) /** * By Definition 1.1.1 from the book the pair (X, T) is called a topological space. From 19782e577347d9ffbf739a2b41a23b321411869c Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Fri, 22 Nov 2024 09:01:41 +0100 Subject: [PATCH 07/38] TheConditional fix --- .../settheory/SetTheoryTactics.scala | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/lisa-sets/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala b/lisa-sets/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala index 651a3f7a1..ea330eb9d 100644 --- a/lisa-sets/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala +++ b/lisa-sets/src/main/scala/lisa/automation/settheory/SetTheoryTactics.scala @@ -2,6 +2,7 @@ package lisa.automation.settheory import lisa.SetTheoryLibrary.{_, given} import lisa.automation.Tautology +import lisa.automation.kernel.CommonTactics.ExistenceAndUniqueness import lisa.fol.FOL.{_, given} import lisa.kernel.proof.SequentCalculus as SCunique import lisa.maths.Quantifiers @@ -21,8 +22,10 @@ object SetTheoryTactics { private val x = variable private val y = variable private val z = variable + private val v = variable private val h = formulaVariable private val P = predicate[1] + private val Q = predicate[1] private val schemPred = predicate[1] /** @@ -197,4 +200,99 @@ object SetTheoryTactics { unwrapTactic(sp)("Subproof for unique comprehension failed.") } } + + /** + * Defines the element that is uniquely given by the uniqueness theorem, or falls back to the error element if the + * assumptions of the theorem are not satisfied. + * + * This is useful in defining specific elements in groups, where their uniqueness (and existence) strongly rely + * on the assumption of the group structure. + */ + object TheConditional { + def apply(using om: OutputManager)(u: Variable, f: Formula)(just: JUSTIFICATION, defaultValue: Term = ∅): The = { + val seq = just.statement + + if (seq.left.isEmpty) { + The(u, f)(just) + } else { + val prem = if (seq.left.size == 1) seq.left.head else And(seq.left.toSeq) + val completeDef = (prem ==> f) /\ (!prem ==> (u === defaultValue)) + val substF = completeDef.substitute(u := defaultValue) + val substDef = completeDef.substitute(u := v) + + val completeUniquenessTheorem = Lemma( + ∃!(u, completeDef) + ) { + val case1 = have(prem |- ∃!(u, completeDef)) subproof { + // We prove the equivalence f <=> completeDef so that we can substitute it in the uniqueness quantifier + val equiv = have(prem |- ∀(u, f <=> completeDef)) subproof { + have(f |- f) by Hypothesis + thenHave((prem, f) |- f) by Weakening + val left = thenHave(f |- (prem ==> f)) by Restate + + have(prem |- prem) by Hypothesis + thenHave((prem, !prem) |- ()) by LeftNot + thenHave((prem, !prem) |- (u === defaultValue)) by Weakening + val right = thenHave(prem |- (!prem ==> (u === defaultValue))) by Restate + + have((prem, f) |- completeDef) by RightAnd(left, right) + val forward = thenHave(prem |- f ==> completeDef) by Restate + + have(completeDef |- completeDef) by Hypothesis + thenHave((prem, completeDef) |- completeDef) by Weakening + thenHave((prem, completeDef) |- f) by Tautology + val backward = thenHave(prem |- completeDef ==> f) by Restate + + have(prem |- f <=> completeDef) by RightIff(forward, backward) + thenHave(thesis) by RightForall + } + + val substitution = have((∃!(u, f), ∀(u, f <=> completeDef)) |- ∃!(u, completeDef)) by Tautology.from( + Quantifiers.uniqueExistentialEquivalenceDistribution of (P := lambda(u, f), Q := lambda(u, completeDef)) + ) + + val implication = have((prem, ∃!(u, f)) |- ∃!(u, completeDef)) by Cut(equiv, substitution) + val uniqueness = have(prem |- ∃!(u, f)) by Restate.from(just) + have(prem |- ∃!(u, completeDef)) by Cut(uniqueness, implication) + } + + val case2 = have(!prem |- ∃!(u, completeDef)) subproof { + val existence = have(!prem |- ∃(u, completeDef)) subproof { + have(!prem |- !prem) by Hypothesis + thenHave((prem, !prem) |- ()) by LeftNot + thenHave((prem, !prem) |- substF) by Weakening + val left = thenHave(!prem |- (prem ==> substF)) by Restate + + have(defaultValue === defaultValue) by RightRefl + thenHave(!prem |- defaultValue === defaultValue) by Weakening + val right = thenHave(!prem ==> (defaultValue === defaultValue)) by Restate + + have(!prem |- (prem ==> substF) /\ (!prem ==> (defaultValue === defaultValue))) by RightAnd(left, right) + thenHave(thesis) by RightExists.withParameters(defaultValue) + } + + val uniqueness = have((!prem, completeDef, substDef) |- (u === v)) subproof { + assume(!prem) + assume(completeDef) + assume(substDef) + + val eq1 = have(u === defaultValue) by Tautology + val eq2 = have(defaultValue === v) by Tautology + val p = have((u === defaultValue) /\ (defaultValue === v)) by RightAnd(eq1, eq2) + + val transitivity = Quantifiers.equalityTransitivity of (x -> u, y -> defaultValue, z -> v) + have(thesis) by Cut(p, transitivity) + } + + have(thesis) by ExistenceAndUniqueness(completeDef)(existence, uniqueness) + } + + have(thesis) by Tautology.from(case1, case2) + } + + The(u, completeDef)(completeUniquenessTheorem) + } + } + } + } From 960d02047c2c88dc6fc9ee3d553f5b3d4cb98b97 Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Fri, 22 Nov 2024 13:16:44 +0100 Subject: [PATCH 08/38] improved definitions --- .../scala/lisa/maths/topology/Instances.scala | 22 ++++++++++++------- .../scala/lisa/maths/topology/Topology.scala | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 211a0f3d7..d49fe9f43 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -16,7 +16,7 @@ object Instances extends lisa.Main { private val S, A, B, Y = variable val discreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === powerSet(X)) - val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> ((S === X) \/ (S === ∅))) + val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === unorderedPair(∅, X)) val discreteIsTopology = Theorem( discreteTopology(X, T) |- topology(X, T) @@ -37,17 +37,23 @@ object Instances extends lisa.Main { val hasEmpty = have(∅ ∈ T) by Tautology.from(a1, a2) val b1 = have(in(X, T) <=> in(X, powerSet(X))) by InstantiateForall(X)(ext) - val b2 = have(in(X, T) <=> forall(z, in(z, X) ==> in(z, X))) by Tautology.from(b1, powerAxiom of (x := X, y := X), subsetAxiom of (x := X, y := X)) - val b3 = have(forall(z, in(z, X) ==> in(z, X))) subproof { - val c1 = have(in(z, X) ==> in(z, X)) by Tautology - thenHave(thesis) by RightForall - } - val hasFull = have(X ∈ T) by Tautology.from(b2, b3) + val hasFull = have(X ∈ T) by Tautology.from(elemInItsPowerSet of (x := X), b1) have(thesis) by Tautology.from(hasEmpty, hasFull, containsExtremes.definition) } + val contUn = have(containsUnion(T)) subproof { + val a1 = have(in(Y, powerSet(T)) |- (union(Y) ∈ T)) subproof { + sorry + } + val a2 = have(in(Y, powerSet(T)) ==> (union(Y) ∈ T)) by Tautology.from(a1) + val a3 = thenHave(forall(Y, in(Y, powerSet(T)) ==> (union(Y) ∈ T))) by RightForall + have(thesis) by Tautology.from(a3, containsUnion.definition) + /*val a1 = have(powerSet(T) === powerSet(powerSet(X))) by Tautology.from(discreteDef) + val a2 = have(forall(z, z ∈ powerSet(T) ==> z ∈ powerSet(powerSet(X)))) by Tautology.from(a1, extensionalityAxiom of (y := powerSet(powerSet(X)), x := powerSet(T))) + val a3 = thenHave(Y ∈ powerSet(T) ==> Y ∈ powerSet(powerSet(X))) by InstantiateForall(Y) + val a4 = have(Y ∈ powerSet(powerSet(X)) <=> forall(z, in(z, Y) ==> in(z, powerSet(X)))) by Tautology.from(powerAxiom of (x := Y, y := powerSet(X)), subsetAxiom of (x := Y, y := powerSet(X)))*/ + } sorry /* - val contUn = have(containsUnion(T)) subproof {} val contInt = have(containsIntersection(T)) have(thesis) by Tautology.from(nonE, subset, contEx, contUn, contInt) */ diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala index bf8ac18a1..c882f3ac2 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala @@ -34,12 +34,12 @@ object Topology extends lisa.Main { /** * The union of any (finite or infinite) number of sets in T belongs to T */ - val containsUnion = DEF(T) --> ∀(Y, powerSet(T), union(Y) ∈ T) + val containsUnion = DEF(T) --> forall(Y, in(Y, powerSet(T)) ==> (union(Y) ∈ T)) /** * The intersection of two sets in T belongs to T */ - val containsIntersection = DEF(T) --> ∀(A, T, ∀(B, T, union(A, B) ∈ T)) + val containsIntersection = DEF(T) --> ∀(A, T, ∀(B, T, union(unorderedPair(A, B)) ∈ T)) /** * By Definition 1.1.1 from the book the pair (X, T) is called a topological space. From c5261e6c721d24f46f5b444bb1613ae774221517 Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Mon, 2 Dec 2024 14:07:12 +0100 Subject: [PATCH 09/38] finished definitions + progress with proving --- .../maths/settheory/SetTheoryBasics.scala | 78 +++++++++++++++++++ .../scala/lisa/maths/topology/Instances.scala | 33 ++++---- .../scala/lisa/maths/topology/Topology.scala | 19 ++--- 3 files changed, 101 insertions(+), 29 deletions(-) create mode 100644 lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala new file mode 100644 index 000000000..7c71e7453 --- /dev/null +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -0,0 +1,78 @@ +package lisa.maths.settheory + +import lisa.automation.kernel.CommonTactics.Definition +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* +import lisa.maths.settheory.SetTheory.* + +import scala.collection.immutable.{Map => ScalaMap} + +object SetTheoryBasics extends lisa.Main { + + // var defs + private val x = variable + private val y = variable + private val z = variable + private val a = variable + private val b = variable + private val c = variable + private val d = variable + private val t = variable + + /** + * Theorems about basic sets + */ + + val equalityBySubset = Theorem((x === y) <=> (x ⊆ y /\ y ⊆ x)) { + val forward = have(x === y |- x ⊆ y /\ y ⊆ x) subproof { + assume(x === y) + have(forall(z, in(z, x) <=> in(z, y))) by Tautology.from(extensionalityAxiom) + val bothDirections = thenHave(in(z, x) <=> in(z, y)) by InstantiateForall(z) + thenHave(in(z, x) ==> in(z, y)) by Weakening + val leftForall = thenHave(forall(z, in(z, x) ==> in(z, y))) by RightForall + val leftSubset = have(x ⊆ y) by Tautology.from(leftForall, subsetAxiom) + have(in(z, y) ==> in(z, x)) by Weakening(bothDirections) + val rightForall = thenHave(forall(z, in(z, y) ==> in(z, x))) by RightForall + val rightSubset = have(y ⊆ x) by Tautology.from(rightForall, subsetAxiom of (x := y, y := x)) + have(thesis) by Tautology.from(leftSubset, rightSubset) + } + val backward = have((x ⊆ y /\ y ⊆ x) |- x === y) subproof { + assume((x ⊆ y), (y ⊆ x)) + have(forall(z, in(z, x) ==> in(z, y))) by Tautology.from(subsetAxiom) + val left = thenHave(in(z, x) ==> in(z, y)) by InstantiateForall(z) + have(forall(z, in(z, y) ==> in(z, x))) by Tautology.from(subsetAxiom of (x := y, y := x)) + val right = thenHave(in(z, y) ==> in(z, x)) by InstantiateForall(z) + have(in(z, x) <=> in(z, y)) by Tautology.from(left, right) + val equalityDef = thenHave(forall(z, in(z, x) <=> in(z, y))) by RightForall + have(thesis) by Tautology.from(equalityDef, extensionalityAxiom) + } + have(thesis) by Tautology.from(forward, backward) + } + + val subsetClosedIntersection = Theorem((x ⊆ z, y ⊆ z) |- (x ∩ y) ⊆ z) { + assume(x ⊆ z, y ⊆ z) + have(forall(t, in(t, x) ==> in(t, z))) by Tautology.from(subsetAxiom of (y := z, z := t)) + val first = thenHave(in(t, x) ==> in(t, z)) by InstantiateForall(t) + + have(forall(t, in(t, y) ==> in(t, z))) by Tautology.from(subsetAxiom of (x := y, y := z, z := t)) + val second = thenHave(in(t, y) ==> in(t, z)) by InstantiateForall(t) + + have(in(t, setIntersection(x, y)) ==> in(t, z)) by Tautology.from(first, second, setIntersectionMembership) + val defSubs = thenHave(forall(t, in(t, setIntersection(x, y)) ==> in(t, z))) by RightForall + have(thesis) by Tautology.from(defSubs, subsetAxiom of (x := setIntersection(x, y), y := z)) + } + + val subsetClosedSetUnion = Theorem((x ⊆ z, y ⊆ z) |- setUnion(x, y) ⊆ z) { + assume(x ⊆ z, y ⊆ z) + have(forall(t, in(t, x) ==> in(t, z))) by Tautology.from(subsetAxiom of (y := z, z := t)) + val first = thenHave(in(t, x) ==> in(t, z)) by InstantiateForall(t) + + have(forall(t, in(t, y) ==> in(t, z))) by Tautology.from(subsetAxiom of (x := y, y := z, z := t)) + val second = thenHave(in(t, y) ==> in(t, z)) by InstantiateForall(t) + + have(in(t, setUnion(x, y)) ==> in(t, z)) by Tautology.from(first, second, setUnionMembership of (z := t)) + val defSubs = thenHave(forall(t, in(t, setUnion(x, y)) ==> in(t, z))) by RightForall + have(thesis) by Tautology.from(defSubs, subsetAxiom of (x := setUnion(x, y), y := z)) + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index d49fe9f43..2c019b27e 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -25,12 +25,7 @@ object Instances extends lisa.Main { val discreteDef = have(nonEmpty(X) /\ (T === powerSet(X))) by Tautology.from(discreteTopology.definition) val ext = have(forall(z, in(z, T) <=> in(z, powerSet(X)))) by Tautology.from(extensionalityAxiom of (x := T, y := powerSet(X)), discreteDef) - val isSub = have(setOfSubsets(X, T)) subproof { - val a = have(in(S, T) <=> in(S, powerSet(X))) by InstantiateForall(S)(ext) - val b = have(in(S, T) ==> subset(S, X)) by Tautology.from(a, powerAxiom of (x := S, y := X)) - val c = thenHave(forall(S, in(S, T) ==> subset(S, X))) by RightForall - have(thesis) by Tautology.from(c, setOfSubsets.definition) - } + val isSub = have(setOfSubsets(X, T)) by Tautology.from(equalityBySubset of (x := T, y := powerSet(X)), discreteDef, setOfSubsets.definition) val contEx = have(containsExtremes(X, T)) subproof { val a1 = have(in(∅, T) <=> in(∅, powerSet(X))) by InstantiateForall(∅)(ext) val a2 = have(∅ ∈ powerSet(X)) by Tautology.from(powerAxiom of (x := ∅, y := X), emptySetIsASubset of (x := X)) @@ -41,20 +36,28 @@ object Instances extends lisa.Main { have(thesis) by Tautology.from(hasEmpty, hasFull, containsExtremes.definition) } val contUn = have(containsUnion(T)) subproof { - val a1 = have(in(Y, powerSet(T)) |- (union(Y) ∈ T)) subproof { + have(Y ⊆ T |- union(Y) ∈ T) subproof { + assume(Y ⊆ T) + have(Y ⊆ powerSet(X)) by Tautology.from(subsetTransitivity of (a := Y, b := T, c := powerSet(X)), isSub, setOfSubsets.definition) + + have(in(z, union(Y)) |- in(z, X)) subproof { + assume(in(z, union(Y))) + have(exists(y, in(y, Y) /\ in(z, y))) by Tautology.from(unionAxiom of (x := Y)) + } + + // forall(z, in(z, union(Y)) ==> in(z, X)) + // in(z, union(Y)) <=> exists(y, in(y, Y) /\ in(z, y)) sorry } - val a2 = have(in(Y, powerSet(T)) ==> (union(Y) ∈ T)) by Tautology.from(a1) - val a3 = thenHave(forall(Y, in(Y, powerSet(T)) ==> (union(Y) ∈ T))) by RightForall - have(thesis) by Tautology.from(a3, containsUnion.definition) - /*val a1 = have(powerSet(T) === powerSet(powerSet(X))) by Tautology.from(discreteDef) - val a2 = have(forall(z, z ∈ powerSet(T) ==> z ∈ powerSet(powerSet(X)))) by Tautology.from(a1, extensionalityAxiom of (y := powerSet(powerSet(X)), x := powerSet(T))) - val a3 = thenHave(Y ∈ powerSet(T) ==> Y ∈ powerSet(powerSet(X))) by InstantiateForall(Y) - val a4 = have(Y ∈ powerSet(powerSet(X)) <=> forall(z, in(z, Y) ==> in(z, powerSet(X)))) by Tautology.from(powerAxiom of (x := Y, y := powerSet(X)), subsetAxiom of (x := Y, y := powerSet(X)))*/ + } + val contInt = have(containsIntersection(T)) subproof { + val sub = have(A ∈ T, B ∈ T |- A ∩ B ∈ T) subproof { + assume((A ∈ T), (B ∈ T)) + + } } sorry /* - val contInt = have(containsIntersection(T)) have(thesis) by Tautology.from(nonE, subset, contEx, contUn, contInt) */ } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala index c882f3ac2..050ea9f3b 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Topology.scala @@ -1,30 +1,21 @@ package lisa.maths.topology import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory.* object Topology extends lisa.Main { private val X, T = variable private val S, A, B, Y = variable - /** - * Bounded quantifiers --- These express the usual `∀x ∈ G ϕ` and `∃x ∈ G ϕ`, for some variables (sets) `x` and `G`, which - * are shorthands for `∀x (x ∈ G ==> ϕ)` and `∃x (x ∈ G ==> ϕ)`, respectively. - */ - def ∀(x: Variable, range: Variable, inner: Formula): Formula = forall(x, in(x, range) ==> inner) - - def ∃(x: Variable, range: Variable, inner: Formula): Formula = exists(x, in(x, range) ==> inner) - - def ∃!(x: Variable, range: Variable, inner: Formula): Formula = existsOne(x, in(x, range) ==> inner) - /** * The ground set X cannot be empty */ val nonEmpty = DEF(X) --> !(X === ∅) /** - * All elements of T are subsets of X + * Elements of T are subsets of X */ - val setOfSubsets = DEF(X, T) --> ∀(S, T, S ⊆ X) + val setOfSubsets = DEF(X, T) --> T ⊆ powerSet(X) /** * X and the empty set ∅ belong to T @@ -34,12 +25,12 @@ object Topology extends lisa.Main { /** * The union of any (finite or infinite) number of sets in T belongs to T */ - val containsUnion = DEF(T) --> forall(Y, in(Y, powerSet(T)) ==> (union(Y) ∈ T)) + val containsUnion = DEF(T) --> forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T)) /** * The intersection of two sets in T belongs to T */ - val containsIntersection = DEF(T) --> ∀(A, T, ∀(B, T, union(unorderedPair(A, B)) ∈ T)) + val containsIntersection = DEF(T) --> forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T)) /** * By Definition 1.1.1 from the book the pair (X, T) is called a topological space. From e8d8f0cddceb4dd128c8bdeba7a40ccf2e94e56d Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Tue, 3 Dec 2024 16:57:05 +0100 Subject: [PATCH 10/38] proved (in)discrete Topology --- .../scala/lisa/automation/Congruence.scala | 2 +- .../lisa/maths/settheory/SetTheory.scala | 17 +- .../maths/settheory/SetTheoryBasics.scala | 76 ++++++++- .../maths/settheory/types/adt/Tactics.scala | 1 + .../scala/lisa/maths/topology/Instances.scala | 148 +++++++++++++++--- 5 files changed, 206 insertions(+), 38 deletions(-) diff --git a/lisa-sets/src/main/scala/lisa/automation/Congruence.scala b/lisa-sets/src/main/scala/lisa/automation/Congruence.scala index 8c60de410..137f4279b 100644 --- a/lisa-sets/src/main/scala/lisa/automation/Congruence.scala +++ b/lisa-sets/src/main/scala/lisa/automation/Congruence.scala @@ -1,11 +1,11 @@ package lisa.automation +import leo.datastructures.TPTP.CNF.AtomicFormula import lisa.fol.FOL.{*, given} import lisa.prooflib.BasicStepTactic.* import lisa.prooflib.ProofTacticLib.* import lisa.prooflib.SimpleDeducedSteps.* import lisa.prooflib.* import lisa.utils.parsing.UnreachableException -import leo.datastructures.TPTP.CNF.AtomicFormula /** * This tactic tries to prove a sequent by congruence. diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala index 3439bad44..78b8e0b71 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala @@ -917,16 +917,21 @@ object SetTheory extends lisa.Main { */ val setIntersection = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, x) /\ in(t, y))))(setIntersectionUniqueness) - val setIntersectionCommutativity = Theorem( - setIntersection(x, y) === setIntersection(y, x) + val setIntersectionMembership = Theorem( + in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y)) ) { - sorry + have(forall(t, in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y)))) by InstantiateForall(setIntersection(x, y))(setIntersection.definition) + thenHave(thesis) by InstantiateForall(t) } - val setIntersectionMembership = Theorem( - in(t, setIntersection(x, y)) <=> (in(t, x) /\ in(t, y)) + val setIntersectionCommutativity = Theorem( + setIntersection(x, y) === setIntersection(y, x) ) { - sorry + val left = have(in(t, setIntersection(x, y)) <=> (in(t, y) /\ in(t, x))) by Tautology.from(setIntersectionMembership) + val right = have(in(t, setIntersection(y, x)) <=> (in(t, y) /\ in(t, x))) by Tautology.from(setIntersectionMembership of (x := y, y := x)) + have(in(t, setIntersection(x, y)) <=> in(t, setIntersection(y, x))) by Tautology.from(left, right) + val equalityPrepare = thenHave(forall(t, (in(t, setIntersection(x, y)) <=> in(t, setIntersection(y, x))))) by RightForall + have(thesis) by Tautology.from(equalityPrepare, extensionalityAxiom of (x := setIntersection(x, y), y := setIntersection(y, x))) } extension (x: Term) { diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala index 7c71e7453..0e1849f24 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -13,6 +13,7 @@ object SetTheoryBasics extends lisa.Main { private val x = variable private val y = variable private val z = variable + private val w = variable private val a = variable private val b = variable private val c = variable @@ -29,11 +30,11 @@ object SetTheoryBasics extends lisa.Main { have(forall(z, in(z, x) <=> in(z, y))) by Tautology.from(extensionalityAxiom) val bothDirections = thenHave(in(z, x) <=> in(z, y)) by InstantiateForall(z) thenHave(in(z, x) ==> in(z, y)) by Weakening - val leftForall = thenHave(forall(z, in(z, x) ==> in(z, y))) by RightForall - val leftSubset = have(x ⊆ y) by Tautology.from(leftForall, subsetAxiom) + thenHave(forall(z, in(z, x) ==> in(z, y))) by RightForall + val leftSubset = have(x ⊆ y) by Tautology.from(lastStep, subsetAxiom) have(in(z, y) ==> in(z, x)) by Weakening(bothDirections) - val rightForall = thenHave(forall(z, in(z, y) ==> in(z, x))) by RightForall - val rightSubset = have(y ⊆ x) by Tautology.from(rightForall, subsetAxiom of (x := y, y := x)) + thenHave(forall(z, in(z, y) ==> in(z, x))) by RightForall + val rightSubset = have(y ⊆ x) by Tautology.from(lastStep, subsetAxiom of (x := y, y := x)) have(thesis) by Tautology.from(leftSubset, rightSubset) } val backward = have((x ⊆ y /\ y ⊆ x) |- x === y) subproof { @@ -49,6 +50,43 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(forward, backward) } + val replaceEqualityContainsRight = Theorem((x === y) ==> ((z ∈ x) <=> (z ∈ y))) { + have((x === y) |- ((z ∈ x) <=> (z ∈ y))) subproof { + assume(x === y) + have(forall(z, (z ∈ x) <=> (z ∈ y))) by Tautology.from(extensionalityAxiom) + thenHave(thesis) by InstantiateForall(z) + } + thenHave(thesis) by Tautology + } + + val replaceEqualityContainsLeft = Theorem((x === y) ==> ((x ∈ z) <=> (y ∈ z))) { + have(((x === y), (y ∈ z)) |- (x ∈ z)) subproof { + have((y ∈ z, (x === y)) |- (y ∈ z)) by Tautology + thenHave(thesis) by RightSubstEq.withParametersSimple(List((y, x)), lambda(x, in(x, z))) + } + have(thesis) by Tautology.from(lastStep, lastStep of (x := y, y := x)) + } + + val replaceEqualitySubsetRight = Theorem((x === y) ==> ((z ⊆ x) <=> (z ⊆ y))) { + val side = have(((x === y), (z ⊆ x)) |- (z ⊆ y)) subproof { + assume((x === y) /\ (z ⊆ x)) + have(thesis) by Tautology.from(equalityBySubset, subsetTransitivity of (a := z, b := x, c := y)) + } + have(thesis) by Tautology.from(side, side of (x := y, y := x)) + } + + val replaceEqualitySubsetLeft = Theorem((x === y) ==> ((x ⊆ z) <=> (y ⊆ z))) { + val side = have(((x === y), (x ⊆ z)) |- (y ⊆ z)) subproof { + assume((x === y) /\ (x ⊆ z)) + have(forall(w, in(w, x) ==> in(w, z))) by Tautology.from(subsetAxiom of (y := z, z := w)) + thenHave(in(w, x) ==> in(w, z)) by InstantiateForall(w) + have(in(w, y) ==> in(w, z)) by Tautology.from(lastStep, replaceEqualityContainsRight of (z := w)) + thenHave(forall(w, in(w, y) ==> in(w, z))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := y, y := z)) + } + have(thesis) by Tautology.from(side, side of (x := y, y := x)) + } + val subsetClosedIntersection = Theorem((x ⊆ z, y ⊆ z) |- (x ∩ y) ⊆ z) { assume(x ⊆ z, y ⊆ z) have(forall(t, in(t, x) ==> in(t, z))) by Tautology.from(subsetAxiom of (y := z, z := t)) @@ -58,8 +96,8 @@ object SetTheoryBasics extends lisa.Main { val second = thenHave(in(t, y) ==> in(t, z)) by InstantiateForall(t) have(in(t, setIntersection(x, y)) ==> in(t, z)) by Tautology.from(first, second, setIntersectionMembership) - val defSubs = thenHave(forall(t, in(t, setIntersection(x, y)) ==> in(t, z))) by RightForall - have(thesis) by Tautology.from(defSubs, subsetAxiom of (x := setIntersection(x, y), y := z)) + thenHave(forall(t, in(t, setIntersection(x, y)) ==> in(t, z))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := setIntersection(x, y), y := z)) } val subsetClosedSetUnion = Theorem((x ⊆ z, y ⊆ z) |- setUnion(x, y) ⊆ z) { @@ -71,8 +109,30 @@ object SetTheoryBasics extends lisa.Main { val second = thenHave(in(t, y) ==> in(t, z)) by InstantiateForall(t) have(in(t, setUnion(x, y)) ==> in(t, z)) by Tautology.from(first, second, setUnionMembership of (z := t)) - val defSubs = thenHave(forall(t, in(t, setUnion(x, y)) ==> in(t, z))) by RightForall - have(thesis) by Tautology.from(defSubs, subsetAxiom of (x := setUnion(x, y), y := z)) + thenHave(forall(t, in(t, setUnion(x, y)) ==> in(t, z))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := setUnion(x, y), y := z)) + } + + val subsetClosedUnion = Theorem((x ⊆ powerSet(y)) |- union(x) ⊆ y) { + assume(x ⊆ powerSet(y)) + have(forall(z, in(z, x) ==> in(z, powerSet(y)))) by Tautology.from(subsetAxiom of (y := powerSet(y))) + thenHave(in(z, x) ==> in(z, powerSet(y))) by InstantiateForall(z) + have(in(z, x) |- forall(w, in(w, z) ==> in(w, y))) by Tautology.from(lastStep, powerAxiom of (x := z), subsetAxiom of (x := z, z := w)) + thenHave(in(z, x) |- (in(w, z) ==> in(w, y))) by InstantiateForall(w) + have((in(z, x) /\ in(w, z)) |- in(w, y)) by Tautology.from(lastStep) + thenHave(exists(z, in(z, x) /\ in(w, z)) |- in(w, y)) by LeftExists + have(in(w, union(x)) ==> in(w, y)) by Tautology.from(lastStep, unionAxiom of (z := w, y := z)) + thenHave(forall(w, in(w, union(x)) ==> in(w, y))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := union(x))) + } + + val unionDoesntShrink = Theorem((x ∈ y) |- (x ⊆ union(y))) { + assume(in(x, y)) + thenHave(in(z, x) |- (in(z, x) /\ in(x, y))) by Tautology + thenHave(in(z, x) |- exists(x, in(z, x) /\ in(x, y))) by RightExists + have(in(z, x) ==> in(z, union(y))) by Tautology.from(lastStep, unionAxiom of (x := y, y := x)) + thenHave(forall(z, in(z, x) ==> in(z, union(y)))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := x, y := union(y))) } } diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala index 7a05b0b49..9a0c92fbf 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala @@ -5,6 +5,7 @@ package lisa.maths.settheory.types.adt import lisa.maths.settheory.SetTheory.{*, given} + import ADTDefinitions.* import Helpers.* diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 2c019b27e..79b76a374 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -7,11 +7,12 @@ import lisa.automation.kernel.CommonTactics.Definition import lisa.maths.topology.Topology.* import lisa.maths.settheory.* -import lisa.maths.settheory.SetTheory._ +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.SetTheoryBasics.* object Instances extends lisa.Main { // var defs - private val x, y, z = variable + private val x, y, z, a, b, c, t = variable private val X, T = variable private val S, A, B, Y = variable @@ -31,40 +32,141 @@ object Instances extends lisa.Main { val a2 = have(∅ ∈ powerSet(X)) by Tautology.from(powerAxiom of (x := ∅, y := X), emptySetIsASubset of (x := X)) val hasEmpty = have(∅ ∈ T) by Tautology.from(a1, a2) - val b1 = have(in(X, T) <=> in(X, powerSet(X))) by InstantiateForall(X)(ext) - val hasFull = have(X ∈ T) by Tautology.from(elemInItsPowerSet of (x := X), b1) - have(thesis) by Tautology.from(hasEmpty, hasFull, containsExtremes.definition) + have(in(X, T) <=> in(X, powerSet(X))) by InstantiateForall(X)(ext) + have(X ∈ T) by Tautology.from(lastStep, elemInItsPowerSet of (x := X)) + have(thesis) by Tautology.from(lastStep, hasEmpty, containsExtremes.definition) } val contUn = have(containsUnion(T)) subproof { - have(Y ⊆ T |- union(Y) ∈ T) subproof { + have((Y ⊆ T) |- (union(Y) ∈ T)) subproof { assume(Y ⊆ T) - have(Y ⊆ powerSet(X)) by Tautology.from(subsetTransitivity of (a := Y, b := T, c := powerSet(X)), isSub, setOfSubsets.definition) - - have(in(z, union(Y)) |- in(z, X)) subproof { - assume(in(z, union(Y))) - have(exists(y, in(y, Y) /\ in(z, y))) by Tautology.from(unionAxiom of (x := Y)) - } - - // forall(z, in(z, union(Y)) ==> in(z, X)) - // in(z, union(Y)) <=> exists(y, in(y, Y) /\ in(z, y)) - sorry + have(Y ⊆ powerSet(X)) by Tautology.from(discreteDef, equalityBySubset of (x := T, y := powerSet(X)), subsetTransitivity of (a := Y, b := T, c := powerSet(X))) + have(union(Y) ∈ powerSet(X)) by Tautology.from(lastStep, subsetClosedUnion of (x := Y, y := X), powerAxiom of (x := union(Y), y := X)) + have(thesis) by Tautology.from(lastStep, discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := union(Y))) } + thenHave((Y ⊆ T) ==> (union(Y) ∈ T)) by Tautology + thenHave(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsUnion.definition) } val contInt = have(containsIntersection(T)) subproof { - val sub = have(A ∈ T, B ∈ T |- A ∩ B ∈ T) subproof { + have((A ∈ T, B ∈ T) |- (A ∩ B ∈ T)) subproof { assume((A ∈ T), (B ∈ T)) - + val first = have(A ⊆ X) by Tautology.from(discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := A), powerAxiom of (x := A, y := X)) + val second = have(B ⊆ X) by Tautology.from(discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := B), powerAxiom of (x := B, y := X)) + have(A ∩ B ∈ powerSet(X)) by Tautology.from(first, second, subsetClosedIntersection of (x := A, y := B, z := X), powerAxiom of (x := A ∩ B, y := X)) + have(thesis) by Tautology.from(lastStep, discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := A ∩ B)) } + have((A ∈ T /\ B ∈ T) ==> (A ∩ B ∈ T)) by Tautology.from(lastStep, containsIntersection.definition) + thenHave(forall(B, (A ∈ T /\ B ∈ T) ==> (A ∩ B ∈ T))) by RightForall + thenHave(forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> (A ∩ B ∈ T)))) by RightForall + have(thesis) by Tautology.from(lastStep, containsIntersection.definition) } - sorry - /* - have(thesis) by Tautology.from(nonE, subset, contEx, contUn, contInt) - */ + + have(thesis) by Tautology.from(discreteDef, isSub, contEx, contUn, contInt, topology.definition) } val indiscreteIsTopology = Theorem( indiscreteTopology(X, T) ==> topology(X, T) ) { - sorry + assume(indiscreteTopology(X, T)) + val indiscreteDef = have(nonEmpty(X) /\ (T === unorderedPair(∅, X))) by Tautology.from(indiscreteTopology.definition) + val emptySubs = have(∅ ∈ powerSet(X)) by Tautology.from(emptySetIsASubset of (x := X), powerAxiom of (x := emptySet, y := X)) + val fullSubs = have(X ∈ powerSet(X)) by Tautology.from(elemInItsPowerSet of (x := X)) + + val isSub = have(setOfSubsets(X, T)) subproof { + have(in(unorderedPair(∅, X), powerSet(powerSet(X)))) by Tautology.from(emptySubs, fullSubs, unorderedPairInPowerSet of (x := powerSet(X), a := emptySet, b := X)) + have(unorderedPair(∅, X) ⊆ powerSet(X)) by Tautology.from(lastStep, powerAxiom of (x := unorderedPair(∅, X), y := powerSet(X))) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualitySubsetLeft of (x := unorderedPair(∅, X), y := T, z := powerSet(X)), setOfSubsets.definition) + } + + val contEx = have(containsExtremes(X, T)) subproof { + val a = have(X ∈ T) by Tautology.from(pairAxiom of (x := emptySet, y := X, z := X), indiscreteDef, replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := X)) + val b = have(∅ ∈ T) by Tautology.from( + pairAxiom of (x := emptySet, y := X, z := emptySet), + indiscreteDef, + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := emptySet) + ) + have(thesis) by Tautology.from(a, b, containsExtremes.definition) + } + + val contUn = have(containsUnion(T)) subproof { + have((Y ⊆ T) |- union(Y) ∈ T) subproof { + assume(Y ⊆ T) + have(Y ⊆ unorderedPair(∅, X)) by Tautology.from(indiscreteDef, replaceEqualitySubsetRight of (x := T, y := unorderedPair(∅, X), z := Y)) + have(forall(z, in(z, Y) ==> in(z, unorderedPair(∅, X)))) by Tautology.from(lastStep, subsetAxiom of (x := Y, y := unorderedPair(∅, X))) + thenHave(in(z, Y) ==> in(z, unorderedPair(∅, X))) by InstantiateForall(z) + have(in(z, Y) |- ((z === ∅) \/ (z === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X)) + thenHave((in(z, Y) /\ in(a, z)) |- (((z === ∅) \/ (z === X)) /\ in(a, z))) by Tautology + thenHave((in(z, Y) /\ in(a, z)) |- ((((z === ∅) /\ in(a, z))) \/ ((z === X) /\ in(a, z)))) by Tautology + have((in(z, Y) /\ in(a, z)) |- (in(a, ∅) \/ (in(a, X) /\ (z === X)))) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := z, y := emptySet, z := a), + replaceEqualityContainsRight of (x := z, y := X, z := a) + ) + have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ (z === X) /\ in(z, Y))) by Tautology.from(lastStep, emptySetAxiom of (x := a)) + have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := X, z := Y)) + thenHave(exists(z, in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by LeftExists + val before = have(in(a, union(Y)) ==> (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, unionAxiom of (z := a, x := Y, y := z), emptySetAxiom of (x := a)) + thenHave(in(a, union(Y)) ==> in(a, X)) by Tautology + val base = thenHave(forall(a, in(a, union(Y)) ==> in(a, X))) by RightForall + val cond1 = have(forall(a, !in(a, union(Y))) |- union(Y) === ∅) by Tautology.from(setWithNoElementsIsEmpty of (y := a, x := union(Y))) + val cond2 = have(exists(a, in(a, union(Y))) |- union(Y) === X) subproof { + val unionGrow = have(in(a, union(Y)) |- (X ⊆ union(Y))) by Tautology.from(before, unionDoesntShrink of (x := X, y := Y)) + have(in(a, union(Y)) |- (union(Y) === X)) by Tautology.from(base, unionGrow, subsetAxiom of (x := union(Y), y := X, z := a), equalityBySubset of (x := union(Y), y := X)) + thenHave(thesis) by LeftExists + } + have((union(Y) === ∅) \/ (union(Y) === X)) by Tautology.from(base, cond1, cond2) + have(union(Y) ∈ unorderedPair(∅, X)) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := union(Y))) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := union(Y))) + } + thenHave((Y ⊆ T) ==> (union(Y) ∈ T)) by Tautology + thenHave(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsUnion.definition) + } + + val contInt = have(containsIntersection(T)) subproof { + have((A ∈ T /\ B ∈ T) |- (A ∩ B ∈ T)) subproof { + assume((A ∈ T /\ B ∈ T)) + have(A ∈ unorderedPair(∅, X) /\ B ∈ unorderedPair(∅, X)) by Tautology.from( + indiscreteDef, + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := A), + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := B) + ) + val allPossibilities = + have(((A === ∅) \/ (A === X)) /\ ((B === ∅) \/ (B === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := A), pairAxiom of (x := emptySet, y := X, z := B)) + val aEmpty = have((A === ∅) |- (A ∩ B) === ∅) subproof { + assume(A === emptySet) + have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) + have(!in(t, setIntersection(A, B))) by Tautology.from(lastStep, replaceEqualityContainsRight of (x := emptySet, y := A, z := t), emptySetAxiom of (x := t)) + thenHave(forall(t, !in(t, setIntersection(A, B)))) by RightForall + have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (y := t, x := setIntersection(A, B))) + } + val oneEmpty = have(((A === ∅) \/ (B === ∅)) |- ((A ∩ B) === ∅)) by Tautology.from( + aEmpty, + aEmpty of (A := B, B := A), + setIntersectionCommutativity of (x := A, y := B), + equalityTransitivity of (x := setIntersection(A, B), y := setIntersection(B, A), z := emptySet) + ) + val bothFull = have((A === X, B === X) |- A ∩ B === X) subproof { + assume(((A === X) /\ (B === X))) + have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) + have(in(t, setIntersection(A, B)) <=> in(t, X)) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := X, y := A, z := t), + replaceEqualityContainsRight of (x := X, y := B, z := t) + ) + thenHave(forall(t, in(t, setIntersection(A, B)) <=> in(t, X))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := setIntersection(A, B), y := X, z := t)) + } + have((A ∩ B === ∅) \/ (A ∩ B === X)) by Tautology.from(allPossibilities, oneEmpty, bothFull) + have(in(A ∩ B, unorderedPair(∅, X))) by Tautology.from(lastStep, pairAxiom of (z := setIntersection(A, B), x := emptySet, y := X)) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := setIntersection(A, B))) + } + thenHave((A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T) by Tautology + thenHave(forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T)) by RightForall + thenHave(forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsIntersection.definition) + } + + have(thesis) by Tautology.from(indiscreteDef, isSub, contEx, contUn, contInt, topology.definition) } } From 9106978a77e7f477914c36b78b39cc48618003ba Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Thu, 5 Dec 2024 15:42:20 +0100 Subject: [PATCH 11/38] started with singleton --- .../scala/lisa/maths/topology/Instances.scala | 97 ++++++++++++++++++- .../lisa/maths/topology/Intersection.scala | 19 ++++ 2 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 79b76a374..6a188db84 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -12,12 +12,11 @@ import lisa.maths.settheory.SetTheoryBasics.* object Instances extends lisa.Main { // var defs - private val x, y, z, a, b, c, t = variable + private val x, y, z, a, b, c, t, p = variable private val X, T = variable private val S, A, B, Y = variable val discreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === powerSet(X)) - val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === unorderedPair(∅, X)) val discreteIsTopology = Theorem( discreteTopology(X, T) |- topology(X, T) @@ -64,6 +63,8 @@ object Instances extends lisa.Main { have(thesis) by Tautology.from(discreteDef, isSub, contEx, contUn, contInt, topology.definition) } + val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === unorderedPair(∅, X)) + val indiscreteIsTopology = Theorem( indiscreteTopology(X, T) ==> topology(X, T) ) { @@ -169,4 +170,96 @@ object Instances extends lisa.Main { have(thesis) by Tautology.from(indiscreteDef, isSub, contEx, contUn, contInt, topology.definition) } + + val singletonSetsUniquenes = Theorem( + ∃!(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x))))) + ) { + val implicationProof = have(exists(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { sorry } + have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( + union(cartesianProduct(S, S)), + lambda(t, exists(x, in(x, S) /\ (t === singleton(x)))), + implicationProof + ) + } + val singletonSets = DEF(S) --> The(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))(singletonSetsUniquenes) + + val singletonSetsMembershipRaw = Theorem( + in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S))) + ) { + have(∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by InstantiateForall(singletonSets(S))(singletonSets.definition) + thenHave(thesis) by InstantiateForall(t) + } + + val singletonSetsMembership = Theorem( + in(x, S) <=> in(singleton(x), singletonSets(S)) + ) { + val memb = have(in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S)))) by Tautology.from(singletonSetsMembershipRaw) + have(in(x, S) |- in(singleton(x), singletonSets(S))) subproof { + assume(in(x, S)) + have(t === singleton(x) |- ((t === singleton(x)) /\ in(x, S))) by Tautology + thenHave(t === singleton(x) |- exists(x, ((t === singleton(x)) /\ in(x, S)))) by RightExists + have((t === singleton(x)) ==> in(t, singletonSets(S))) by Tautology.from(lastStep, memb) + thenHave(forall(t, (t === singleton(x)) ==> in(t, singletonSets(S)))) by RightForall + thenHave((singleton(x) === singleton(x)) ==> in(singleton(x), singletonSets(S))) by InstantiateForall(singleton(x)) + have(thesis) by Tautology.from(lastStep) + } + have(in(singleton(x), singletonSets(S)) |- in(x, S)) subproof { + assume(in(singleton(x), singletonSets(S))) + + val removeExists = have((exists(y, in(y, S) /\ (t === singleton(y))), t === singleton(x)) |- in(x, S)) subproof { + have((in(y, S), t === singleton(x), t === singleton(y)) |- (in(y, S), t === singleton(x), t === singleton(y))) + thenHave((in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by Tautology.from( + singletonExtensionality, + equalityTransitivity of (x := singleton(x), y := t, z := singleton(y)), + replaceEqualityContainsLeft of (z := S) + ) + thenHave(exists(y, in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by LeftExists + have(exists(y, in(y, S) /\ (t === singleton(y))) /\ (t === singleton(x)) |- (in(x, S))) by Tautology.from( + lastStep, + existentialConjunctionWithClosedFormula of (x := y, p := (t === singleton(x))) + ) + thenHave(thesis) by Tautology + } + have((t === singleton(x), in(t, singletonSets(S))) |- (t === singleton(x), exists(x, ((t === singleton(x)) /\ in(x, S))))) by Tautology.from(singletonSetsMembershipRaw of (x := y)) + have((t === singleton(x), in(t, singletonSets(S))) |- in(x, S)) by Tautology.from(lastStep, removeExists) + have(in(singleton(x), singletonSets(S)) |- in(x, S)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := t, y := singleton(x), z := singletonSets(S))) + } + } + + val ifContainsSingletonIsDiscrete = Theorem( + (topology(X, T), ∀(x, x ∈ X ==> singleton(x) ∈ T)) |- discreteTopology(X, T) + ) { + assume(∀(x, x ∈ X ==> singleton(x) ∈ T), topology(X, T)) + val topo = have(nonEmpty(X) /\ setOfSubsets(X, T) /\ containsExtremes(X, T) /\ containsUnion(T) /\ containsIntersection(T)) by Tautology.from(topology.definition) + have(∀(x, x ∈ X ==> singleton(x) ∈ T)) by Tautology + val singleDef = thenHave((x ∈ X) ==> (singleton(x) ∈ T)) by InstantiateForall(x) + have(T === powerSet(X)) subproof { + // show T subs powerSet(X) (by def of topology) + val left = have(T ⊆ powerSet(X)) by Tautology.from(topo, setOfSubsets.definition) + // show powerSet(X) subs T + + // For any S ⊆ X we have S = U{x} -> S ∈ T by unionDef + have((S ⊆ X) |- S ∈ T) subproof { + assume(S ⊆ X) + // prove union(cartesianProduct(S, S)) ⊆ T + // -> S = union(union(cartesianProduct(S, S))) in T + have(forall(z, in(z, S) ==> in(z, X))) by Tautology.from(subsetAxiom of (x := S, y := X)) + thenHave(in(z, S) ==> in(z, X)) by InstantiateForall(z) + have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(lastStep, singleDef of (x := z)) + // have(in(z, S) /\ forall(a, in(z, S) <=> in(singleton(z), V)) |- in(singleton(z), V)) + // have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(sorry) + // have(in(singleton(z), singleton(S)) ==> in(singleton(z), T)) + // have(singleton(S) ⊆ T) + // have(union(singleton(S)) ∈ T) + // have(S ∈ T) + sorry + } + have(in(S, powerSet(X)) ==> in(S, T)) by Tautology.from(lastStep, powerAxiom of (x := S, y := X)) + thenHave(forall(S, in(S, powerSet(X)) ==> in(S, T))) by RightForall + val right = have(powerSet(X) ⊆ T) by Tautology.from(lastStep, subsetAxiom of (x := powerSet(X), y := T, z := S)) + + have(thesis) by Tautology.from(left, right, equalityBySubset of (x := powerSet(X), y := T)) + } + have(discreteTopology(X, T)) by Tautology.from(lastStep, topo, discreteTopology.definition) + } } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala new file mode 100644 index 000000000..5344283ff --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala @@ -0,0 +1,19 @@ +package lisa.maths.topology + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* + +import lisa.automation.kernel.CommonTactics.Definition + +import lisa.maths.topology.Topology.* +import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.SetTheoryBasics.* + +object Instances extends lisa.Main { + // var defs + private val x, y, z, a, b, c, t, p = variable + private val X, T = variable + private val S, A, B, Y = variable + +} From 74f5964af5fcd16858d9c9e89c58daaa3a7e4c2f Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Fri, 6 Dec 2024 15:55:39 +0100 Subject: [PATCH 12/38] Direct image + proof of direct image --- .../scala/lisa/maths/topology/Instances.scala | 184 +++++++++++++++++- 1 file changed, 181 insertions(+), 3 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index f499934a4..c59dfcc0b 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -1,12 +1,17 @@ package Topology import lisa.maths.topology.Topology.* -import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory.* +import lisa.automation.kernel.CommonTactics.* +import lisa.maths.settheory.functions.Functionals.* +import lisa.automation.settheory.SetTheoryTactics.UniqueComprehension +import lisa.automation.settheory.SetTheoryTactics.TheConditional object Instances extends lisa.Main { + import lisa.maths.settheory.SetTheory.* // var defs - private val X, T = variable - private val S, A, B, Y = variable + private val x, z, X, T = variable + private val S, A, B, a, b, c, Y = variable val discreteTopology = DEF(X, T) --> (T === powerSet(X)) val indiscreteTopology = DEF(X, T) --> ∅ ∈ T /\ X ∈ T /\ forall(S, in(S, T) ==> ((S === X) \/ (S === ∅))) @@ -17,6 +22,179 @@ object Instances extends lisa.Main { sorry } + private val f, y, s = variable + inline def directImageFormula = y ∈ s <=> (y ∈ functionRange(f) /\ ∃(x, (app(f, x) === y) /\ x ∈ A)) + + val directImageUniqueness = Theorem( + (functional(f), subset(A, functionDomain(f))) |- ∃!(s, forall(y, directImageFormula)) + ) { + have(∃!(s, forall(y, directImageFormula))) by UniqueComprehension(functionRange(f), lambda(y, ∃(x, (app(f, x) === y) /\ x ∈ A))) + thenHave(thesis) by Weakening + } + + val directImage = DEF(f, A) --> TheConditional(s, forall(y, directImageFormula))(directImageUniqueness) + + val directImageSetUnion = Theorem( + functional(f) /\ + subset(A, functionDomain(f)) /\ + subset(B, functionDomain(f)) + |- setUnion(directImage(f, A), directImage(f, B)) === directImage(f, setUnion(A, B)) + ) { + assume( + functional(f) /\ + subset(A, functionDomain(f)) /\ + subset(B, functionDomain(f)) + ) + + val subsetAorB = have(subset(setUnion(A, B), functionDomain(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionDomain(f))) + + have(forall(z, z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, A))(directImage.definition) + val defA = thenHave(z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) + have(forall(z, z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, B))(directImage.definition of (A := B)) + val defB = thenHave(z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z) + have( + subset(setUnion(A, B), functionDomain(f)) |- + forall( + z, + z ∈ directImage(f, setUnion(A, B)) <=> + (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) + ) + ) by InstantiateForall(directImage(f, setUnion(A, B)))(directImage.definition of (A := setUnion(A, B))) + thenHave( + subset(setUnion(A, B), functionDomain(f)) |- + z ∈ directImage(f, setUnion(A, B)) <=> + (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) + ) by InstantiateForall(z) + val defAorB = have( + z ∈ directImage(f, setUnion(A, B)) <=> + (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) + ) by Tautology.from(lastStep, subsetAorB) + + val forward = have(z ∈ setUnion(directImage(f, A), directImage(f, B)) ==> z ∈ directImage(f, setUnion(A, B))) subproof { + val firstPart = have(z ∈ setUnion(directImage(f, A), directImage(f, B)) |- (z ∈ directImage(f, A)) \/ (z ∈ directImage(f, B))) by Tautology.from( + setUnionMembership of (x := directImage(f, A), y := directImage(f, B)) + ) + assume(z ∈ setUnion(directImage(f, A), directImage(f, B))) + have(z ∈ functionRange(f) /\ ((∃(x, (app(f, x) === z) /\ x ∈ A)) \/ ∃(x, (app(f, x) === z) /\ x ∈ B))) by Tautology.from( + defA, + defB, + firstPart + ) + val partialResult = thenHave(z ∈ functionRange(f) /\ (∃(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))))) by Tautology + have( + (z ∈ functionRange(f), (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) + |- + (app(f, x) === z) /\ x ∈ setUnion(A, B) + ) by Tautology.from(setUnionMembership of (x := A, y := B, z := x), defAorB) + thenHave((z ∈ functionRange(f), (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by RightExists + have((z ∈ functionRange(f), (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- z ∈ directImage(f, setUnion(A, B))) by Tautology.from(lastStep, defAorB) + thenHave((z ∈ functionRange(f), exists(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B)))) |- z ∈ directImage(f, setUnion(A, B))) by LeftExists + have(z ∈ directImage(f, setUnion(A, B))) by Tautology.from(lastStep, partialResult) + } + val backward = have(z ∈ directImage(f, setUnion(A, B)) ==> z ∈ setUnion(directImage(f, A), directImage(f, B))) subproof { + val initial = have( + z ∈ directImage(f, setUnion(A, B)) |- + (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) + ) by Tautology.from(defAorB) + val intermediate = have( + z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- + (z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ A) + \/ (z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ B) + ) by Tautology.from(setUnionMembership of (x := A, y := B, z := x)) + + assume(z ∈ directImage(f, setUnion(A, B))) + have(z ∈ functionRange(f) /\ exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by Tautology.from(defAorB) + have((app(f, x) === z) /\ x ∈ A |- (app(f, x) === z) /\ x ∈ A) by Tautology + val existsA = thenHave( + (app(f, x) === z) /\ x ∈ A |- + exists(x, (app(f, x) === z) /\ x ∈ A) + ) by RightExists + have((app(f, x) === z) /\ x ∈ B |- (app(f, x) === z) /\ x ∈ B) by Tautology + val existsB = thenHave( + (app(f, x) === z) /\ x ∈ B |- + exists(x, (app(f, x) === z) /\ x ∈ B) + ) by RightExists + have( + z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- + (z ∈ directImage(f, A)) \/ (z ∈ directImage(f, B)) + ) by Tautology.from(intermediate, existsA, existsB, defA, defB) + have( + (z ∈ functionRange(f), (app(f, x) === z) /\ x ∈ setUnion(A, B)) |- + z ∈ setUnion(directImage(f, A), directImage(f, B)) + ) by Tautology.from(lastStep, setUnionMembership of (x := directImage(f, A), y := directImage(f, B), z := z)) + thenHave( + (z ∈ functionRange(f), exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) |- + z ∈ setUnion(directImage(f, A), directImage(f, B)) + ) by LeftExists + have(z ∈ setUnion(directImage(f, A), directImage(f, B))) by Tautology.from(lastStep, initial) + } + have(z ∈ directImage(f, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, A), directImage(f, B))) by RightIff(forward, backward) + thenHave(∀(z, z ∈ directImage(f, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, A), directImage(f, B)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, setUnion(A, B)), y := setUnion(directImage(f, A), directImage(f, B))))) + } + + /*inline def directImageUnionFormula = y ∈ s <=> y ∈ functionRange(f) /\ ∃(A, y ∈ directImage(f, A)) + val directImageUnionUniqueness = Theorem( + functional(f) |- ∃!(s, ∀(y, directImageUnionFormula)) + ) { + have(∃!(s, ∀(y, directImageUnionFormula))) by UniqueComprehension(functionRange(f), lambda(y, ∃(A, y ∈ directImage(f, A)))) + thenHave(thesis) by Weakening + } + + val directImageUnion = DEF(f, A) --> TheConditional(s, forall(z, z ∈ s <=> ∃(A, z ∈ directImage(f, A))))(directImageUnionUniqueness) + + val directImageUnionThm = Theorem( + functional(f) /\ forall(A, A ∈ T ==> subset(A, functionDomain(f))) |- + union(directImage(f, T)) === directImage(f, union(A)) + ) { + sorry + }*/ + + inline def inverseImageFormula = x ∈ s <=> (x ∈ functionDomain(f) /\ app(f, x) ∈ B) + + val inverseImageUniqueness = Theorem( + (functional(f), subset(B, functionRange(f))) |- ∃!(s, forall(x, inverseImageFormula)) + ) { + have(∃!(s, forall(x, inverseImageFormula))) by UniqueComprehension(functionDomain(f), lambda(x, app(f, x) ∈ B)) + thenHave(thesis) by Weakening + } + + val inverseImage = DEF(f, B) --> TheConditional(s, forall(x, inverseImageFormula))(inverseImageUniqueness) + + val inverseImageUnion = Theorem( + functional(f) /\ + subset(A, functionRange(f)) /\ + subset(B, functionRange(f)) + |- setUnion(inverseImage(f, A), inverseImage(f, B)) === inverseImage(f, setUnion(A, B)) + ) { + assume( + functional(f) /\ + subset(A, functionRange(f)) /\ + subset(B, functionRange(f)) + ) + + val subsetAorB = have(subset(setUnion(A, B), functionDomain(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionDomain(f))) + + have(forall(z, z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, A))(directImage.definition) + val defA = thenHave(z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) + have(forall(z, z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, B))(directImage.definition of (A := B)) + val defB = thenHave(z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z) + + val forward = have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) ==> z ∈ inverseImage(f, setUnion(A, B))) subproof { + have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) |- (z ∈ inverseImage(f, A)) \/ (z ∈ inverseImage(f, B))) by Tautology.from( + setUnionMembership of (x := inverseImage(f, A), y := inverseImage(f, B)) + ) + // have(z ∈ inverseImage(f, A) |- app(f, z) ∈ B) by Tautology.from(inverseImage.definition of (z := x)) + sorry + } + val backward = have(z ∈ inverseImage(f, setUnion(A, B)) ==> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) subproof { + sorry + } + have(z ∈ inverseImage(f, setUnion(A, B)) <=> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) by RightIff(forward, backward) + thenHave(∀(z, z ∈ inverseImage(f, setUnion(A, B)) <=> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := inverseImage(f, setUnion(A, B)), y := setUnion(inverseImage(f, A), inverseImage(f, B))))) + } + val indiscreteIsTopology = Theorem( indiscreteTopology(X, T) ==> topology(X, T) ) { From 8b3833d16c701e16c1ec7d1d5d40c418bcf1152b Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Fri, 6 Dec 2024 16:30:56 +0100 Subject: [PATCH 13/38] Fix --- .../scala/lisa/maths/topology/Instances.scala | 24 +++++++++++-------- .../lisa/maths/topology/Intersection.scala | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 0646e6527..4babfbedf 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -17,7 +17,7 @@ import lisa.automation.settheory.SetTheoryTactics.TheConditional object Instances extends lisa.Main { import lisa.maths.settheory.SetTheory.* // var defs - private val x, y, z, a, b, c, t, p = variable + private val x, y, z, a, b, c, t, p, f, s = variable private val X, T = variable private val S, A, B, Y = variable @@ -69,7 +69,7 @@ object Instances extends lisa.Main { } val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === unorderedPair(∅, X)) - private val f, y, s = variable + inline def directImageFormula = y ∈ s <=> (y ∈ functionRange(f) /\ ∃(x, (app(f, x) === y) /\ x ∈ A)) val directImageUniqueness = Theorem( @@ -220,12 +220,12 @@ object Instances extends lisa.Main { subset(B, functionRange(f)) ) - val subsetAorB = have(subset(setUnion(A, B), functionDomain(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionDomain(f))) + /*val subsetAorB = have(subset(setUnion(A, B), functionDomain(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionDomain(f))) have(forall(z, z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, A))(directImage.definition) val defA = thenHave(z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) have(forall(z, z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, B))(directImage.definition of (A := B)) - val defB = thenHave(z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z) + val defB = thenHave(z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z)*/ val forward = have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) ==> z ∈ inverseImage(f, setUnion(A, B))) subproof { have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) |- (z ∈ inverseImage(f, A)) \/ (z ∈ inverseImage(f, B))) by Tautology.from( @@ -375,16 +375,17 @@ object Instances extends lisa.Main { assume(in(x, S)) have(t === singleton(x) |- ((t === singleton(x)) /\ in(x, S))) by Tautology thenHave(t === singleton(x) |- exists(x, ((t === singleton(x)) /\ in(x, S)))) by RightExists - have((t === singleton(x)) ==> in(t, singletonSets(S))) by Tautology.from(lastStep, memb) + sorry + /*have((t === singleton(x)) ==> in(t, singletonSets(S))) by Tautology.from(lastStep, memb) thenHave(forall(t, (t === singleton(x)) ==> in(t, singletonSets(S)))) by RightForall thenHave((singleton(x) === singleton(x)) ==> in(singleton(x), singletonSets(S))) by InstantiateForall(singleton(x)) - have(thesis) by Tautology.from(lastStep) + have(thesis) by Tautology.from(lastStep)*/ } have(in(singleton(x), singletonSets(S)) |- in(x, S)) subproof { assume(in(singleton(x), singletonSets(S))) val removeExists = have((exists(y, in(y, S) /\ (t === singleton(y))), t === singleton(x)) |- in(x, S)) subproof { - have((in(y, S), t === singleton(x), t === singleton(y)) |- (in(y, S), t === singleton(x), t === singleton(y))) + /*have((in(y, S), t === singleton(x), t === singleton(y)) |- (in(y, S), t === singleton(x), t === singleton(y))) thenHave((in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by Tautology.from( singletonExtensionality, equalityTransitivity of (x := singleton(x), y := t, z := singleton(y)), @@ -395,12 +396,15 @@ object Instances extends lisa.Main { lastStep, existentialConjunctionWithClosedFormula of (x := y, p := (t === singleton(x))) ) - thenHave(thesis) by Tautology + thenHave(thesis) by Tautology*/ + sorry } have((t === singleton(x), in(t, singletonSets(S))) |- (t === singleton(x), exists(x, ((t === singleton(x)) /\ in(x, S))))) by Tautology.from(singletonSetsMembershipRaw of (x := y)) - have((t === singleton(x), in(t, singletonSets(S))) |- in(x, S)) by Tautology.from(lastStep, removeExists) - have(in(singleton(x), singletonSets(S)) |- in(x, S)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := t, y := singleton(x), z := singletonSets(S))) + sorry + /* have((t === singleton(x), in(t, singletonSets(S))) |- in(x, S)) by Tautology.from(lastStep, removeExists) + have(in(singleton(x), singletonSets(S)) |- in(x, S)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := t, y := singleton(x), z := singletonSets(S)))*/ } + sorry } val ifContainsSingletonIsDiscrete = Theorem( diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala index 5344283ff..abb6503ef 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala @@ -10,7 +10,7 @@ import lisa.maths.settheory.* import lisa.maths.settheory.SetTheory.* import lisa.maths.settheory.SetTheoryBasics.* -object Instances extends lisa.Main { +object Intersection extends lisa.Main { // var defs private val x, y, z, a, b, c, t, p = variable private val X, T = variable From 30456d569f45e24a47f4f8f3334c5791eb94a3ef Mon Sep 17 00:00:00 2001 From: Dobrin Bashev Date: Fri, 6 Dec 2024 17:17:29 +0100 Subject: [PATCH 14/38] Add implicationProof --- .../scala/lisa/maths/topology/Instances.scala | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 4babfbedf..942b31fe6 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -351,7 +351,67 @@ object Instances extends lisa.Main { val singletonSetsUniquenes = Theorem( ∃!(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x))))) ) { - val implicationProof = have(exists(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { sorry } + val implicationProof = have(exists(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { + + val elementInSingleton = have(in(x, singleton(x))) subproof { + have(thesis) by Tautology.from(pairAxiom of (x := x, y := x, z := x)) + } + + val singletonInPowerSet = have(in(x, S) |- in(singleton(x), powerSet(S))) subproof { + assume(in(x, S)) + val introduceY = thenHave((x === y) |- in(y, S)) by Substitution.ApplyRules(x === y) + val useSingleton = have(in(y, singleton(x)) |- in(y, S)) by Tautology.from(introduceY, singletonHasNoExtraElements) + thenHave(() |- (!(in(y, singleton(x))), in(y, S))) by RightNot + thenHave(() |- in(y, singleton(x)) ==> in(y, S)) by Tautology + val universalY = thenHave(() |- forall(y, in(y, singleton(x)) ==> in(y, S))) by RightForall + val singletonIsSubset = have(() |- singleton(x) ⊆ S) by Tautology.from(universalY, subsetAxiom of (z := y, y := S, x := singleton(x))) + have(thesis) by Tautology.from(singletonIsSubset, powerAxiom of (x := singleton(x), y := S)) + } + + val abExist = have((singleton(t) === pair(x, x)) /\ in(x, S) |- ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) subproof { + have((singleton(t) === pair(x, x)) /\ in(x, S) |- (singleton(t) === pair(x, x)) /\ in(x, S) /\ in(x, S)) by Restate + thenHave((singleton(t) === pair(x, x)) /\ in(x, S) |- exists(b, (singleton(t) === pair(x, b)) /\ in(x, S) /\ in(b, S))) by RightExists + thenHave((singleton(t) === pair(x, x)) /\ in(x, S) |- exists(a, exists(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) by RightExists + } + + val cartesianInstantiated = have(in(singleton(t), cartesianProduct(S, S)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, S)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S))))) subproof { + val instance = have((cartesianProduct(x, y) === cartesianProduct(x, y)) <=> ∀(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by InstantiateForall(cartesianProduct(x, y))(cartesianProduct.definition) + thenHave(∀(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by Tautology + thenHave(in(singleton(t), cartesianProduct(x, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, x) /\ in(b, y))))) by InstantiateForall(singleton(t)) + thenHave(forall(x, in(singleton(t), cartesianProduct(x, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by RightForall + thenHave(in(singleton(t), cartesianProduct(S, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, y))))) by InstantiateForall(S) + thenHave(forall(y, in(singleton(t), cartesianProduct(S, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, y)))))) by RightForall + thenHave(in(singleton(t), cartesianProduct(S, S)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, S)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S))))) by InstantiateForall(S) + } + + val init = have(in(x, S) |- in(x, S)) by Restate + val inSS = have(in(x, S) |- in(x, setUnion(S, S))) by Tautology.from(init, setUnionMembership of (z := x, x := S, y := S)) + val step1 = have(in(x, S) |- in(singleton(x), powerSet(setUnion(S, S))) /\ (singleton(singleton(x)) === pair(x, x))) by + Tautology.from(inSS, singletonInPowerSet of (S := setUnion(S, S))) + thenHave((in(x, S), t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by + Substitution.ApplyRules(singleton(x) === t) + val introduceT = thenHave(in(x, S) /\ (t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by Tautology + val step2 = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ (singleton(t) === pair(x, x))) by + Tautology.from(introduceT, singletonInPowerSet of (x := t, S := powerSet(setUnion(S, S)))) + val extendRHS = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) + /\ ((singleton(t) === pair(x, x)) /\ in(x, S))) by Tautology.from(step2, elementInSingleton of (x := t)) + val existAB = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) by Tautology.from(extendRHS, abExist) + val cartesian = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), cartesianProduct(S, S)) /\ in(t, singleton(t))) by + Tautology.from(existAB, cartesianInstantiated) + val introduceZ = thenHave(in(x, S) /\ (t === singleton(x)) |- exists(z, in(z, cartesianProduct(S, S)) /\ in(t, z))) by RightExists + val unionOfProduct = have((in(x, S) /\ (t === singleton(x))) |- in(t, union(cartesianProduct(S, S)))) by + Tautology.from(introduceZ, unionAxiom of (y := z, x := cartesianProduct(S, S), z := t)) + thenHave(exists(x, (in(x, S) /\ (t === singleton(x)))) |- in(t, union(cartesianProduct(S, S)))) by LeftExists + } have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( union(cartesianProduct(S, S)), lambda(t, exists(x, in(x, S) /\ (t === singleton(x)))), From 0886df8f3363f8065d19e43d8fc0925e3e7369ac Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Fri, 6 Dec 2024 17:59:13 +0100 Subject: [PATCH 15/38] Proof of inverse image union --- .../scala/lisa/maths/topology/Instances.scala | 60 ++++++++++++++----- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 4babfbedf..25747f74b 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -139,10 +139,6 @@ object Instances extends lisa.Main { have(z ∈ directImage(f, setUnion(A, B))) by Tautology.from(lastStep, partialResult) } val backward = have(z ∈ directImage(f, setUnion(A, B)) ==> z ∈ setUnion(directImage(f, A), directImage(f, B))) subproof { - val initial = have( - z ∈ directImage(f, setUnion(A, B)) |- - (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) - ) by Tautology.from(defAorB) val intermediate = have( z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- (z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ A) @@ -173,7 +169,7 @@ object Instances extends lisa.Main { (z ∈ functionRange(f), exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) |- z ∈ setUnion(directImage(f, A), directImage(f, B)) ) by LeftExists - have(z ∈ setUnion(directImage(f, A), directImage(f, B))) by Tautology.from(lastStep, initial) + have(z ∈ setUnion(directImage(f, A), directImage(f, B))) by Tautology.from(lastStep, defAorB) } have(z ∈ directImage(f, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, A), directImage(f, B))) by RightIff(forward, backward) thenHave(∀(z, z ∈ directImage(f, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, A), directImage(f, B)))) by RightForall @@ -208,7 +204,7 @@ object Instances extends lisa.Main { val inverseImage = DEF(f, B) --> TheConditional(s, forall(x, inverseImageFormula))(inverseImageUniqueness) - val inverseImageUnion = Theorem( + val inverseImageSetUnion = Theorem( functional(f) /\ subset(A, functionRange(f)) /\ subset(B, functionRange(f)) @@ -220,22 +216,56 @@ object Instances extends lisa.Main { subset(B, functionRange(f)) ) - /*val subsetAorB = have(subset(setUnion(A, B), functionDomain(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionDomain(f))) + val subsetAorB = have(subset(setUnion(A, B), functionRange(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionRange(f))) - have(forall(z, z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, A))(directImage.definition) - val defA = thenHave(z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) - have(forall(z, z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, B))(directImage.definition of (A := B)) - val defB = thenHave(z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z)*/ + have(forall(z, z ∈ inverseImage(f, A) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ A))) by InstantiateForall(inverseImage(f, A))(inverseImage.definition of (B := A)) + val defA = thenHave(z ∈ inverseImage(f, A) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ A)) by InstantiateForall(z) + have(forall(z, z ∈ inverseImage(f, B) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ B))) by InstantiateForall(inverseImage(f, B))(inverseImage.definition) + val defB = thenHave(z ∈ inverseImage(f, B) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ B)) by InstantiateForall(z) + have( + subset(setUnion(A, B), functionRange(f)) |- + forall( + z, + z ∈ inverseImage(f, setUnion(A, B)) <=> + (z ∈ functionDomain(f) /\ app(f, z) ∈ setUnion(A, B)) + ) + ) by InstantiateForall(inverseImage(f, setUnion(A, B)))(inverseImage.definition of (B := setUnion(A, B))) + thenHave( + subset(setUnion(A, B), functionRange(f)) |- + z ∈ inverseImage(f, setUnion(A, B)) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ setUnion(A, B)) + ) by InstantiateForall(z) + val defAorB = have( + z ∈ inverseImage(f, setUnion(A, B)) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ setUnion(A, B)) + ) by Tautology.from(lastStep, subsetAorB) val forward = have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) ==> z ∈ inverseImage(f, setUnion(A, B))) subproof { - have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) |- (z ∈ inverseImage(f, A)) \/ (z ∈ inverseImage(f, B))) by Tautology.from( + val firstPart = have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) |- (z ∈ inverseImage(f, A)) \/ (z ∈ inverseImage(f, B))) by Tautology.from( setUnionMembership of (x := inverseImage(f, A), y := inverseImage(f, B)) ) - // have(z ∈ inverseImage(f, A) |- app(f, z) ∈ B) by Tautology.from(inverseImage.definition of (z := x)) - sorry + assume(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) + have(z ∈ functionDomain(f) /\ ((app(f, z) ∈ A) \/ (app(f, z) ∈ B))) by Tautology.from( + defA, + defB, + firstPart + ) + val partialResult = have(z ∈ functionDomain(f) /\ (app(f, z) ∈ setUnion(A, B))) by Tautology.from( + lastStep, + setUnionMembership of (x := A, y := B, z := app(f, z)) + ) + have(thesis) by Tautology.from(defAorB, lastStep) } val backward = have(z ∈ inverseImage(f, setUnion(A, B)) ==> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) subproof { - sorry + assume(z ∈ inverseImage(f, setUnion(A, B))) + have((z ∈ inverseImage(f, A)) \/ (z ∈ inverseImage(f, B))) by Tautology.from( + defAorB, + setUnionMembership of (x := A, y := B, z := app(f, z)), + defA, + defB + ) + have(thesis) by Tautology.from( + lastStep, + setUnionMembership of (x := inverseImage(f, A), y := inverseImage(f, B), z := z) + ) } have(z ∈ inverseImage(f, setUnion(A, B)) <=> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) by RightIff(forward, backward) thenHave(∀(z, z ∈ inverseImage(f, setUnion(A, B)) <=> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)))) by RightForall From bd92f77f9adfab3d0d66d8e9d92603d30595b0e5 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Fri, 6 Dec 2024 21:55:19 +0100 Subject: [PATCH 16/38] Mapping + Continuity --- .../scala/lisa/maths/topology/Instances.scala | 66 ++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 25747f74b..2b88838ff 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -18,8 +18,8 @@ object Instances extends lisa.Main { import lisa.maths.settheory.SetTheory.* // var defs private val x, y, z, a, b, c, t, p, f, s = variable - private val X, T = variable - private val S, A, B, Y = variable + private val X, T, T1, T2 = variable + private val S, A, B, Y, o, O, O2 = variable val discreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === powerSet(X)) @@ -473,4 +473,66 @@ object Instances extends lisa.Main { } have(discreteTopology(X, T)) by Tautology.from(lastStep, topo, discreteTopology.definition) } + + // ------------------- + // Mappings + // ------------------- + val mapping = DEF(f, X, T1, Y, T2) --> + (functionFrom(f, X, Y) /\ topology(X, T1) /\ topology(Y, T2)) + + // ------------------- + // Continuity + // ------------------- + val continuous = DEF(f, X, T1, Y, T2) --> + (mapping(f, X, T1, Y, T2) /\ forall(O, O ∈ T2 ==> inverseImage(f, O) ∈ T1)) + + // ------------------- + // Connectedness + // ------------------- + val clopen = DEF(X, T, A) --> ( + topology(X, T) /\ + A ∈ T /\ setDifference(X, A) ∈ T + ) + + val connectedTop = DEF(X, T) --> ( + topology(X, T) /\ + forall(A, clopen(X, T, A) ==> ((A === emptySet) \/ (A === X))) + ) + + // Couldn't import surjectivity from FunctionProperties without an error, so here it is + val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) + + // ------------------- + // Intermediate value theorem + // ------------------- + val intermediateValueThm = Theorem((connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- connectedTop(Y, T2)) { + sorry + } + + // ------------------- + // Compactness + // ------------------- + + val cover = DEF(X, O) --> + forall(o, in(o, O) ==> subset(o, X)) /\ + subset(X, union(O)) + + val openCover = DEF(X, T, O) --> + cover(X, O) /\ subset(O, T) + + val finite = DEF(X) --> (X === emptySet) // TODO + + val compactness = DEF(X, T) --> + topology(X, T) /\ + forall( + O, + openCover(X, T, O) ==> + exists( + O2, // Another subcovering + subset(O2, O) /\ + openCover(X, T, O2) /\ + finite(O2) + ) + ) + } From 36da1a41c70d1d4e78f8096879cc54671ce907f2 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Sun, 8 Dec 2024 19:53:28 +0100 Subject: [PATCH 17/38] Main steps towards IVT Intermediate value theorem --- .../maths/settheory/SetTheoryBasics.scala | 58 +++ .../scala/lisa/maths/topology/Instances.scala | 482 ++++++++++++++---- 2 files changed, 440 insertions(+), 100 deletions(-) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala index 0e1849f24..fc302ea52 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -4,8 +4,10 @@ import lisa.automation.kernel.CommonTactics.Definition import lisa.automation.settheory.SetTheoryTactics.* import lisa.maths.Quantifiers.* import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.functions.Functionals.* import scala.collection.immutable.{Map => ScalaMap} +import lisa.maths.settheory.SetTheory.relationDomain object SetTheoryBasics extends lisa.Main { @@ -19,6 +21,8 @@ object SetTheoryBasics extends lisa.Main { private val c = variable private val d = variable private val t = variable + private val f = variable + private val r = variable /** * Theorems about basic sets @@ -135,4 +139,58 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := x, y := union(y))) } + val subsetTactic = Theorem((x ⊆ y, z ∈ x) |- z ∈ y) { + assume(x ⊆ y, z ∈ x) + + have(forall(z, z ∈ x ==> z ∈ y)) by Tautology.from(subsetAxiom) + thenHave(z ∈ x ==> z ∈ y) by InstantiateForall(z) + thenHave(thesis) by Tautology + } + + /** + * Lemma --- Range introduction and elimination rules. If en element is in the image of a function, then it has a preimage inside its domain. + * + * `functional(f) |- y ⊆ Im(f) <=> ∃x ∈ Dom(f). f(x) = y` + */ + val functionRangeMembership = Lemma(functional(f) |- in(y, relationRange(f)) <=> ∃(x, in(x, relationDomain(f)) /\ (app(f, x) === y))) { + assume(functional(f)) + + have(forall(y, y ∈ relationRange(f) <=> ∃(x, in(pair(x, y), f)))) by InstantiateForall(relationRange(f))(relationRange.definition of (r := f)) + val defRange = thenHave(y ∈ relationRange(f) <=> ∃(x, in(pair(x, y), f))) by InstantiateForall(y) + + have(∀(x, x ∈ relationDomain(f) <=> ∃(y, pair(x, y) ∈ f))) by InstantiateForall(relationDomain(f))(relationDomain.definition of (r := f)) + val defDomain = thenHave(x ∈ relationDomain(f) <=> ∃(y, pair(x, y) ∈ f)) by InstantiateForall(x) + + val forward = have(y ∈ relationRange(f) ==> ∃(x, in(x, relationDomain(f)) /\ (app(f, x) === y))) subproof { + assume(y ∈ relationRange(f)) + have(pair(x, y) ∈ f |- pair(x, y) ∈ f) by Tautology + thenHave(pair(x, y) ∈ f |- ∃(y, pair(x, y) ∈ f)) by RightExists + have(pair(x, y) ∈ f |- x ∈ relationDomain(f) /\ (app(f, x) === y)) by Tautology.from( + lastStep, + pairInFunctionIsApp of (a := x, b := y), + defDomain + ) + thenHave(in(pair(x, y), f) |- ∃(x, x ∈ relationDomain(f) /\ (app(f, x) === y))) by RightExists + thenHave(∃(x, in(pair(x, y), f)) |- ∃(x, x ∈ relationDomain(f) /\ (app(f, x) === y))) by LeftExists + have(thesis) by Tautology.from(lastStep, defRange) + } + + val backward = have(∃(x, in(x, relationDomain(f)) /\ (app(f, x) === y)) |- y ∈ relationRange(f)) subproof { + have(in(x, relationDomain(f)) /\ (app(f, x) === y) |- pair(x, y) ∈ f) by Tautology.from(pairInFunctionIsApp of (a := x, b := y)) + thenHave(in(x, relationDomain(f)) /\ (app(f, x) === y) |- ∃(x, pair(x, y) ∈ f)) by RightExists + have(in(x, relationDomain(f)) /\ (app(f, x) === y) |- y ∈ relationRange(f)) by Tautology.from( + lastStep, + defRange + ) + thenHave(thesis) by LeftExists + } + + have(thesis) by Tautology.from(forward, backward) + } + + val equalitySymmetry = Theorem( + x === y |- y === x + ) { + sorry + } } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 2b88838ff..9ac5c9396 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -17,7 +17,7 @@ import lisa.automation.settheory.SetTheoryTactics.TheConditional object Instances extends lisa.Main { import lisa.maths.settheory.SetTheory.* // var defs - private val x, y, z, a, b, c, t, p, f, s = variable + private val x, y, z, a, b, c, t, p, f, r, s = variable private val X, T, T1, T2 = variable private val S, A, B, Y, o, O, O2 = variable @@ -70,83 +70,83 @@ object Instances extends lisa.Main { val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === unorderedPair(∅, X)) - inline def directImageFormula = y ∈ s <=> (y ∈ functionRange(f) /\ ∃(x, (app(f, x) === y) /\ x ∈ A)) + inline def directImageFormula = y ∈ s <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ A)) val directImageUniqueness = Theorem( - (functional(f), subset(A, functionDomain(f))) |- ∃!(s, forall(y, directImageFormula)) + (functionFrom(f, X, Y), subset(A, X)) |- ∃!(s, forall(y, directImageFormula)) ) { - have(∃!(s, forall(y, directImageFormula))) by UniqueComprehension(functionRange(f), lambda(y, ∃(x, (app(f, x) === y) /\ x ∈ A))) + have(∃!(s, forall(y, directImageFormula))) by UniqueComprehension(Y, lambda(y, ∃(x, (app(f, x) === y) /\ x ∈ A))) thenHave(thesis) by Weakening } - val directImage = DEF(f, A) --> TheConditional(s, forall(y, directImageFormula))(directImageUniqueness) + val directImage = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImageFormula))(directImageUniqueness) val directImageSetUnion = Theorem( - functional(f) /\ - subset(A, functionDomain(f)) /\ - subset(B, functionDomain(f)) - |- setUnion(directImage(f, A), directImage(f, B)) === directImage(f, setUnion(A, B)) + functionFrom(f, X, Y) /\ + subset(A, X) /\ + subset(B, X) + |- setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)) === directImage(f, X, Y, setUnion(A, B)) ) { assume( - functional(f) /\ - subset(A, functionDomain(f)) /\ - subset(B, functionDomain(f)) + functionFrom(f, X, Y) /\ + subset(A, X) /\ + subset(B, X) ) - val subsetAorB = have(subset(setUnion(A, B), functionDomain(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionDomain(f))) + val subsetAorB = have(subset(setUnion(A, B), X)) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := X)) - have(forall(z, z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, A))(directImage.definition) - val defA = thenHave(z ∈ directImage(f, A) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) - have(forall(z, z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, B))(directImage.definition of (A := B)) - val defB = thenHave(z ∈ directImage(f, B) <=> (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z) + have(forall(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) + val defA = thenHave(z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) + have(forall(z, z ∈ directImage(f, X, Y, B) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, X, Y, B))(directImage.definition of (A := B)) + val defB = thenHave(z ∈ directImage(f, X, Y, B) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z) have( - subset(setUnion(A, B), functionDomain(f)) |- + subset(setUnion(A, B), X) |- forall( z, - z ∈ directImage(f, setUnion(A, B)) <=> - (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) + z ∈ directImage(f, X, Y, setUnion(A, B)) <=> + (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) ) - ) by InstantiateForall(directImage(f, setUnion(A, B)))(directImage.definition of (A := setUnion(A, B))) + ) by InstantiateForall(directImage(f, X, Y, setUnion(A, B)))(directImage.definition of (A := setUnion(A, B))) thenHave( - subset(setUnion(A, B), functionDomain(f)) |- - z ∈ directImage(f, setUnion(A, B)) <=> - (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) + subset(setUnion(A, B), X) |- + z ∈ directImage(f, X, Y, setUnion(A, B)) <=> + (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) ) by InstantiateForall(z) val defAorB = have( - z ∈ directImage(f, setUnion(A, B)) <=> - (z ∈ functionRange(f) /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) + z ∈ directImage(f, X, Y, setUnion(A, B)) <=> + (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) ) by Tautology.from(lastStep, subsetAorB) - val forward = have(z ∈ setUnion(directImage(f, A), directImage(f, B)) ==> z ∈ directImage(f, setUnion(A, B))) subproof { - val firstPart = have(z ∈ setUnion(directImage(f, A), directImage(f, B)) |- (z ∈ directImage(f, A)) \/ (z ∈ directImage(f, B))) by Tautology.from( - setUnionMembership of (x := directImage(f, A), y := directImage(f, B)) + val forward = have(z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)) ==> z ∈ directImage(f, X, Y, setUnion(A, B))) subproof { + val firstPart = have(z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)) |- (z ∈ directImage(f, X, Y, A)) \/ (z ∈ directImage(f, X, Y, B))) by Tautology.from( + setUnionMembership of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B)) ) - assume(z ∈ setUnion(directImage(f, A), directImage(f, B))) - have(z ∈ functionRange(f) /\ ((∃(x, (app(f, x) === z) /\ x ∈ A)) \/ ∃(x, (app(f, x) === z) /\ x ∈ B))) by Tautology.from( + assume(z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))) + have(z ∈ Y /\ ((∃(x, (app(f, x) === z) /\ x ∈ A)) \/ ∃(x, (app(f, x) === z) /\ x ∈ B))) by Tautology.from( defA, defB, firstPart ) - val partialResult = thenHave(z ∈ functionRange(f) /\ (∃(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))))) by Tautology + val partialResult = thenHave(z ∈ Y /\ (∃(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))))) by Tautology have( - (z ∈ functionRange(f), (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) + (z ∈ Y, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- (app(f, x) === z) /\ x ∈ setUnion(A, B) ) by Tautology.from(setUnionMembership of (x := A, y := B, z := x), defAorB) - thenHave((z ∈ functionRange(f), (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by RightExists - have((z ∈ functionRange(f), (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- z ∈ directImage(f, setUnion(A, B))) by Tautology.from(lastStep, defAorB) - thenHave((z ∈ functionRange(f), exists(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B)))) |- z ∈ directImage(f, setUnion(A, B))) by LeftExists - have(z ∈ directImage(f, setUnion(A, B))) by Tautology.from(lastStep, partialResult) + thenHave((z ∈ Y, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by RightExists + have((z ∈ Y, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- z ∈ directImage(f, X, Y, setUnion(A, B))) by Tautology.from(lastStep, defAorB) + thenHave((z ∈ Y, exists(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B)))) |- z ∈ directImage(f, X, Y, setUnion(A, B))) by LeftExists + have(z ∈ directImage(f, X, Y, setUnion(A, B))) by Tautology.from(lastStep, partialResult) } - val backward = have(z ∈ directImage(f, setUnion(A, B)) ==> z ∈ setUnion(directImage(f, A), directImage(f, B))) subproof { + val backward = have(z ∈ directImage(f, X, Y, setUnion(A, B)) ==> z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))) subproof { val intermediate = have( - z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- - (z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ A) - \/ (z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ B) + z ∈ Y /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- + (z ∈ Y /\ (app(f, x) === z) /\ x ∈ A) + \/ (z ∈ Y /\ (app(f, x) === z) /\ x ∈ B) ) by Tautology.from(setUnionMembership of (x := A, y := B, z := x)) - assume(z ∈ directImage(f, setUnion(A, B))) - have(z ∈ functionRange(f) /\ exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by Tautology.from(defAorB) + assume(z ∈ directImage(f, X, Y, setUnion(A, B))) + have(z ∈ Y /\ exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by Tautology.from(defAorB) have((app(f, x) === z) /\ x ∈ A |- (app(f, x) === z) /\ x ∈ A) by Tautology val existsA = thenHave( (app(f, x) === z) /\ x ∈ A |- @@ -158,105 +158,105 @@ object Instances extends lisa.Main { exists(x, (app(f, x) === z) /\ x ∈ B) ) by RightExists have( - z ∈ functionRange(f) /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- - (z ∈ directImage(f, A)) \/ (z ∈ directImage(f, B)) + z ∈ Y /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- + (z ∈ directImage(f, X, Y, A)) \/ (z ∈ directImage(f, X, Y, B)) ) by Tautology.from(intermediate, existsA, existsB, defA, defB) have( - (z ∈ functionRange(f), (app(f, x) === z) /\ x ∈ setUnion(A, B)) |- - z ∈ setUnion(directImage(f, A), directImage(f, B)) - ) by Tautology.from(lastStep, setUnionMembership of (x := directImage(f, A), y := directImage(f, B), z := z)) + (z ∈ Y, (app(f, x) === z) /\ x ∈ setUnion(A, B)) |- + z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)) + ) by Tautology.from(lastStep, setUnionMembership of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B), z := z)) thenHave( - (z ∈ functionRange(f), exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) |- - z ∈ setUnion(directImage(f, A), directImage(f, B)) + (z ∈ Y, exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) |- + z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)) ) by LeftExists - have(z ∈ setUnion(directImage(f, A), directImage(f, B))) by Tautology.from(lastStep, defAorB) + have(z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))) by Tautology.from(lastStep, defAorB) } - have(z ∈ directImage(f, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, A), directImage(f, B))) by RightIff(forward, backward) - thenHave(∀(z, z ∈ directImage(f, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, A), directImage(f, B)))) by RightForall - andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, setUnion(A, B)), y := setUnion(directImage(f, A), directImage(f, B))))) + have(z ∈ directImage(f, X, Y, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))) by RightIff(forward, backward) + thenHave(∀(z, z ∈ directImage(f, X, Y, setUnion(A, B)) <=> z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, setUnion(A, B)), y := setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))))) } - /*inline def directImageUnionFormula = y ∈ s <=> y ∈ functionRange(f) /\ ∃(A, y ∈ directImage(f, A)) + /*inline def directImageUnionFormula = y ∈ s <=> y ∈ Y /\ ∃(A, y ∈ directImage(f, A)) val directImageUnionUniqueness = Theorem( functional(f) |- ∃!(s, ∀(y, directImageUnionFormula)) ) { - have(∃!(s, ∀(y, directImageUnionFormula))) by UniqueComprehension(functionRange(f), lambda(y, ∃(A, y ∈ directImage(f, A)))) + have(∃!(s, ∀(y, directImageUnionFormula))) by UniqueComprehension(Y, lambda(y, ∃(A, y ∈ directImage(f, A)))) thenHave(thesis) by Weakening } val directImageUnion = DEF(f, A) --> TheConditional(s, forall(z, z ∈ s <=> ∃(A, z ∈ directImage(f, A))))(directImageUnionUniqueness) val directImageUnionThm = Theorem( - functional(f) /\ forall(A, A ∈ T ==> subset(A, functionDomain(f))) |- + functional(f) /\ forall(A, A ∈ T ==> subset(A, X)) |- union(directImage(f, T)) === directImage(f, union(A)) ) { sorry }*/ - inline def inverseImageFormula = x ∈ s <=> (x ∈ functionDomain(f) /\ app(f, x) ∈ B) + inline def preimageFormula = x ∈ s <=> (x ∈ X /\ app(f, x) ∈ B) - val inverseImageUniqueness = Theorem( - (functional(f), subset(B, functionRange(f))) |- ∃!(s, forall(x, inverseImageFormula)) + val preimageUniqueness = Theorem( + (functionFrom(f, X, Y), subset(B, Y)) |- ∃!(s, forall(x, preimageFormula)) ) { - have(∃!(s, forall(x, inverseImageFormula))) by UniqueComprehension(functionDomain(f), lambda(x, app(f, x) ∈ B)) + have(∃!(s, forall(x, preimageFormula))) by UniqueComprehension(X, lambda(x, app(f, x) ∈ B)) thenHave(thesis) by Weakening } - val inverseImage = DEF(f, B) --> TheConditional(s, forall(x, inverseImageFormula))(inverseImageUniqueness) + val preimage = DEF(f, X, Y, B) --> TheConditional(s, forall(x, preimageFormula))(preimageUniqueness) - val inverseImageSetUnion = Theorem( - functional(f) /\ - subset(A, functionRange(f)) /\ - subset(B, functionRange(f)) - |- setUnion(inverseImage(f, A), inverseImage(f, B)) === inverseImage(f, setUnion(A, B)) + val preimageSetUnion = Theorem( + functionFrom(f, X, Y) /\ + subset(A, Y) /\ + subset(B, Y) + |- setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B)) === preimage(f, X, Y, setUnion(A, B)) ) { assume( - functional(f) /\ - subset(A, functionRange(f)) /\ - subset(B, functionRange(f)) + functionFrom(f, X, Y) /\ + subset(A, Y) /\ + subset(B, Y) ) - val subsetAorB = have(subset(setUnion(A, B), functionRange(f))) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := functionRange(f))) + val subsetAorB = have(subset(setUnion(A, B), Y)) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := Y)) - have(forall(z, z ∈ inverseImage(f, A) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ A))) by InstantiateForall(inverseImage(f, A))(inverseImage.definition of (B := A)) - val defA = thenHave(z ∈ inverseImage(f, A) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ A)) by InstantiateForall(z) - have(forall(z, z ∈ inverseImage(f, B) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ B))) by InstantiateForall(inverseImage(f, B))(inverseImage.definition) - val defB = thenHave(z ∈ inverseImage(f, B) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ B)) by InstantiateForall(z) + have(forall(z, z ∈ preimage(f, X, Y, A) <=> (z ∈ X /\ app(f, z) ∈ A))) by InstantiateForall(preimage(f, X, Y, A))(preimage.definition of (B := A)) + val defA = thenHave(z ∈ preimage(f, X, Y, A) <=> (z ∈ X /\ app(f, z) ∈ A)) by InstantiateForall(z) + have(forall(z, z ∈ preimage(f, X, Y, B) <=> (z ∈ X /\ app(f, z) ∈ B))) by InstantiateForall(preimage(f, X, Y, B))(preimage.definition) + val defB = thenHave(z ∈ preimage(f, X, Y, B) <=> (z ∈ X /\ app(f, z) ∈ B)) by InstantiateForall(z) have( - subset(setUnion(A, B), functionRange(f)) |- + subset(setUnion(A, B), Y) |- forall( z, - z ∈ inverseImage(f, setUnion(A, B)) <=> - (z ∈ functionDomain(f) /\ app(f, z) ∈ setUnion(A, B)) + z ∈ preimage(f, X, Y, setUnion(A, B)) <=> + (z ∈ X /\ app(f, z) ∈ setUnion(A, B)) ) - ) by InstantiateForall(inverseImage(f, setUnion(A, B)))(inverseImage.definition of (B := setUnion(A, B))) + ) by InstantiateForall(preimage(f, X, Y, setUnion(A, B)))(preimage.definition of (B := setUnion(A, B))) thenHave( - subset(setUnion(A, B), functionRange(f)) |- - z ∈ inverseImage(f, setUnion(A, B)) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ setUnion(A, B)) + subset(setUnion(A, B), Y) |- + z ∈ preimage(f, X, Y, setUnion(A, B)) <=> (z ∈ X /\ app(f, z) ∈ setUnion(A, B)) ) by InstantiateForall(z) val defAorB = have( - z ∈ inverseImage(f, setUnion(A, B)) <=> (z ∈ functionDomain(f) /\ app(f, z) ∈ setUnion(A, B)) + z ∈ preimage(f, X, Y, setUnion(A, B)) <=> (z ∈ X /\ app(f, z) ∈ setUnion(A, B)) ) by Tautology.from(lastStep, subsetAorB) - val forward = have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) ==> z ∈ inverseImage(f, setUnion(A, B))) subproof { - val firstPart = have(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)) |- (z ∈ inverseImage(f, A)) \/ (z ∈ inverseImage(f, B))) by Tautology.from( - setUnionMembership of (x := inverseImage(f, A), y := inverseImage(f, B)) + val forward = have(z ∈ setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B)) ==> z ∈ preimage(f, X, Y, setUnion(A, B))) subproof { + val firstPart = have(z ∈ setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B)) |- (z ∈ preimage(f, X, Y, A)) \/ (z ∈ preimage(f, X, Y, B))) by Tautology.from( + setUnionMembership of (x := preimage(f, X, Y, A), y := preimage(f, X, Y, B)) ) - assume(z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) - have(z ∈ functionDomain(f) /\ ((app(f, z) ∈ A) \/ (app(f, z) ∈ B))) by Tautology.from( + assume(z ∈ setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B))) + have(z ∈ X /\ ((app(f, z) ∈ A) \/ (app(f, z) ∈ B))) by Tautology.from( defA, defB, firstPart ) - val partialResult = have(z ∈ functionDomain(f) /\ (app(f, z) ∈ setUnion(A, B))) by Tautology.from( + val partialResult = have(z ∈ X /\ (app(f, z) ∈ setUnion(A, B))) by Tautology.from( lastStep, setUnionMembership of (x := A, y := B, z := app(f, z)) ) have(thesis) by Tautology.from(defAorB, lastStep) } - val backward = have(z ∈ inverseImage(f, setUnion(A, B)) ==> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) subproof { - assume(z ∈ inverseImage(f, setUnion(A, B))) - have((z ∈ inverseImage(f, A)) \/ (z ∈ inverseImage(f, B))) by Tautology.from( + val backward = have(z ∈ preimage(f, X, Y, setUnion(A, B)) ==> z ∈ setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B))) subproof { + assume(z ∈ preimage(f, X, Y, setUnion(A, B))) + have((z ∈ preimage(f, X, Y, A)) \/ (z ∈ preimage(f, X, Y, B))) by Tautology.from( defAorB, setUnionMembership of (x := A, y := B, z := app(f, z)), defA, @@ -264,12 +264,207 @@ object Instances extends lisa.Main { ) have(thesis) by Tautology.from( lastStep, - setUnionMembership of (x := inverseImage(f, A), y := inverseImage(f, B), z := z) + setUnionMembership of (x := preimage(f, X, Y, A), y := preimage(f, X, Y, B), z := z) ) } - have(z ∈ inverseImage(f, setUnion(A, B)) <=> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B))) by RightIff(forward, backward) - thenHave(∀(z, z ∈ inverseImage(f, setUnion(A, B)) <=> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)))) by RightForall - andThen(Substitution.applySubst(extensionalityAxiom of (x := inverseImage(f, setUnion(A, B)), y := setUnion(inverseImage(f, A), inverseImage(f, B))))) + have(z ∈ preimage(f, X, Y, setUnion(A, B)) <=> z ∈ setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B))) by RightIff(forward, backward) + thenHave(∀(z, z ∈ preimage(f, X, Y, setUnion(A, B)) <=> z ∈ setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := preimage(f, X, Y, setUnion(A, B)), y := setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B))))) + } + + // Couldn't import surjectivity from FunctionProperties without an error, so here it is + val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) + + /** + * Theorem --- if a function is [[surjective]], its range is equal to its codomain. + */ + val surjectiveImpliesRangeIsCodomain = Theorem( + surjective(f, x, y) |- (y === functionRange(f)) + ) { + have(surjective(f, x, y) |- ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f)))) by Tautology.from(surjective.definition) + val surjDef = thenHave(surjective(f, x, y) |- in(b, y) ==> ∃(a, in(pair(a, b), f))) by InstantiateForall(b) + have(∀(t, in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f))))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) + val rangeDef = thenHave(in(b, functionRange(f)) <=> (∃(a, in(pair(a, b), f)))) by InstantiateForall(b) + + have(surjective(f, x, y) |- in(b, y) ==> in(b, functionRange(f))) by Tautology.from(surjDef, rangeDef) + thenHave(surjective(f, x, y) |- ∀(b, in(b, y) ==> in(b, functionRange(f)))) by RightForall + val surjsub = andThen(Substitution.applySubst(subsetAxiom of (x -> y, y -> functionRange(f)))) + + have((surjective(f, x, y), functionFrom(f, x, y)) |- subset(y, functionRange(f)) /\ subset(functionRange(f), y)) by RightAnd(surjsub, functionImpliesRangeSubsetOfCodomain) + val funceq = andThen(Substitution.applySubst(subsetEqualitySymmetry of (x -> y, y -> functionRange(f)))) + + val surjfunc = have(surjective(f, x, y) |- functionFrom(f, x, y)) by Tautology.from(surjective.definition) + + have(thesis) by Cut(surjfunc, funceq) + } + + val preimageDifference = Theorem( + (functionFrom(f, X, Y), subset(A, Y)) + |- setDifference(X, preimage(f, X, Y, A)) === preimage(f, X, Y, setDifference(Y, A)) + ) { + assume(functionFrom(f, X, Y), subset(A, Y)) + + have(forall(t, t ∈ setDifference(Y, A) <=> (in(t, Y) /\ !in(t, A)))) by InstantiateForall(setDifference(Y, A))(setDifference.definition of (x := Y, y := A)) + val defDiffY = thenHave(z ∈ setDifference(Y, A) <=> (in(z, Y) /\ !in(z, A))) by InstantiateForall(z) + + val forward = have(x ∈ setDifference(X, preimage(f, X, Y, A)) ==> x ∈ preimage(f, X, Y, setDifference(Y, A))) subproof { + assume(x ∈ setDifference(X, preimage(f, X, Y, A))) + sorry + } + + val backward = have(x ∈ preimage(f, X, Y, setDifference(Y, A)) ==> x ∈ setDifference(X, preimage(f, X, Y, A))) subproof { + assume(x ∈ preimage(f, X, Y, setDifference(Y, A))) + sorry + } + + have(x ∈ setDifference(X, preimage(f, X, Y, A)) <=> x ∈ preimage(f, X, Y, setDifference(Y, A))) by RightIff(forward, backward) + thenHave(∀(x, x ∈ setDifference(X, preimage(f, X, Y, A)) <=> x ∈ preimage(f, X, Y, setDifference(Y, A)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A))))) + } + + val directImageEmptySet = Theorem( + (functionFrom(f, X, Y)) + |- directImage(f, X, Y, emptySet) === emptySet + ) { + assume(functionFrom(f, X, Y)) + + have(subset(emptySet, X) |- forall(z, z ∈ directImage(f, X, Y, emptySet) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ emptySet)))) by InstantiateForall(directImage(f, X, Y, emptySet))( + directImage.definition of (A := emptySet) + ) + thenHave(subset(emptySet, X) |- y ∈ directImage(f, X, Y, emptySet) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ emptySet))) by InstantiateForall(y) + val defA = have(y ∈ directImage(f, X, Y, emptySet) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ emptySet))) by Tautology.from(lastStep, emptySetIsASubset of (x := X)) + + val noElements = have(!in(y, directImage(f, X, Y, emptySet))) subproof { + assume(in(y, directImage(f, X, Y, emptySet))) + have((app(f, x) === y) /\ x ∈ emptySet |- x ∈ emptySet) by Tautology + have((app(f, x) === y) /\ x ∈ emptySet |- False) by Tautology.from(lastStep, emptySetAxiom) + thenHave(∃(x, (app(f, x) === y) /\ x ∈ emptySet) |- False) by LeftExists + have(False) by Tautology.from(lastStep, defA) + } + thenHave(∀(y, !in(y, directImage(f, X, Y, emptySet)))) by RightForall + + have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (x := directImage(f, X, Y, emptySet))) + } + + val directImageSubset = Theorem( + (functionFrom(f, X, Y), subset(A, X)) + |- directImage(f, X, Y, A) ⊆ functionRange(f) + ) { + assume(functionFrom(f, X, Y), subset(A, X)) + + have(forall(y, y ∈ relationRange(f) <=> ∃(x, in(pair(x, y), f)))) by InstantiateForall(relationRange(f))(relationRange.definition of (r := f)) + val defRange = thenHave(z ∈ relationRange(f) <=> ∃(x, in(pair(x, z), f))) by InstantiateForall(z) + + have(forall(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) + val defA = thenHave(z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) + + have(z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f)) subproof { + assume(z ∈ directImage(f, X, Y, A)) + have(∃(x, (app(f, x) === z) /\ x ∈ A)) by Tautology.from(defA) + have(x ∈ A /\ (app(f, x) === z) |- pair(x, z) ∈ f) by Tautology.from( + pairInFunctionIsApp of (a := x, b := z), + functionFromImpliesFunctional of (x := X, y := Y), + subsetTactic of (x := A, y := X, z := x), + functionFromImpliesDomainEq of (x := X, y := Y), + replaceEqualityContainsRight of (x := functionDomain(f), y := X, z := x) + ) + thenHave(x ∈ A /\ (app(f, x) === z) |- ∃(x, pair(x, z) ∈ f)) by RightExists + have(x ∈ A /\ (app(f, x) === z) |- z ∈ relationRange(f)) by Tautology.from(lastStep, defRange) + thenHave(∃(x, x ∈ A /\ (app(f, x) === z)) |- z ∈ relationRange(f)) by LeftExists + have(∃(x, x ∈ A /\ (app(f, x) === z)) |- z ∈ relationRange(f) /\ z ∈ Y) by Tautology.from( + lastStep, + functionImpliesRangeSubsetOfCodomain of (x := X, y := Y), + subsetTactic of (x := relationRange(f), y := Y) + ) + have(thesis) by Tautology.from(lastStep, defA) + } + + thenHave(forall(z, z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f))) by RightForall + have(thesis) by Tautology.from(subsetAxiom of (x := directImage(f, X, Y, A), y := functionRange(f)), lastStep) + } + + val directImageX = Theorem( + functionFrom(f, X, Y) + |- directImage(f, X, Y, X) === functionRange(f) + ) { + assume(functionFrom(f, X, Y)) + + have(subset(X, X) |- forall(z, z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X)))) by InstantiateForall(directImage(f, X, Y, X))(directImage.definition of (A := X)) + thenHave(subset(X, X) |- z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X))) by InstantiateForall(z) + val defIm = have(z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X))) by Tautology.from( + lastStep, + subsetReflexivity of (x := X) + ) + + val forward = have(z ∈ directImage(f, X, Y, X) ==> z ∈ functionRange(f)) by Tautology.from( + directImageSubset of (A := X), + subsetReflexivity of (x := X), + subsetTactic of (x := directImage(f, X, Y, X), y := functionRange(f)) + ) + + val backward = have(z ∈ functionRange(f) ==> z ∈ directImage(f, X, Y, X)) subproof { + assume(z ∈ functionRange(f)) + + have(subset(functionRange(f), Y)) by Tautology.from(functionImpliesRangeSubsetOfCodomain of (x := X, y := Y)) + val zInY = have(z ∈ Y) by Tautology.from(lastStep, subsetTactic of (x := functionRange(f), y := Y)) + + have(x ∈ functionDomain(f) /\ (app(f, x) === z) |- x ∈ X /\ (app(f, x) === z)) by Tautology.from( + functionFromImpliesDomainEq of (x := X, y := Y), + replaceEqualityContainsRight of (x := functionDomain(f), y := X, z := x) + ) + thenHave(x ∈ functionDomain(f) /\ (app(f, x) === z) |- exists(x, (app(f, x) === z) /\ x ∈ X)) by RightExists + have(x ∈ functionDomain(f) /\ (app(f, x) === z) |- z ∈ directImage(f, X, Y, X)) by Tautology.from( + lastStep, + defIm, + zInY + ) + thenHave(exists(x, x ∈ functionDomain(f) /\ (app(f, x) === z)) |- z ∈ directImage(f, X, Y, X)) by LeftExists + + have(thesis) by Tautology.from( + lastStep, + functionRangeMembership of (y := z), + functionFromImpliesFunctional of (x := X, y := Y) + ) + } + + have(z ∈ directImage(f, X, Y, X) <=> z ∈ functionRange(f)) by RightIff(forward, backward) + thenHave(∀(z, z ∈ directImage(f, X, Y, X) <=> z ∈ functionRange(f))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, X), y := functionRange(f)))) + } + + val applyDirectImage = Theorem( + A === B |- directImage(f, X, Y, A) === directImage(f, X, Y, B) + ) { + sorry + } + + val directImagePreimage = Theorem( + (functionFrom(f, X, Y), subset(A, Y)) + |- directImage(f, X, Y, preimage(f, X, Y, A)) ⊆ A + ) { + assume(functionFrom(f, X, Y), subset(A, Y)) + sorry + } + + val directImagePreimageSurjective = Theorem( + (functionFrom(f, X, Y), surjective(f, X, Y), subset(A, Y)) + |- directImage(f, X, Y, preimage(f, X, Y, A)) === A + ) { + assume(functionFrom(f, X, Y), surjective(f, X, Y), subset(A, Y)) + + val forward = have(x ∈ directImage(f, X, Y, preimage(f, X, Y, A)) ==> x ∈ A) by Tautology.from( + subsetTactic of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A, z := x), + directImagePreimage + ) + + val backward = have(x ∈ A ==> x ∈ directImage(f, X, Y, preimage(f, X, Y, A))) subproof { + assume(x ∈ A) + sorry + } + + have(x ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> x ∈ A) by RightIff(forward, backward) + thenHave(∀(x, x ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> x ∈ A)) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A))) } val indiscreteIsTopology = Theorem( @@ -484,7 +679,7 @@ object Instances extends lisa.Main { // Continuity // ------------------- val continuous = DEF(f, X, T1, Y, T2) --> - (mapping(f, X, T1, Y, T2) /\ forall(O, O ∈ T2 ==> inverseImage(f, O) ∈ T1)) + (mapping(f, X, T1, Y, T2) /\ forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) // ------------------- // Connectedness @@ -499,14 +694,101 @@ object Instances extends lisa.Main { forall(A, clopen(X, T, A) ==> ((A === emptySet) \/ (A === X))) ) - // Couldn't import surjectivity from FunctionProperties without an error, so here it is - val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) - // ------------------- // Intermediate value theorem // ------------------- val intermediateValueThm = Theorem((connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- connectedTop(Y, T2)) { - sorry + assume(connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) + val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) + val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) + + val xIsConnected = have(forall(A, clopen(X, T1, A) ==> ((A === emptySet) \/ (A === X)))) by Tautology.from(connectedTop.definition of (T := T1)) + val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) + + val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) + + have(clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y))) subproof { + assume(clopen(Y, T2, A)) + + val aIsSubset = have(A ⊆ Y) subproof { + have(A ∈ T2) by Tautology.from(clopen.definition of (X := Y, T := T2)) + have(A ∈ powerSet(Y)) by Tautology.from( + lastStep, + yIsTop, + topology.definition of (X := Y, T := T2), + setOfSubsets.definition of (X := Y, T := T2), + subsetTactic of (x := T2, y := powerSet(Y), z := A) + ) + have(thesis) by Tautology.from(lastStep, powerAxiom of (x := A, y := Y)) + } + + val preimageA = have(A ∈ T2 ==> preimage(f, X, Y, A) ∈ T1) by InstantiateForall(A)(isContinuous) + val yIsClopen = have(A ∈ T2 /\ setDifference(Y, A) ∈ T2) by Tautology.from(clopen.definition of (X := Y, T := T2)) + val part1 = have(preimage(f, X, Y, A) ∈ T1) by Tautology.from(yIsClopen, preimageA) + val preimageYA = have(setDifference(Y, A) ∈ T2 ==> preimage(f, X, Y, setDifference(Y, A)) ∈ T1) by InstantiateForall(setDifference(Y, A))(isContinuous) + have(preimage(f, X, Y, setDifference(Y, A)) ∈ T1) by Tautology.from(yIsClopen, preimageYA) + val part2 = have(setDifference(X, preimage(f, X, Y, A)) ∈ T1) by Tautology.from( + lastStep, + aIsSubset, + fIsFunction, + preimageDifference, + replaceEqualityContainsLeft of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A)), z := T1) + ) + + // So f^-1(A) is clopen + val inverseIsClopen = have(clopen(X, T1, preimage(f, X, Y, A))) by Tautology.from( + xIsTop, + part1, + part2, + clopen.definition of (T := T1, A := preimage(f, X, Y, A)) + ) + + // Hence (f^-1(A) === emptySet) \/ (preimage(f, X, Y, A) === X) by connectedness of X + have(clopen(X, T1, preimage(f, X, Y, A)) ==> (preimage(f, X, Y, A) === emptySet) \/ (preimage(f, X, Y, A) === X)) by InstantiateForall(preimage(f, X, Y, A))(xIsConnected) + val preImageIsConnected = have((preimage(f, X, Y, A) === emptySet) \/ (preimage(f, X, Y, A) === X)) by Tautology.from( + lastStep, + inverseIsClopen + ) + + // Use the fact that f(emptyset)=emptyset, f(f^-1(A)) = A (by surjectivity), f(X) = Y (by surjectivity) to conclude + val firstCase = have(preimage(f, X, Y, A) === emptySet |- A === emptySet) subproof { + assume(preimage(f, X, Y, A) === emptySet) + have(thesis) by Tautology.from( + lastStep, + fIsFunction, + aIsSubset, + applyDirectImage of (A := preimage(f, X, Y, A), B := emptySet), + directImagePreimageSurjective, + directImageEmptySet, + equalityTransitivity of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := directImage(f, X, Y, emptySet), z := emptySet), + equalitySymmetry of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A), + equalityTransitivity of (x := A, y := directImage(f, X, Y, preimage(f, X, Y, A)), z := emptySet) + ) + } + + val secondCase = have(preimage(f, X, Y, A) === X |- A === Y) subproof { + assume(preimage(f, X, Y, A) === X) + have(thesis) by Tautology.from( + lastStep, + fIsFunction, + aIsSubset, + applyDirectImage of (A := preimage(f, X, Y, A), B := X), + directImagePreimageSurjective, + directImageX, + surjectiveImpliesRangeIsCodomain of (x := X, y := Y), + equalitySymmetry of (x := Y, y := functionRange(f)), + equalityTransitivity of (x := directImage(f, X, Y, X), y := functionRange(f), z := Y), + equalityTransitivity of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := directImage(f, X, Y, X), z := Y), + equalitySymmetry of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A), + equalityTransitivity of (x := A, y := directImage(f, X, Y, preimage(f, X, Y, A)), z := Y) + ) + } + + have(thesis) by Tautology.from(preImageIsConnected, firstCase, secondCase) + } + + val allClopen = thenHave(forall(A, clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y)))) by RightForall + have(connectedTop(Y, T2)) by Tautology.from(allClopen, yIsTop, connectedTop.definition of (X := Y, T := T2)) } // ------------------- From f1c99382714878d65db5943b372c59616d795d46 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Sun, 8 Dec 2024 20:39:03 +0100 Subject: [PATCH 18/38] Framework for Heine-Borel thm --- .../scala/lisa/maths/topology/Instances.scala | 108 +++++++++++++----- 1 file changed, 82 insertions(+), 26 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 9ac5c9396..12eaad236 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -19,7 +19,7 @@ object Instances extends lisa.Main { // var defs private val x, y, z, a, b, c, t, p, f, r, s = variable private val X, T, T1, T2 = variable - private val S, A, B, Y, o, O, O2 = variable + private val S, A, B, Y, o, O, O2, O3 = variable val discreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === powerSet(X)) @@ -133,9 +133,9 @@ object Instances extends lisa.Main { |- (app(f, x) === z) /\ x ∈ setUnion(A, B) ) by Tautology.from(setUnionMembership of (x := A, y := B, z := x), defAorB) - thenHave((z ∈ Y, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by RightExists + thenHave((z ∈ Y, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by RightExists have((z ∈ Y, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B))) |- z ∈ directImage(f, X, Y, setUnion(A, B))) by Tautology.from(lastStep, defAorB) - thenHave((z ∈ Y, exists(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B)))) |- z ∈ directImage(f, X, Y, setUnion(A, B))) by LeftExists + thenHave((z ∈ Y, ∃(x, (app(f, x) === z) /\ ((x ∈ A) \/ (x ∈ B)))) |- z ∈ directImage(f, X, Y, setUnion(A, B))) by LeftExists have(z ∈ directImage(f, X, Y, setUnion(A, B))) by Tautology.from(lastStep, partialResult) } val backward = have(z ∈ directImage(f, X, Y, setUnion(A, B)) ==> z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))) subproof { @@ -146,16 +146,16 @@ object Instances extends lisa.Main { ) by Tautology.from(setUnionMembership of (x := A, y := B, z := x)) assume(z ∈ directImage(f, X, Y, setUnion(A, B))) - have(z ∈ Y /\ exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by Tautology.from(defAorB) + have(z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) by Tautology.from(defAorB) have((app(f, x) === z) /\ x ∈ A |- (app(f, x) === z) /\ x ∈ A) by Tautology val existsA = thenHave( (app(f, x) === z) /\ x ∈ A |- - exists(x, (app(f, x) === z) /\ x ∈ A) + ∃(x, (app(f, x) === z) /\ x ∈ A) ) by RightExists have((app(f, x) === z) /\ x ∈ B |- (app(f, x) === z) /\ x ∈ B) by Tautology val existsB = thenHave( (app(f, x) === z) /\ x ∈ B |- - exists(x, (app(f, x) === z) /\ x ∈ B) + ∃(x, (app(f, x) === z) /\ x ∈ B) ) by RightExists have( z ∈ Y /\ (app(f, x) === z) /\ x ∈ setUnion(A, B) |- @@ -166,7 +166,7 @@ object Instances extends lisa.Main { z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)) ) by Tautology.from(lastStep, setUnionMembership of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B), z := z)) thenHave( - (z ∈ Y, exists(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) |- + (z ∈ Y, ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) |- z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B)) ) by LeftExists have(z ∈ setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))) by Tautology.from(lastStep, defAorB) @@ -412,13 +412,13 @@ object Instances extends lisa.Main { functionFromImpliesDomainEq of (x := X, y := Y), replaceEqualityContainsRight of (x := functionDomain(f), y := X, z := x) ) - thenHave(x ∈ functionDomain(f) /\ (app(f, x) === z) |- exists(x, (app(f, x) === z) /\ x ∈ X)) by RightExists + thenHave(x ∈ functionDomain(f) /\ (app(f, x) === z) |- ∃(x, (app(f, x) === z) /\ x ∈ X)) by RightExists have(x ∈ functionDomain(f) /\ (app(f, x) === z) |- z ∈ directImage(f, X, Y, X)) by Tautology.from( lastStep, defIm, zInY ) - thenHave(exists(x, x ∈ functionDomain(f) /\ (app(f, x) === z)) |- z ∈ directImage(f, X, Y, X)) by LeftExists + thenHave(∃(x, x ∈ functionDomain(f) /\ (app(f, x) === z)) |- z ∈ directImage(f, X, Y, X)) by LeftExists have(thesis) by Tautology.from( lastStep, @@ -467,6 +467,18 @@ object Instances extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A))) } + val preimageY = Theorem( + functionFrom(f, X, Y) |- preimage(f, X, Y, Y) === X + ) { + sorry + } + + val imageSurjective = Theorem( + (functionFrom(f, X, Y), surjective(f, X, Y)) |- directImage(f, X, Y, X) === Y + ) { + sorry + } + val indiscreteIsTopology = Theorem( indiscreteTopology(X, T) ==> topology(X, T) ) { @@ -507,12 +519,12 @@ object Instances extends lisa.Main { ) have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ (z === X) /\ in(z, Y))) by Tautology.from(lastStep, emptySetAxiom of (x := a)) have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := X, z := Y)) - thenHave(exists(z, in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by LeftExists + thenHave(∃(z, in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by LeftExists val before = have(in(a, union(Y)) ==> (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, unionAxiom of (z := a, x := Y, y := z), emptySetAxiom of (x := a)) thenHave(in(a, union(Y)) ==> in(a, X)) by Tautology val base = thenHave(forall(a, in(a, union(Y)) ==> in(a, X))) by RightForall val cond1 = have(forall(a, !in(a, union(Y))) |- union(Y) === ∅) by Tautology.from(setWithNoElementsIsEmpty of (y := a, x := union(Y))) - val cond2 = have(exists(a, in(a, union(Y))) |- union(Y) === X) subproof { + val cond2 = have(∃(a, in(a, union(Y))) |- union(Y) === X) subproof { val unionGrow = have(in(a, union(Y)) |- (X ⊆ union(Y))) by Tautology.from(before, unionDoesntShrink of (x := X, y := Y)) have(in(a, union(Y)) |- (union(Y) === X)) by Tautology.from(base, unionGrow, subsetAxiom of (x := union(Y), y := X, z := a), equalityBySubset of (x := union(Y), y := X)) thenHave(thesis) by LeftExists @@ -574,32 +586,32 @@ object Instances extends lisa.Main { } val singletonSetsUniquenes = Theorem( - ∃!(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x))))) + ∃!(z, ∀(t, in(t, z) <=> ∃(x, in(x, S) /\ (t === singleton(x))))) ) { - val implicationProof = have(exists(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { sorry } - have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( + val implicationProof = have(∃(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { sorry } + have(() |- existsOne(z, forall(t, in(t, z) <=> ∃(x, in(x, S) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( union(cartesianProduct(S, S)), - lambda(t, exists(x, in(x, S) /\ (t === singleton(x)))), + lambda(t, ∃(x, in(x, S) /\ (t === singleton(x)))), implicationProof ) } - val singletonSets = DEF(S) --> The(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))(singletonSetsUniquenes) + val singletonSets = DEF(S) --> The(z, ∀(t, in(t, z) <=> ∃(x, in(x, S) /\ (t === singleton(x)))))(singletonSetsUniquenes) val singletonSetsMembershipRaw = Theorem( - in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S))) + in(t, singletonSets(S)) <=> ∃(x, ((t === singleton(x)) /\ in(x, S))) ) { - have(∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by InstantiateForall(singletonSets(S))(singletonSets.definition) + have(∀(t, in(t, singletonSets(S)) <=> ∃(x, in(x, S) /\ (t === singleton(x))))) by InstantiateForall(singletonSets(S))(singletonSets.definition) thenHave(thesis) by InstantiateForall(t) } val singletonSetsMembership = Theorem( in(x, S) <=> in(singleton(x), singletonSets(S)) ) { - val memb = have(in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S)))) by Tautology.from(singletonSetsMembershipRaw) + val memb = have(in(t, singletonSets(S)) <=> ∃(x, ((t === singleton(x)) /\ in(x, S)))) by Tautology.from(singletonSetsMembershipRaw) have(in(x, S) |- in(singleton(x), singletonSets(S))) subproof { assume(in(x, S)) have(t === singleton(x) |- ((t === singleton(x)) /\ in(x, S))) by Tautology - thenHave(t === singleton(x) |- exists(x, ((t === singleton(x)) /\ in(x, S)))) by RightExists + thenHave(t === singleton(x) |- ∃(x, ((t === singleton(x)) /\ in(x, S)))) by RightExists sorry /*have((t === singleton(x)) ==> in(t, singletonSets(S))) by Tautology.from(lastStep, memb) thenHave(forall(t, (t === singleton(x)) ==> in(t, singletonSets(S)))) by RightForall @@ -609,22 +621,22 @@ object Instances extends lisa.Main { have(in(singleton(x), singletonSets(S)) |- in(x, S)) subproof { assume(in(singleton(x), singletonSets(S))) - val removeExists = have((exists(y, in(y, S) /\ (t === singleton(y))), t === singleton(x)) |- in(x, S)) subproof { + val removeExists = have((∃(y, in(y, S) /\ (t === singleton(y))), t === singleton(x)) |- in(x, S)) subproof { /*have((in(y, S), t === singleton(x), t === singleton(y)) |- (in(y, S), t === singleton(x), t === singleton(y))) thenHave((in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by Tautology.from( singletonExtensionality, equalityTransitivity of (x := singleton(x), y := t, z := singleton(y)), replaceEqualityContainsLeft of (z := S) ) - thenHave(exists(y, in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by LeftExists - have(exists(y, in(y, S) /\ (t === singleton(y))) /\ (t === singleton(x)) |- (in(x, S))) by Tautology.from( + thenHave(∃(y, in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by LeftExists + have(∃(y, in(y, S) /\ (t === singleton(y))) /\ (t === singleton(x)) |- (in(x, S))) by Tautology.from( lastStep, existentialConjunctionWithClosedFormula of (x := y, p := (t === singleton(x))) ) thenHave(thesis) by Tautology*/ sorry } - have((t === singleton(x), in(t, singletonSets(S))) |- (t === singleton(x), exists(x, ((t === singleton(x)) /\ in(x, S))))) by Tautology.from(singletonSetsMembershipRaw of (x := y)) + have((t === singleton(x), in(t, singletonSets(S))) |- (t === singleton(x), ∃(x, ((t === singleton(x)) /\ in(x, S))))) by Tautology.from(singletonSetsMembershipRaw of (x := y)) sorry /* have((t === singleton(x), in(t, singletonSets(S))) |- in(x, S)) by Tautology.from(lastStep, removeExists) have(in(singleton(x), singletonSets(S)) |- in(x, S)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := t, y := singleton(x), z := singletonSets(S)))*/ @@ -804,12 +816,12 @@ object Instances extends lisa.Main { val finite = DEF(X) --> (X === emptySet) // TODO - val compactness = DEF(X, T) --> + val compact = DEF(X, T) --> topology(X, T) /\ forall( O, openCover(X, T, O) ==> - exists( + ∃( O2, // Another subcovering subset(O2, O) /\ openCover(X, T, O2) /\ @@ -817,4 +829,48 @@ object Instances extends lisa.Main { ) ) + val heineBorelThm = Theorem((compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- compact(Y, T2)) { + assume(compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) + + val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) + val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) + + val xIsCompact = have(forall(O, openCover(X, T1, O) ==> ∃(O2, subset(O2, O) /\ openCover(X, T1, O2) /\ finite(O2)))) by Tautology.from( + compact.definition of (T := T1) + ) + val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) + + val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) + + have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { + assume(openCover(Y, T2, O)) + + // TODO: change O is not the correct subcover for X, it should be unionPreimage(O) + // We have an open cover of X + val isOpenCover = have(openCover(X, T1, O)) subproof { + sorry + } + + // Whence the existence of a finite subcover O3 + // TODO: change O is not the correct cover for X, it should be unionPreimage(O) (to be defined) + have(openCover(X, T1, O) ==> ∃(O3, subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3))) by InstantiateForall(O)(xIsCompact) + val existsO3 = have(∃(O3, subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) + + // From that finite subcover O3, we can recover a finite subcover O2 of Y + // TODO: change O is not the correct cover for X, it should be unionPreimage(O) (to be defined) + have(subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { + assume(subset(O3, O), openCover(X, T1, O3), finite(O3)) + sorry + } + + // Concluding + // TODO: change O is not the correct cover for X, it should be unionPreimage(O) (to be defined) + thenHave(∃(O3, subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by LeftExists + have(thesis) by Tautology.from(lastStep, existsO3) + } + thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by Tautology + val yIsCompact = thenHave(forall(O, openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2)))) by RightForall + + have(thesis) by Tautology.from(yIsCompact, yIsTop, compact.definition of (X := Y, T := T2)) + } } From c60719f9cbe61fb26d7503d60437b9277d115525 Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Thu, 12 Dec 2024 15:11:13 +0100 Subject: [PATCH 19/38] added intersection lemma --- .../maths/settheory/SetTheoryBasics.scala | 14 + .../scala/lisa/maths/topology/Instances.scala | 372 ++++++------------ .../lisa/maths/topology/Intersection.scala | 77 +++- .../lisa/maths/topology/SingletonSet.scala | 224 +++++++++++ 4 files changed, 424 insertions(+), 263 deletions(-) create mode 100644 lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala index 0e1849f24..127dc3199 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -100,6 +100,20 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := setIntersection(x, y), y := z)) } + val subsetUnderIntersection = Theorem((z ⊆ (x ∩ y)) |- (z ⊆ x /\ z ⊆ y)) { + assume(z ⊆ (x ∩ y)) + have(forall(t, in(t, z) ==> in(t, x ∩ y))) by Tautology.from(subsetAxiom of (x := z, y := x ∩ y, z := t)) + thenHave(in(t, z) ==> in(t, x ∩ y)) by InstantiateForall(t) + val both = have((in(t, z) ==> in(t, x)) /\ (in(t, z) ==> in(t, y))) by Tautology.from(lastStep, setIntersectionMembership of (x := x, y := y)) + have(in(t, z) ==> in(t, x)) by Weakening(both) + thenHave(forall(t, in(t, z) ==> in(t, x))) by RightForall + val first = have(z ⊆ x) by Tautology.from(lastStep, subsetAxiom of (x := z, y := x, z := t)) + have(in(t, z) ==> in(t, y)) by Weakening(both) + thenHave(forall(t, in(t, z) ==> in(t, y))) by RightForall + val second = have(z ⊆ y) by Tautology.from(lastStep, subsetAxiom of (x := z, y := y, z := t)) + have(thesis) by Tautology.from(first, second) + } + val subsetClosedSetUnion = Theorem((x ⊆ z, y ⊆ z) |- setUnion(x, y) ⊆ z) { assume(x ⊆ z, y ⊆ z) have(forall(t, in(t, x) ==> in(t, z))) by Tautology.from(subsetAxiom of (y := z, z := t)) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 942b31fe6..f9368711a 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -21,8 +21,10 @@ object Instances extends lisa.Main { private val X, T = variable private val S, A, B, Y = variable + // A discrete Topology is is where T contains all possible subsets of X from Def 1.1.6 in the book val discreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === powerSet(X)) + // Proof that a discrite Topology is actually a topology (satisfies all conditions of a topology) val discreteIsTopology = Theorem( discreteTopology(X, T) |- topology(X, T) ) { @@ -68,8 +70,116 @@ object Instances extends lisa.Main { have(thesis) by Tautology.from(discreteDef, isSub, contEx, contUn, contInt, topology.definition) } + // A indiscrete Topology is is where T contains only the empty set and X. Therefore the smallest possible topology for a given X. From Def 1.1.6 in the book val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === unorderedPair(∅, X)) + // proof that the indiscrete Topology is acutally a topology + val indiscreteIsTopology = Theorem( + indiscreteTopology(X, T) ==> topology(X, T) + ) { + assume(indiscreteTopology(X, T)) + val indiscreteDef = have(nonEmpty(X) /\ (T === unorderedPair(∅, X))) by Tautology.from(indiscreteTopology.definition) + val emptySubs = have(∅ ∈ powerSet(X)) by Tautology.from(emptySetIsASubset of (x := X), powerAxiom of (x := emptySet, y := X)) + val fullSubs = have(X ∈ powerSet(X)) by Tautology.from(elemInItsPowerSet of (x := X)) + + val isSub = have(setOfSubsets(X, T)) subproof { + have(in(unorderedPair(∅, X), powerSet(powerSet(X)))) by Tautology.from(emptySubs, fullSubs, unorderedPairInPowerSet of (x := powerSet(X), a := emptySet, b := X)) + have(unorderedPair(∅, X) ⊆ powerSet(X)) by Tautology.from(lastStep, powerAxiom of (x := unorderedPair(∅, X), y := powerSet(X))) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualitySubsetLeft of (x := unorderedPair(∅, X), y := T, z := powerSet(X)), setOfSubsets.definition) + } + + val contEx = have(containsExtremes(X, T)) subproof { + val a = have(X ∈ T) by Tautology.from(pairAxiom of (x := emptySet, y := X, z := X), indiscreteDef, replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := X)) + val b = have(∅ ∈ T) by Tautology.from( + pairAxiom of (x := emptySet, y := X, z := emptySet), + indiscreteDef, + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := emptySet) + ) + have(thesis) by Tautology.from(a, b, containsExtremes.definition) + } + + val contUn = have(containsUnion(T)) subproof { + have((Y ⊆ T) |- union(Y) ∈ T) subproof { + assume(Y ⊆ T) + have(Y ⊆ unorderedPair(∅, X)) by Tautology.from(indiscreteDef, replaceEqualitySubsetRight of (x := T, y := unorderedPair(∅, X), z := Y)) + have(forall(z, in(z, Y) ==> in(z, unorderedPair(∅, X)))) by Tautology.from(lastStep, subsetAxiom of (x := Y, y := unorderedPair(∅, X))) + thenHave(in(z, Y) ==> in(z, unorderedPair(∅, X))) by InstantiateForall(z) + have(in(z, Y) |- ((z === ∅) \/ (z === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X)) + thenHave((in(z, Y) /\ in(a, z)) |- (((z === ∅) \/ (z === X)) /\ in(a, z))) by Tautology + thenHave((in(z, Y) /\ in(a, z)) |- ((((z === ∅) /\ in(a, z))) \/ ((z === X) /\ in(a, z)))) by Tautology + have((in(z, Y) /\ in(a, z)) |- (in(a, ∅) \/ (in(a, X) /\ (z === X)))) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := z, y := emptySet, z := a), + replaceEqualityContainsRight of (x := z, y := X, z := a) + ) + have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ (z === X) /\ in(z, Y))) by Tautology.from(lastStep, emptySetAxiom of (x := a)) + have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := X, z := Y)) + thenHave(exists(z, in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by LeftExists + val before = have(in(a, union(Y)) ==> (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, unionAxiom of (z := a, x := Y, y := z), emptySetAxiom of (x := a)) + thenHave(in(a, union(Y)) ==> in(a, X)) by Tautology + val base = thenHave(forall(a, in(a, union(Y)) ==> in(a, X))) by RightForall + val cond1 = have(forall(a, !in(a, union(Y))) |- union(Y) === ∅) by Tautology.from(setWithNoElementsIsEmpty of (y := a, x := union(Y))) + val cond2 = have(exists(a, in(a, union(Y))) |- union(Y) === X) subproof { + val unionGrow = have(in(a, union(Y)) |- (X ⊆ union(Y))) by Tautology.from(before, unionDoesntShrink of (x := X, y := Y)) + have(in(a, union(Y)) |- (union(Y) === X)) by Tautology.from(base, unionGrow, subsetAxiom of (x := union(Y), y := X, z := a), equalityBySubset of (x := union(Y), y := X)) + thenHave(thesis) by LeftExists + } + have((union(Y) === ∅) \/ (union(Y) === X)) by Tautology.from(base, cond1, cond2) + have(union(Y) ∈ unorderedPair(∅, X)) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := union(Y))) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := union(Y))) + } + thenHave((Y ⊆ T) ==> (union(Y) ∈ T)) by Tautology + thenHave(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsUnion.definition) + } + + val contInt = have(containsIntersection(T)) subproof { + have((A ∈ T /\ B ∈ T) |- (A ∩ B ∈ T)) subproof { + assume((A ∈ T /\ B ∈ T)) + have(A ∈ unorderedPair(∅, X) /\ B ∈ unorderedPair(∅, X)) by Tautology.from( + indiscreteDef, + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := A), + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := B) + ) + val allPossibilities = + have(((A === ∅) \/ (A === X)) /\ ((B === ∅) \/ (B === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := A), pairAxiom of (x := emptySet, y := X, z := B)) + val aEmpty = have((A === ∅) |- (A ∩ B) === ∅) subproof { + assume(A === emptySet) + have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) + have(!in(t, setIntersection(A, B))) by Tautology.from(lastStep, replaceEqualityContainsRight of (x := emptySet, y := A, z := t), emptySetAxiom of (x := t)) + thenHave(forall(t, !in(t, setIntersection(A, B)))) by RightForall + have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (y := t, x := setIntersection(A, B))) + } + val oneEmpty = have(((A === ∅) \/ (B === ∅)) |- ((A ∩ B) === ∅)) by Tautology.from( + aEmpty, + aEmpty of (A := B, B := A), + setIntersectionCommutativity of (x := A, y := B), + equalityTransitivity of (x := setIntersection(A, B), y := setIntersection(B, A), z := emptySet) + ) + val bothFull = have((A === X, B === X) |- A ∩ B === X) subproof { + assume(((A === X) /\ (B === X))) + have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) + have(in(t, setIntersection(A, B)) <=> in(t, X)) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := X, y := A, z := t), + replaceEqualityContainsRight of (x := X, y := B, z := t) + ) + thenHave(forall(t, in(t, setIntersection(A, B)) <=> in(t, X))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := setIntersection(A, B), y := X, z := t)) + } + have((A ∩ B === ∅) \/ (A ∩ B === X)) by Tautology.from(allPossibilities, oneEmpty, bothFull) + have(in(A ∩ B, unorderedPair(∅, X))) by Tautology.from(lastStep, pairAxiom of (z := setIntersection(A, B), x := emptySet, y := X)) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := setIntersection(A, B))) + } + thenHave((A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T) by Tautology + thenHave(forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T)) by RightForall + thenHave(forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsIntersection.definition) + } + + have(thesis) by Tautology.from(indiscreteDef, isSub, contEx, contUn, contInt, topology.definition) + } + inline def directImageFormula = y ∈ s <=> (y ∈ functionRange(f) /\ ∃(x, (app(f, x) === y) /\ x ∈ A)) val directImageUniqueness = Theorem( @@ -241,266 +351,4 @@ object Instances extends lisa.Main { thenHave(∀(z, z ∈ inverseImage(f, setUnion(A, B)) <=> z ∈ setUnion(inverseImage(f, A), inverseImage(f, B)))) by RightForall andThen(Substitution.applySubst(extensionalityAxiom of (x := inverseImage(f, setUnion(A, B)), y := setUnion(inverseImage(f, A), inverseImage(f, B))))) } - - val indiscreteIsTopology = Theorem( - indiscreteTopology(X, T) ==> topology(X, T) - ) { - assume(indiscreteTopology(X, T)) - val indiscreteDef = have(nonEmpty(X) /\ (T === unorderedPair(∅, X))) by Tautology.from(indiscreteTopology.definition) - val emptySubs = have(∅ ∈ powerSet(X)) by Tautology.from(emptySetIsASubset of (x := X), powerAxiom of (x := emptySet, y := X)) - val fullSubs = have(X ∈ powerSet(X)) by Tautology.from(elemInItsPowerSet of (x := X)) - - val isSub = have(setOfSubsets(X, T)) subproof { - have(in(unorderedPair(∅, X), powerSet(powerSet(X)))) by Tautology.from(emptySubs, fullSubs, unorderedPairInPowerSet of (x := powerSet(X), a := emptySet, b := X)) - have(unorderedPair(∅, X) ⊆ powerSet(X)) by Tautology.from(lastStep, powerAxiom of (x := unorderedPair(∅, X), y := powerSet(X))) - have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualitySubsetLeft of (x := unorderedPair(∅, X), y := T, z := powerSet(X)), setOfSubsets.definition) - } - - val contEx = have(containsExtremes(X, T)) subproof { - val a = have(X ∈ T) by Tautology.from(pairAxiom of (x := emptySet, y := X, z := X), indiscreteDef, replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := X)) - val b = have(∅ ∈ T) by Tautology.from( - pairAxiom of (x := emptySet, y := X, z := emptySet), - indiscreteDef, - replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := emptySet) - ) - have(thesis) by Tautology.from(a, b, containsExtremes.definition) - } - - val contUn = have(containsUnion(T)) subproof { - have((Y ⊆ T) |- union(Y) ∈ T) subproof { - assume(Y ⊆ T) - have(Y ⊆ unorderedPair(∅, X)) by Tautology.from(indiscreteDef, replaceEqualitySubsetRight of (x := T, y := unorderedPair(∅, X), z := Y)) - have(forall(z, in(z, Y) ==> in(z, unorderedPair(∅, X)))) by Tautology.from(lastStep, subsetAxiom of (x := Y, y := unorderedPair(∅, X))) - thenHave(in(z, Y) ==> in(z, unorderedPair(∅, X))) by InstantiateForall(z) - have(in(z, Y) |- ((z === ∅) \/ (z === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X)) - thenHave((in(z, Y) /\ in(a, z)) |- (((z === ∅) \/ (z === X)) /\ in(a, z))) by Tautology - thenHave((in(z, Y) /\ in(a, z)) |- ((((z === ∅) /\ in(a, z))) \/ ((z === X) /\ in(a, z)))) by Tautology - have((in(z, Y) /\ in(a, z)) |- (in(a, ∅) \/ (in(a, X) /\ (z === X)))) by Tautology.from( - lastStep, - replaceEqualityContainsRight of (x := z, y := emptySet, z := a), - replaceEqualityContainsRight of (x := z, y := X, z := a) - ) - have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ (z === X) /\ in(z, Y))) by Tautology.from(lastStep, emptySetAxiom of (x := a)) - have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := X, z := Y)) - thenHave(exists(z, in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by LeftExists - val before = have(in(a, union(Y)) ==> (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, unionAxiom of (z := a, x := Y, y := z), emptySetAxiom of (x := a)) - thenHave(in(a, union(Y)) ==> in(a, X)) by Tautology - val base = thenHave(forall(a, in(a, union(Y)) ==> in(a, X))) by RightForall - val cond1 = have(forall(a, !in(a, union(Y))) |- union(Y) === ∅) by Tautology.from(setWithNoElementsIsEmpty of (y := a, x := union(Y))) - val cond2 = have(exists(a, in(a, union(Y))) |- union(Y) === X) subproof { - val unionGrow = have(in(a, union(Y)) |- (X ⊆ union(Y))) by Tautology.from(before, unionDoesntShrink of (x := X, y := Y)) - have(in(a, union(Y)) |- (union(Y) === X)) by Tautology.from(base, unionGrow, subsetAxiom of (x := union(Y), y := X, z := a), equalityBySubset of (x := union(Y), y := X)) - thenHave(thesis) by LeftExists - } - have((union(Y) === ∅) \/ (union(Y) === X)) by Tautology.from(base, cond1, cond2) - have(union(Y) ∈ unorderedPair(∅, X)) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := union(Y))) - have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := union(Y))) - } - thenHave((Y ⊆ T) ==> (union(Y) ∈ T)) by Tautology - thenHave(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by RightForall - have(thesis) by Tautology.from(lastStep, containsUnion.definition) - } - - val contInt = have(containsIntersection(T)) subproof { - have((A ∈ T /\ B ∈ T) |- (A ∩ B ∈ T)) subproof { - assume((A ∈ T /\ B ∈ T)) - have(A ∈ unorderedPair(∅, X) /\ B ∈ unorderedPair(∅, X)) by Tautology.from( - indiscreteDef, - replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := A), - replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := B) - ) - val allPossibilities = - have(((A === ∅) \/ (A === X)) /\ ((B === ∅) \/ (B === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := A), pairAxiom of (x := emptySet, y := X, z := B)) - val aEmpty = have((A === ∅) |- (A ∩ B) === ∅) subproof { - assume(A === emptySet) - have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) - have(!in(t, setIntersection(A, B))) by Tautology.from(lastStep, replaceEqualityContainsRight of (x := emptySet, y := A, z := t), emptySetAxiom of (x := t)) - thenHave(forall(t, !in(t, setIntersection(A, B)))) by RightForall - have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (y := t, x := setIntersection(A, B))) - } - val oneEmpty = have(((A === ∅) \/ (B === ∅)) |- ((A ∩ B) === ∅)) by Tautology.from( - aEmpty, - aEmpty of (A := B, B := A), - setIntersectionCommutativity of (x := A, y := B), - equalityTransitivity of (x := setIntersection(A, B), y := setIntersection(B, A), z := emptySet) - ) - val bothFull = have((A === X, B === X) |- A ∩ B === X) subproof { - assume(((A === X) /\ (B === X))) - have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) - have(in(t, setIntersection(A, B)) <=> in(t, X)) by Tautology.from( - lastStep, - replaceEqualityContainsRight of (x := X, y := A, z := t), - replaceEqualityContainsRight of (x := X, y := B, z := t) - ) - thenHave(forall(t, in(t, setIntersection(A, B)) <=> in(t, X))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := setIntersection(A, B), y := X, z := t)) - } - have((A ∩ B === ∅) \/ (A ∩ B === X)) by Tautology.from(allPossibilities, oneEmpty, bothFull) - have(in(A ∩ B, unorderedPair(∅, X))) by Tautology.from(lastStep, pairAxiom of (z := setIntersection(A, B), x := emptySet, y := X)) - have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := setIntersection(A, B))) - } - thenHave((A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T) by Tautology - thenHave(forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T)) by RightForall - thenHave(forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T))) by RightForall - have(thesis) by Tautology.from(lastStep, containsIntersection.definition) - } - - have(thesis) by Tautology.from(indiscreteDef, isSub, contEx, contUn, contInt, topology.definition) - } - - val singletonSetsUniquenes = Theorem( - ∃!(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x))))) - ) { - val implicationProof = have(exists(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { - - val elementInSingleton = have(in(x, singleton(x))) subproof { - have(thesis) by Tautology.from(pairAxiom of (x := x, y := x, z := x)) - } - - val singletonInPowerSet = have(in(x, S) |- in(singleton(x), powerSet(S))) subproof { - assume(in(x, S)) - val introduceY = thenHave((x === y) |- in(y, S)) by Substitution.ApplyRules(x === y) - val useSingleton = have(in(y, singleton(x)) |- in(y, S)) by Tautology.from(introduceY, singletonHasNoExtraElements) - thenHave(() |- (!(in(y, singleton(x))), in(y, S))) by RightNot - thenHave(() |- in(y, singleton(x)) ==> in(y, S)) by Tautology - val universalY = thenHave(() |- forall(y, in(y, singleton(x)) ==> in(y, S))) by RightForall - val singletonIsSubset = have(() |- singleton(x) ⊆ S) by Tautology.from(universalY, subsetAxiom of (z := y, y := S, x := singleton(x))) - have(thesis) by Tautology.from(singletonIsSubset, powerAxiom of (x := singleton(x), y := S)) - } - - val abExist = have((singleton(t) === pair(x, x)) /\ in(x, S) |- ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) subproof { - have((singleton(t) === pair(x, x)) /\ in(x, S) |- (singleton(t) === pair(x, x)) /\ in(x, S) /\ in(x, S)) by Restate - thenHave((singleton(t) === pair(x, x)) /\ in(x, S) |- exists(b, (singleton(t) === pair(x, b)) /\ in(x, S) /\ in(b, S))) by RightExists - thenHave((singleton(t) === pair(x, x)) /\ in(x, S) |- exists(a, exists(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) by RightExists - } - - val cartesianInstantiated = have(in(singleton(t), cartesianProduct(S, S)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, S)))) - /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S))))) subproof { - val instance = have((cartesianProduct(x, y) === cartesianProduct(x, y)) <=> ∀(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) - /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by InstantiateForall(cartesianProduct(x, y))(cartesianProduct.definition) - thenHave(∀(t, in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) - /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by Tautology - thenHave(in(singleton(t), cartesianProduct(x, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(x, y)))) - /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, x) /\ in(b, y))))) by InstantiateForall(singleton(t)) - thenHave(forall(x, in(singleton(t), cartesianProduct(x, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(x, y)))) - /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, x) /\ in(b, y)))))) by RightForall - thenHave(in(singleton(t), cartesianProduct(S, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, y)))) - /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, y))))) by InstantiateForall(S) - thenHave(forall(y, in(singleton(t), cartesianProduct(S, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, y)))) - /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, y)))))) by RightForall - thenHave(in(singleton(t), cartesianProduct(S, S)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, S)))) - /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S))))) by InstantiateForall(S) - } - - val init = have(in(x, S) |- in(x, S)) by Restate - val inSS = have(in(x, S) |- in(x, setUnion(S, S))) by Tautology.from(init, setUnionMembership of (z := x, x := S, y := S)) - val step1 = have(in(x, S) |- in(singleton(x), powerSet(setUnion(S, S))) /\ (singleton(singleton(x)) === pair(x, x))) by - Tautology.from(inSS, singletonInPowerSet of (S := setUnion(S, S))) - thenHave((in(x, S), t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by - Substitution.ApplyRules(singleton(x) === t) - val introduceT = thenHave(in(x, S) /\ (t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by Tautology - val step2 = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ (singleton(t) === pair(x, x))) by - Tautology.from(introduceT, singletonInPowerSet of (x := t, S := powerSet(setUnion(S, S)))) - val extendRHS = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) - /\ ((singleton(t) === pair(x, x)) /\ in(x, S))) by Tautology.from(step2, elementInSingleton of (x := t)) - val existAB = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) - /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) by Tautology.from(extendRHS, abExist) - val cartesian = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), cartesianProduct(S, S)) /\ in(t, singleton(t))) by - Tautology.from(existAB, cartesianInstantiated) - val introduceZ = thenHave(in(x, S) /\ (t === singleton(x)) |- exists(z, in(z, cartesianProduct(S, S)) /\ in(t, z))) by RightExists - val unionOfProduct = have((in(x, S) /\ (t === singleton(x))) |- in(t, union(cartesianProduct(S, S)))) by - Tautology.from(introduceZ, unionAxiom of (y := z, x := cartesianProduct(S, S), z := t)) - thenHave(exists(x, (in(x, S) /\ (t === singleton(x)))) |- in(t, union(cartesianProduct(S, S)))) by LeftExists - } - have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( - union(cartesianProduct(S, S)), - lambda(t, exists(x, in(x, S) /\ (t === singleton(x)))), - implicationProof - ) - } - val singletonSets = DEF(S) --> The(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))(singletonSetsUniquenes) - - val singletonSetsMembershipRaw = Theorem( - in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S))) - ) { - have(∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by InstantiateForall(singletonSets(S))(singletonSets.definition) - thenHave(thesis) by InstantiateForall(t) - } - - val singletonSetsMembership = Theorem( - in(x, S) <=> in(singleton(x), singletonSets(S)) - ) { - val memb = have(in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S)))) by Tautology.from(singletonSetsMembershipRaw) - have(in(x, S) |- in(singleton(x), singletonSets(S))) subproof { - assume(in(x, S)) - have(t === singleton(x) |- ((t === singleton(x)) /\ in(x, S))) by Tautology - thenHave(t === singleton(x) |- exists(x, ((t === singleton(x)) /\ in(x, S)))) by RightExists - sorry - /*have((t === singleton(x)) ==> in(t, singletonSets(S))) by Tautology.from(lastStep, memb) - thenHave(forall(t, (t === singleton(x)) ==> in(t, singletonSets(S)))) by RightForall - thenHave((singleton(x) === singleton(x)) ==> in(singleton(x), singletonSets(S))) by InstantiateForall(singleton(x)) - have(thesis) by Tautology.from(lastStep)*/ - } - have(in(singleton(x), singletonSets(S)) |- in(x, S)) subproof { - assume(in(singleton(x), singletonSets(S))) - - val removeExists = have((exists(y, in(y, S) /\ (t === singleton(y))), t === singleton(x)) |- in(x, S)) subproof { - /*have((in(y, S), t === singleton(x), t === singleton(y)) |- (in(y, S), t === singleton(x), t === singleton(y))) - thenHave((in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by Tautology.from( - singletonExtensionality, - equalityTransitivity of (x := singleton(x), y := t, z := singleton(y)), - replaceEqualityContainsLeft of (z := S) - ) - thenHave(exists(y, in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by LeftExists - have(exists(y, in(y, S) /\ (t === singleton(y))) /\ (t === singleton(x)) |- (in(x, S))) by Tautology.from( - lastStep, - existentialConjunctionWithClosedFormula of (x := y, p := (t === singleton(x))) - ) - thenHave(thesis) by Tautology*/ - sorry - } - have((t === singleton(x), in(t, singletonSets(S))) |- (t === singleton(x), exists(x, ((t === singleton(x)) /\ in(x, S))))) by Tautology.from(singletonSetsMembershipRaw of (x := y)) - sorry - /* have((t === singleton(x), in(t, singletonSets(S))) |- in(x, S)) by Tautology.from(lastStep, removeExists) - have(in(singleton(x), singletonSets(S)) |- in(x, S)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := t, y := singleton(x), z := singletonSets(S)))*/ - } - sorry - } - - val ifContainsSingletonIsDiscrete = Theorem( - (topology(X, T), ∀(x, x ∈ X ==> singleton(x) ∈ T)) |- discreteTopology(X, T) - ) { - assume(∀(x, x ∈ X ==> singleton(x) ∈ T), topology(X, T)) - val topo = have(nonEmpty(X) /\ setOfSubsets(X, T) /\ containsExtremes(X, T) /\ containsUnion(T) /\ containsIntersection(T)) by Tautology.from(topology.definition) - have(∀(x, x ∈ X ==> singleton(x) ∈ T)) by Tautology - val singleDef = thenHave((x ∈ X) ==> (singleton(x) ∈ T)) by InstantiateForall(x) - have(T === powerSet(X)) subproof { - // show T subs powerSet(X) (by def of topology) - val left = have(T ⊆ powerSet(X)) by Tautology.from(topo, setOfSubsets.definition) - // show powerSet(X) subs T - - // For any S ⊆ X we have S = U{x} -> S ∈ T by unionDef - have((S ⊆ X) |- S ∈ T) subproof { - assume(S ⊆ X) - // prove union(cartesianProduct(S, S)) ⊆ T - // -> S = union(union(cartesianProduct(S, S))) in T - have(forall(z, in(z, S) ==> in(z, X))) by Tautology.from(subsetAxiom of (x := S, y := X)) - thenHave(in(z, S) ==> in(z, X)) by InstantiateForall(z) - have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(lastStep, singleDef of (x := z)) - // have(in(z, S) /\ forall(a, in(z, S) <=> in(singleton(z), V)) |- in(singleton(z), V)) - // have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(sorry) - // have(in(singleton(z), singleton(S)) ==> in(singleton(z), T)) - // have(singleton(S) ⊆ T) - // have(union(singleton(S)) ∈ T) - // have(S ∈ T) - sorry - } - have(in(S, powerSet(X)) ==> in(S, T)) by Tautology.from(lastStep, powerAxiom of (x := S, y := X)) - thenHave(forall(S, in(S, powerSet(X)) ==> in(S, T))) by RightForall - val right = have(powerSet(X) ⊆ T) by Tautology.from(lastStep, subsetAxiom of (x := powerSet(X), y := T, z := S)) - - have(thesis) by Tautology.from(left, right, equalityBySubset of (x := powerSet(X), y := T)) - } - have(discreteTopology(X, T)) by Tautology.from(lastStep, topo, discreteTopology.definition) - } } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala index abb6503ef..dd8a348a2 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Intersection.scala @@ -13,7 +13,82 @@ import lisa.maths.settheory.SetTheoryBasics.* object Intersection extends lisa.Main { // var defs private val x, y, z, a, b, c, t, p = variable - private val X, T = variable + private val X, T, T1, T2 = variable private val S, A, B, Y = variable + // Proof that the intersection over two topologies defined over the same set X is also a topology + val intersectionIsTopology = Theorem( + (topology(X, T1) /\ topology(X, T2)) |- topology(X, T1 ∩ T2) + ) { + assume(topology(X, T1) /\ topology(X, T2)) + val topo1 = have(nonEmpty(X) /\ setOfSubsets(X, T1) /\ containsExtremes(X, T1) /\ containsUnion(T1) /\ containsIntersection(T1)) by Tautology.from(topology.definition of (T := T1)) + val topo2 = have(nonEmpty(X) /\ setOfSubsets(X, T2) /\ containsExtremes(X, T2) /\ containsUnion(T2) /\ containsIntersection(T2)) by Tautology.from(topology.definition of (T := T2)) + + val nonEm = have(nonEmpty(X)) by Tautology.from(topo1) + + val isSub = have(setOfSubsets(X, T1 ∩ T2)) by Tautology.from( + subsetClosedIntersection of (x := T1, y := T2, z := powerSet(X)), + topo1, + topo2, + setOfSubsets.definition of (T := T1 ∩ T2), + setOfSubsets.definition of (T := T1), + setOfSubsets.definition of (T := T2) + ) + val contEx = have(containsExtremes(X, T1 ∩ T2)) subproof { + val empty = have(∅ ∈ (T1 ∩ T2)) by Tautology.from( + setIntersectionMembership of (t := emptySet, x := T1, y := T2), + containsExtremes.definition of (T := T1), + containsExtremes.definition of (T := T2), + topo1, + topo2 + ) + val full = have(X ∈ (T1 ∩ T2)) by Tautology.from( + setIntersectionMembership of (t := X, x := T1, y := T2), + containsExtremes.definition of (T := T1), + containsExtremes.definition of (T := T2), + topo1, + topo2 + ) + have(thesis) by Tautology.from(empty, full, containsExtremes.definition of (T := T1 ∩ T2)) + } + val contUn = have(containsUnion(T1 ∩ T2)) subproof { + have(forall(Y, (Y ⊆ T1) ==> (union(Y) ∈ T1))) by Tautology.from(topo1, containsUnion.definition of (T := T1)) + val t1 = thenHave((Y ⊆ T1) ==> (union(Y) ∈ T1)) by InstantiateForall(Y) + have(forall(Y, (Y ⊆ T2) ==> (union(Y) ∈ T2))) by Tautology.from(topo2, containsUnion.definition of (T := T2)) + val t2 = thenHave((Y ⊆ T2) ==> (union(Y) ∈ T2)) by InstantiateForall(Y) + have((Y ⊆ (T1 ∩ T2)) ==> (union(Y) ∈ (T1 ∩ T2))) subproof { + assume(Y ⊆ (T1 ∩ T2)) + val leadsUnion = have((containsUnion(T), Y ⊆ T) |- union(Y) ∈ T) subproof { + assume(containsUnion(T)) + have(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by Tautology.from(containsUnion.definition) + thenHave((Y ⊆ T) ==> (union(Y) ∈ T)) by InstantiateForall(Y) + thenHave(thesis) by Tautology + } + have((Y ⊆ T1) /\ (Y ⊆ T2)) by Tautology.from(subsetUnderIntersection of (x := T1, y := T2, z := Y)) + have(union(Y) ∈ T1 /\ union(Y) ∈ T2) by Tautology.from(leadsUnion of (T := T1), leadsUnion of (T := T2), lastStep, topo1, topo2) + have(thesis) by Tautology.from(lastStep, setIntersectionMembership of (x := T1, y := T2, t := union(Y))) + } + thenHave(forall(Y, (Y ⊆ (T1 ∩ T2)) ==> (union(Y) ∈ (T1 ∩ T2)))) by RightForall + have(thesis) by Tautology.from(lastStep, containsUnion.definition of (T := T1 ∩ T2)) + } + val contInt = have(containsIntersection(T1 ∩ T2)) subproof { + have((A ∈ (T1 ∩ T2) /\ B ∈ (T1 ∩ T2)) ==> A ∩ B ∈ (T1 ∩ T2)) subproof { + assume(A ∈ (T1 ∩ T2) /\ B ∈ (T1 ∩ T2)) + val leadsInt = have((containsIntersection(T), (A ∈ T /\ B ∈ T)) |- (A ∩ B ∈ T)) subproof { + assume(containsIntersection(T)) + have(forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T))) by Tautology.from(containsIntersection.definition) + thenHave(forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T)) by InstantiateForall(A) + thenHave((A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T) by InstantiateForall(B) + thenHave(thesis) by Tautology + } + have(A ∈ T1 /\ A ∈ T2 /\ B ∈ T1 /\ B ∈ T2) by Tautology.from(setIntersectionMembership of (t := A, x := T1, y := T2), setIntersectionMembership of (t := B, x := T1, y := T2)) + have(A ∩ B ∈ T1 /\ A ∩ B ∈ T2) by Tautology.from(lastStep, leadsInt of (T := T1), leadsInt of (T := T2), topo1, topo2) + have(thesis) by Tautology.from(lastStep, setIntersectionMembership of (t := A ∩ B, x := T1, y := T2)) + } + thenHave(forall(B, (A ∈ (T1 ∩ T2) /\ B ∈ (T1 ∩ T2)) ==> A ∩ B ∈ (T1 ∩ T2))) by RightForall + thenHave(forall(A, forall(B, (A ∈ (T1 ∩ T2) /\ B ∈ (T1 ∩ T2)) ==> A ∩ B ∈ (T1 ∩ T2)))) by RightForall + have(thesis) by Tautology.from(lastStep, containsIntersection.definition of (T := T1 ∩ T2)) + } + have(thesis) by Tautology.from(nonEm, isSub, contEx, contUn, contInt, topology.definition of (T := T1 ∩ T2)) + } } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala new file mode 100644 index 000000000..f8d09926e --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala @@ -0,0 +1,224 @@ +package lisa.maths.topology + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* + +import lisa.automation.kernel.CommonTactics.Definition + +import lisa.maths.topology.Topology.* +import lisa.maths.topology.Instances.* +import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.SetTheoryBasics.* +import lisa.automation.kernel.CommonTactics.* +import lisa.maths.settheory.functions.Functionals.* +import lisa.automation.settheory.SetTheoryTactics.UniqueComprehension +import lisa.automation.settheory.SetTheoryTactics.TheConditional + +object SingletonSet extends lisa.Main { + import lisa.maths.settheory.SetTheory.* + // var defs + private val x, y, z, a, b, c, t, p, f, s = variable + private val X, T = variable + private val S, A, B, Y = variable + + val singletonSetsUniquenes = Theorem( + ∃!(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x))))) + ) { + val implicationProof = have(exists(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { + + val elementInSingleton = have(in(x, singleton(x))) subproof { + have(thesis) by Tautology.from(pairAxiom of (x := x, y := x, z := x)) + } + + val singletonInPowerSet = have(in(x, S) |- in(singleton(x), powerSet(S))) subproof { + assume(in(x, S)) + val introduceY = thenHave((x === y) |- in(y, S)) by Substitution.ApplyRules(x === y) + val useSingleton = have(in(y, singleton(x)) |- in(y, S)) by Tautology.from(introduceY, singletonHasNoExtraElements) + thenHave(() |- (!(in(y, singleton(x))), in(y, S))) by RightNot + thenHave(() |- in(y, singleton(x)) ==> in(y, S)) by Tautology + val universalY = thenHave(() |- forall(y, in(y, singleton(x)) ==> in(y, S))) by RightForall + val singletonIsSubset = have(() |- singleton(x) ⊆ S) by Tautology.from(universalY, subsetAxiom of (z := y, y := S, x := singleton(x))) + have(thesis) by Tautology.from(singletonIsSubset, powerAxiom of (x := singleton(x), y := S)) + } + + val abExist = have((singleton(t) === pair(x, x)) /\ in(x, S) |- ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) subproof { + have((singleton(t) === pair(x, x)) /\ in(x, S) |- (singleton(t) === pair(x, x)) /\ in(x, S) /\ in(x, S)) by Restate + thenHave((singleton(t) === pair(x, x)) /\ in(x, S) |- exists(b, (singleton(t) === pair(x, b)) /\ in(x, S) /\ in(b, S))) by RightExists + thenHave((singleton(t) === pair(x, x)) /\ in(x, S) |- exists(a, exists(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) by RightExists + } + + val cartesianInstantiated = have( + in(singleton(t), cartesianProduct(S, S)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, S)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) + ) subproof { + val instance = have( + (cartesianProduct(x, y) === cartesianProduct(x, y)) <=> ∀( + t, + in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) + ) + ) by InstantiateForall(cartesianProduct(x, y))(cartesianProduct.definition) + thenHave( + ∀( + t, + in(t, cartesianProduct(x, y)) <=> (in(t, powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (t === pair(a, b)) /\ in(a, x) /\ in(b, y)))) + ) + ) by Tautology + thenHave( + in(singleton(t), cartesianProduct(x, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, x) /\ in(b, y)))) + ) by InstantiateForall(singleton(t)) + thenHave( + forall( + x, + in(singleton(t), cartesianProduct(x, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(x, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, x) /\ in(b, y)))) + ) + ) by RightForall + thenHave( + in(singleton(t), cartesianProduct(S, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, y)))) + ) by InstantiateForall(S) + thenHave( + forall( + y, + in(singleton(t), cartesianProduct(S, y)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, y)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, y)))) + ) + ) by RightForall + thenHave( + in(singleton(t), cartesianProduct(S, S)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, S)))) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) + ) by InstantiateForall(S) + } + + val init = have(in(x, S) |- in(x, S)) by Restate + val inSS = have(in(x, S) |- in(x, setUnion(S, S))) by Tautology.from(init, setUnionMembership of (z := x, x := S, y := S)) + val step1 = have(in(x, S) |- in(singleton(x), powerSet(setUnion(S, S))) /\ (singleton(singleton(x)) === pair(x, x))) by + Tautology.from(inSS, singletonInPowerSet of (S := setUnion(S, S))) + thenHave((in(x, S), t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by + Substitution.ApplyRules(singleton(x) === t) + val introduceT = thenHave(in(x, S) /\ (t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by Tautology + val step2 = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ (singleton(t) === pair(x, x))) by + Tautology.from(introduceT, singletonInPowerSet of (x := t, S := powerSet(setUnion(S, S)))) + val extendRHS = have( + in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) + /\ ((singleton(t) === pair(x, x)) /\ in(x, S)) + ) by Tautology.from(step2, elementInSingleton of (x := t)) + val existAB = have( + in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) + /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S))) + ) by Tautology.from(extendRHS, abExist) + val cartesian = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), cartesianProduct(S, S)) /\ in(t, singleton(t))) by + Tautology.from(existAB, cartesianInstantiated) + val introduceZ = thenHave(in(x, S) /\ (t === singleton(x)) |- exists(z, in(z, cartesianProduct(S, S)) /\ in(t, z))) by RightExists + val unionOfProduct = have((in(x, S) /\ (t === singleton(x))) |- in(t, union(cartesianProduct(S, S)))) by + Tautology.from(introduceZ, unionAxiom of (y := z, x := cartesianProduct(S, S), z := t)) + thenHave(exists(x, (in(x, S) /\ (t === singleton(x)))) |- in(t, union(cartesianProduct(S, S)))) by LeftExists + } + have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( + union(cartesianProduct(S, S)), + lambda(t, exists(x, in(x, S) /\ (t === singleton(x)))), + implicationProof + ) + } + val singletonSets = DEF(S) --> The(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))(singletonSetsUniquenes) + + val singletonSetsMembershipRaw = Theorem( + in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S))) + ) { + have(∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by InstantiateForall(singletonSets(S))(singletonSets.definition) + thenHave(thesis) by InstantiateForall(t) + } + + val singletonSetsMembership = Theorem( + in(x, S) <=> in(singleton(x), singletonSets(S)) + ) { + val form = formulaVariable + val memb = have(in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S)))) by Tautology.from(singletonSetsMembershipRaw) + have(in(x, S) |- in(singleton(x), singletonSets(S))) subproof { + assume(in(x, S)) + have(t === singleton(x) |- ((t === singleton(x)) /\ in(x, S))) by Tautology + thenHave(t === singleton(x) |- exists(x, ((t === singleton(x)) /\ in(x, S)))) by RightExists + have((t === singleton(x)) ==> in(t, singletonSets(S))) by Tautology.from(lastStep, memb) + thenHave(forall(t, (t === singleton(x)) ==> in(t, singletonSets(S)))) by RightForall + thenHave((singleton(x) === singleton(x)) ==> in(singleton(x), singletonSets(S))) by InstantiateForall(singleton(x)) + have(thesis) by Tautology.from(lastStep) + } + have(in(singleton(x), singletonSets(S)) |- in(x, S)) subproof { + assume(in(singleton(x), singletonSets(S))) + have(() |- exists(y, ((singleton(x) === singleton(y)) /\ in(y, S)))) by Tautology.from(singletonSetsMembershipRaw of (t := singleton(x), y := x)) + thenHave(((singleton(x) === singleton(y)) <=> (x === y)) |- exists(y, (x === y) /\ in(y, S))) by RightSubstIff + .withParametersSimple( + List(((singleton(x) === singleton(y)), (x === y))), + lambda(form, (exists(y, form /\ in(y, S)))) + ) + + // thenHave(exists(y, (x === y) /\ in(y, S))) by Restate(singletonExtensionality) + /* + val removeExists = have((exists(y, in(y, S) /\ (t === singleton(y))), t === singleton(x)) |- in(x, S)) subproof { + have((in(y, S), t === singleton(x), t === singleton(y)) |- (in(y, S), t === singleton(x), t === singleton(y))) + have((in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by Tautology.from( + lastStep, + singletonExtensionality, + equalityTransitivity of (x := singleton(x), y := t, z := singleton(y)), + replaceEqualityContainsLeft of (z := S) + ) + // thenHave(exists(y, in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by LeftExists + sorry + /*have(exists(y, in(y, S) /\ (t === singleton(y))) /\ (t === singleton(x)) |- (in(x, S))) by Tautology.from( + lastStep, + existentialConjunctionWithClosedFormula of (x := y, p := forall(z, in(z, z) <=> in(z, z))) + ) + // Substitution.ApplyRules(existentialConjunctionWithClosedFormula) + thenHave(thesis) by Tautology*/ + } + // have((t === singleton(x), in(t, singletonSets(S))) |- (t === singleton(x), exists(x, ((t === singleton(x)) /\ in(x, S))))) by Tautology.from(singletonSetsMembershipRaw of (x := y)) + sorry + have((t === singleton(x), in(t, singletonSets(S))) |- in(x, S)) by Tautology.from(lastStep, removeExists) + have(in(singleton(x), singletonSets(S)) |- in(x, S)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := t, y := singleton(x), z := singletonSets(S))) + */ + sorry + } + sorry + } + + val ifContainsSingletonIsDiscrete = Theorem( + (topology(X, T), ∀(x, x ∈ X ==> singleton(x) ∈ T)) |- discreteTopology(X, T) + ) { + assume(∀(x, x ∈ X ==> singleton(x) ∈ T), topology(X, T)) + val topo = have(nonEmpty(X) /\ setOfSubsets(X, T) /\ containsExtremes(X, T) /\ containsUnion(T) /\ containsIntersection(T)) by Tautology.from(topology.definition) + have(∀(x, x ∈ X ==> singleton(x) ∈ T)) by Tautology + val singleDef = thenHave((x ∈ X) ==> (singleton(x) ∈ T)) by InstantiateForall(x) + have(T === powerSet(X)) subproof { + // show T subs powerSet(X) (by def of topology) + val left = have(T ⊆ powerSet(X)) by Tautology.from(topo, setOfSubsets.definition) + // show powerSet(X) subs T + + // For any S ⊆ X we have S = U{x} -> S ∈ T by unionDef + have((S ⊆ X) |- S ∈ T) subproof { + assume(S ⊆ X) + // prove union(cartesianProduct(S, S)) ⊆ T + // -> S = union(union(cartesianProduct(S, S))) in T + have(forall(z, in(z, S) ==> in(z, X))) by Tautology.from(subsetAxiom of (x := S, y := X)) + thenHave(in(z, S) ==> in(z, X)) by InstantiateForall(z) + have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(lastStep, singleDef of (x := z)) + // have(in(z, S) /\ forall(a, in(z, S) <=> in(singleton(z), V)) |- in(singleton(z), V)) + // have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(sorry) + // have(in(singleton(z), singleton(S)) ==> in(singleton(z), T)) + // have(singleton(S) ⊆ T) + // have(union(singleton(S)) ∈ T) + // have(S ∈ T) + sorry + } + have(in(S, powerSet(X)) ==> in(S, T)) by Tautology.from(lastStep, powerAxiom of (x := S, y := X)) + thenHave(forall(S, in(S, powerSet(X)) ==> in(S, T))) by RightForall + val right = have(powerSet(X) ⊆ T) by Tautology.from(lastStep, subsetAxiom of (x := powerSet(X), y := T, z := S)) + + have(thesis) by Tautology.from(left, right, equalityBySubset of (x := powerSet(X), y := T)) + } + have(discreteTopology(X, T)) by Tautology.from(lastStep, topo, discreteTopology.definition) + } +} From fe48a8613409b7f26878136411172755d46c7f2e Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Thu, 12 Dec 2024 16:02:40 +0100 Subject: [PATCH 20/38] Work on Heine's thm --- .../scala/lisa/maths/topology/Instances.scala | 74 +++++++++++++++---- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 12eaad236..d4bc21ed5 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -204,6 +204,17 @@ object Instances extends lisa.Main { val preimage = DEF(f, X, Y, B) --> TheConditional(s, forall(x, preimageFormula))(preimageUniqueness) + inline def unionPreimageFormula = x ∈ s <=> (x ∈ X /\ app(f, x) ∈ union(A)) + + val unionPreimageUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, unionPreimageFormula)) + ) { + have(∃!(s, forall(x, unionPreimageFormula))) by UniqueComprehension(X, lambda(x, app(f, x) ∈ union(A))) + thenHave(thesis) by Weakening + } + + val unionPreimage = DEF(f, X, Y, A) --> TheConditional(s, forall(x, unionPreimageFormula))(unionPreimageUniqueness) + val preimageSetUnion = Theorem( functionFrom(f, X, Y) /\ subset(A, Y) /\ @@ -272,6 +283,13 @@ object Instances extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := preimage(f, X, Y, setUnion(A, B)), y := setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B))))) } + val preimageUnionThm = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- + preimage(f, X, Y, union(A)) === unionPreimage(f, X, Y, A) + ) { + sorry + } + // Couldn't import surjectivity from FunctionProperties without an error, so here it is val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) @@ -467,6 +485,12 @@ object Instances extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A))) } + val preimageSubset = Theorem( + (functionFrom(f, X, Y), subset(A, Y)) |- preimage(f, X, Y, A) ⊆ X + ) { + sorry + } + val preimageY = Theorem( functionFrom(f, X, Y) |- preimage(f, X, Y, Y) === X ) { @@ -845,27 +869,49 @@ object Instances extends lisa.Main { have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { assume(openCover(Y, T2, O)) - // TODO: change O is not the correct subcover for X, it should be unionPreimage(O) - // We have an open cover of X - val isOpenCover = have(openCover(X, T1, O)) subproof { - sorry + // have(cover(Y, O)) by Tautology.from(cover.definition of (X := Y, O := O)) + // have(O ⊆ T2 ==> (union(O) ∈ T2)) by InstantiateForall(containsUnion.definition of (T := T2))(O) + /*val unionInT2 = have(union(O) ∈ T2) by Tautology.from( + openCover.definition of (X := Y, T := T2), + containsUnion.definition of (T := T2), + lastStep + )*/ + + // We have an open cover of X, that's unionPreimage(f, X, Y, O) + val isOpenCover = have(openCover(X, T1, unionPreimage(f, X, Y, O))) subproof { + val isCover = have(cover(X, unionPreimage(f, X, Y, O))) subproof { + sorry + } + have(z ∈ unionPreimage(f, X, Y, O) ==> z ∈ T1) subproof { + assume(z ∈ unionPreimage(f, X, Y, O)) + + sorry + } /*Tautology.from( + // preimageSubset of (f := f, X := X, Y := Y, A := union(O)), + // preimageUnionThm of (f := f, X := X, Y := Y, O := O) + sorry + )*/ + thenHave(forall(z, z ∈ unionPreimage(f, X, Y, O) ==> z ∈ T1)) by RightForall + val isSubset = have(unionPreimage(f, X, Y, O) ⊆ T1) by Tautology.from( + subsetAxiom of (x := unionPreimage(f, X, Y, O), y := T1), + lastStep + ) + have(thesis) by Tautology.from(openCover.definition of (T := T1, O := unionPreimage(f, X, Y, O)), isCover, isSubset) } // Whence the existence of a finite subcover O3 - // TODO: change O is not the correct cover for X, it should be unionPreimage(O) (to be defined) - have(openCover(X, T1, O) ==> ∃(O3, subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3))) by InstantiateForall(O)(xIsCompact) - val existsO3 = have(∃(O3, subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) - - // From that finite subcover O3, we can recover a finite subcover O2 of Y - // TODO: change O is not the correct cover for X, it should be unionPreimage(O) (to be defined) - have(subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { - assume(subset(O3, O), openCover(X, T1, O3), finite(O3)) + have(openCover(X, T1, unionPreimage(f, X, Y, O)) ==> ∃(O3, subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by InstantiateForall(unionPreimage(f, X, Y, O))( + xIsCompact + ) + val existsO3 = have(∃(O3, subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) + + have(subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { + assume(subset(O3, unionPreimage(f, X, Y, O)), openCover(X, T1, O3), finite(O3)) sorry } // Concluding - // TODO: change O is not the correct cover for X, it should be unionPreimage(O) (to be defined) - thenHave(∃(O3, subset(O3, O) /\ openCover(X, T1, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by LeftExists + thenHave(∃(O3, subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by LeftExists have(thesis) by Tautology.from(lastStep, existsO3) } thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by Tautology From 3af5856cea1a2857ac4a906fbe201623c8ce486f Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Thu, 12 Dec 2024 16:55:54 +0100 Subject: [PATCH 21/38] added memberships --- .../maths/settheory/SetTheoryBasics.scala | 2 +- .../scala/lisa/maths/topology/Instances.scala | 77 +++++++++++++++---- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala index fc302ea52..075ba392f 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -191,6 +191,6 @@ object SetTheoryBasics extends lisa.Main { val equalitySymmetry = Theorem( x === y |- y === x ) { - sorry + have(thesis) by Tautology.from(equalityBySubset, equalityBySubset of (x := y, y := x)) } } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index d4bc21ed5..c65815cb3 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -81,6 +81,12 @@ object Instances extends lisa.Main { val directImage = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImageFormula))(directImageUniqueness) + val directImageMembership = Theorem((functionFrom(f, X, Y), subset(A, X)) |- y ∈ directImage(f, X, Y, A) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ A))) { + assume(functionFrom(f, X, Y) /\ subset(A, X)) + have(forall(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) + thenHave(thesis) by InstantiateForall(y) + } + val directImageSetUnion = Theorem( functionFrom(f, X, Y) /\ subset(A, X) /\ @@ -204,6 +210,35 @@ object Instances extends lisa.Main { val preimage = DEF(f, X, Y, B) --> TheConditional(s, forall(x, preimageFormula))(preimageUniqueness) + val preimageMembership = Theorem((functionFrom(f, X, Y), subset(B, Y)) |- x ∈ preimage(f, X, Y, B) <=> (x ∈ X /\ app(f, x) ∈ B)) { + assume(functionFrom(f, X, Y) /\ subset(B, Y)) + have(forall(x, x ∈ preimage(f, X, Y, B) <=> (x ∈ X /\ app(f, x) ∈ B))) by InstantiateForall(preimage(f, X, Y, B))(preimage.definition) + thenHave(thesis) by InstantiateForall(x) + } + + val preimageSubset = Theorem( + (functionFrom(f, X, Y), subset(A, Y)) |- preimage(f, X, Y, A) ⊆ X + ) { + assume(functionFrom(f, X, Y) /\ subset(A, Y)) + have(in(z, preimage(f, X, Y, A)) ==> in(z, X)) by Tautology.from(preimageMembership of (B := A, x := z)) + thenHave(forall(z, in(z, preimage(f, X, Y, A)) ==> in(z, X))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimage(f, X, Y, A), y := X)) + } + + val preimageY = Theorem( + functionFrom(f, X, Y) |- preimage(f, X, Y, Y) === X + ) { + assume(functionFrom(f, X, Y)) + val first = have(preimage(f, X, Y, Y) ⊆ X) by Tautology.from(subsetReflexivity of (x := Y), preimageSubset of (A := Y)) + + have(in(z, X) ==> in(z, preimage(f, X, Y, Y))) subproof { + assume(in(z, X)) + sorry + } + thenHave(forall(z, in(z, X) ==> in(z, preimage(f, X, Y, Y)))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := X, y := preimage(f, X, Y, Y))) + } + inline def unionPreimageFormula = x ∈ s <=> (x ∈ X /\ app(f, x) ∈ union(A)) val unionPreimageUniqueness = Theorem( @@ -215,6 +250,12 @@ object Instances extends lisa.Main { val unionPreimage = DEF(f, X, Y, A) --> TheConditional(s, forall(x, unionPreimageFormula))(unionPreimageUniqueness) + val unionPreimageMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A))) { + assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) + have(forall(x, x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A)))) by InstantiateForall(unionPreimage(f, X, Y, A))(unionPreimage.definition) + thenHave(thesis) by InstantiateForall(x) + } + val preimageSetUnion = Theorem( functionFrom(f, X, Y) /\ subset(A, Y) /\ @@ -453,7 +494,13 @@ object Instances extends lisa.Main { val applyDirectImage = Theorem( A === B |- directImage(f, X, Y, A) === directImage(f, X, Y, B) ) { - sorry + have(((A === B), in(z, directImage(f, X, Y, A))) |- in(z, directImage(f, X, Y, B))) subproof { + have(((A === B), in(z, directImage(f, X, Y, A))) |- in(z, directImage(f, X, Y, A))) by Tautology + thenHave(thesis) by RightSubstEq.withParametersSimple(List((A, B)), lambda(x, in(z, directImage(f, X, Y, x)))) + } + have(A === B |- in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B))) by Tautology.from(lastStep, lastStep of (A := B, B := A)) + thenHave(A === B |- forall(z, in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B)))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B))) } val directImagePreimage = Theorem( @@ -461,7 +508,21 @@ object Instances extends lisa.Main { |- directImage(f, X, Y, preimage(f, X, Y, A)) ⊆ A ) { assume(functionFrom(f, X, Y), subset(A, Y)) - sorry + have(subset(preimage(f, X, Y, A), X) |- forall(z, z ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A))))) by InstantiateForall( + directImage(f, X, Y, preimage(f, X, Y, A)) + )(directImage.definition of (A := preimage(f, X, Y, A))) + thenHave(subset(preimage(f, X, Y, A), X) |- z ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A)))) by InstantiateForall(z) + val imageMember = have(z ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A)))) by Tautology.from(lastStep, preimageSubset) + + have(in(z, directImage(f, X, Y, preimage(f, X, Y, A))) ==> in(z, A)) subproof { + assume(in(z, directImage(f, X, Y, preimage(f, X, Y, A)))) + have(((app(f, x) === z), x ∈ preimage(f, X, Y, A)) |- (app(f, x) === z) /\ (x ∈ X /\ app(f, x) ∈ A)) by Tautology.from(preimageMembership of (B := A)) + have(((app(f, x) === z) /\ x ∈ preimage(f, X, Y, A)) |- in(z, A)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := app(f, x), y := z, z := A)) + thenHave(exists(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A)) |- in(z, A)) by LeftExists + have(thesis) by Tautology.from(lastStep, imageMember) + } + thenHave(forall(z, in(z, directImage(f, X, Y, preimage(f, X, Y, A))) ==> in(z, A))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A)) } val directImagePreimageSurjective = Theorem( @@ -485,18 +546,6 @@ object Instances extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A))) } - val preimageSubset = Theorem( - (functionFrom(f, X, Y), subset(A, Y)) |- preimage(f, X, Y, A) ⊆ X - ) { - sorry - } - - val preimageY = Theorem( - functionFrom(f, X, Y) |- preimage(f, X, Y, Y) === X - ) { - sorry - } - val imageSurjective = Theorem( (functionFrom(f, X, Y), surjective(f, X, Y)) |- directImage(f, X, Y, X) === Y ) { From a1d99c22e4e082e272d58d3b127efdb0277a9f2b Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Thu, 12 Dec 2024 17:00:15 +0100 Subject: [PATCH 22/38] preimageUnion to preimages --- .../scala/lisa/maths/topology/Instances.scala | 86 +++++++++++-------- 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index c65815cb3..d6d371be3 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -236,25 +236,26 @@ object Instances extends lisa.Main { sorry } thenHave(forall(z, in(z, X) ==> in(z, preimage(f, X, Y, Y)))) by RightForall - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := X, y := preimage(f, X, Y, Y))) + sorry + // have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := X, y := preimage(f, X, Y, Y))) } - inline def unionPreimageFormula = x ∈ s <=> (x ∈ X /\ app(f, x) ∈ union(A)) + inline def preimagesFormula = x ∈ s <=> (x ∈ X /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) - val unionPreimageUniqueness = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, unionPreimageFormula)) + val preimagesUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, preimagesFormula)) ) { - have(∃!(s, forall(x, unionPreimageFormula))) by UniqueComprehension(X, lambda(x, app(f, x) ∈ union(A))) + have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(X, lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) thenHave(thesis) by Weakening } - val unionPreimage = DEF(f, X, Y, A) --> TheConditional(s, forall(x, unionPreimageFormula))(unionPreimageUniqueness) + val preimages = DEF(f, X, Y, A) --> TheConditional(s, forall(x, preimagesFormula))(preimagesUniqueness) - val unionPreimageMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A))) { + /*val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A))) { assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) have(forall(x, x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A)))) by InstantiateForall(unionPreimage(f, X, Y, A))(unionPreimage.definition) thenHave(thesis) by InstantiateForall(x) - } + }*/ val preimageSetUnion = Theorem( functionFrom(f, X, Y) /\ @@ -326,7 +327,7 @@ object Instances extends lisa.Main { val preimageUnionThm = Theorem( (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- - preimage(f, X, Y, union(A)) === unionPreimage(f, X, Y, A) + preimage(f, X, Y, union(A)) === union(preimages(f, X, Y, A)) ) { sorry } @@ -549,6 +550,7 @@ object Instances extends lisa.Main { val imageSurjective = Theorem( (functionFrom(f, X, Y), surjective(f, X, Y)) |- directImage(f, X, Y, X) === Y ) { + // Should be fairly easy using surjectiveImpliesRangeIsCodomain and directImageX sorry } @@ -918,49 +920,65 @@ object Instances extends lisa.Main { have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { assume(openCover(Y, T2, O)) - // have(cover(Y, O)) by Tautology.from(cover.definition of (X := Y, O := O)) - // have(O ⊆ T2 ==> (union(O) ∈ T2)) by InstantiateForall(containsUnion.definition of (T := T2))(O) - /*val unionInT2 = have(union(O) ∈ T2) by Tautology.from( - openCover.definition of (X := Y, T := T2), + have(forall(O, (O ⊆ T2) ==> (union(O) ∈ T2))) by Tautology.from( containsUnion.definition of (T := T2), + yIsTop, + topology.definition of (X := Y, T := T2) + ) + thenHave(O ⊆ T2 ==> (union(O) ∈ T2)) by InstantiateForall(O) + val unionInT2 = have(union(O) ∈ T2) by Tautology.from( + openCover.definition of (X := Y, T := T2), lastStep - )*/ + ) - // We have an open cover of X, that's unionPreimage(f, X, Y, O) - val isOpenCover = have(openCover(X, T1, unionPreimage(f, X, Y, O))) subproof { - val isCover = have(cover(X, unionPreimage(f, X, Y, O))) subproof { - sorry + // We have an open cover of X, that's preimages(f, X, Y, O) + val isOpenCover = have(openCover(X, T1, preimages(f, X, Y, O))) subproof { + // Firstly, it's a cover + val isCover = have(cover(X, preimages(f, X, Y, O))) subproof { + have(o ∈ preimages(f, X, Y, O) ==> o ⊆ X) subproof { + sorry + } + val firstPart = thenHave(∀(o, o ∈ preimages(f, X, Y, O) ==> o ⊆ X)) by RightForall + val secondPart = have(X ⊆ union(preimages(f, X, Y, O))) subproof { + sorry + } + have(thesis) by Tautology.from(firstPart, secondPart, cover.definition of (O := preimages(f, X, Y, O))) } - have(z ∈ unionPreimage(f, X, Y, O) ==> z ∈ T1) subproof { - assume(z ∈ unionPreimage(f, X, Y, O)) + // Also, its elements are open + have(z ∈ preimages(f, X, Y, O) ==> z ∈ T1) subproof { + assume(z ∈ preimages(f, X, Y, O)) + // have(exists(o, o ∈ O /\ z == preimage(f, X, Y, o))) by + /*have(O ⊆ powerSet(Y) |- z ∈ preimage(f, X, Y, union(O))) by Tautology.from( + fIsFunction, + preimageUnionThm of (A := O), + replaceEqualityContainsRight of (x := preimage(f, X, Y, union(O)), y := preimages(f, X, Y, O)), + lastStep + )*/ sorry - } /*Tautology.from( - // preimageSubset of (f := f, X := X, Y := Y, A := union(O)), - // preimageUnionThm of (f := f, X := X, Y := Y, O := O) - sorry - )*/ - thenHave(forall(z, z ∈ unionPreimage(f, X, Y, O) ==> z ∈ T1)) by RightForall - val isSubset = have(unionPreimage(f, X, Y, O) ⊆ T1) by Tautology.from( - subsetAxiom of (x := unionPreimage(f, X, Y, O), y := T1), + } + thenHave(forall(z, z ∈ preimages(f, X, Y, O) ==> z ∈ T1)) by RightForall + val isOpenSubset = have(preimages(f, X, Y, O) ⊆ T1) by Tautology.from( + subsetAxiom of (x := preimages(f, X, Y, O), y := T1), lastStep ) - have(thesis) by Tautology.from(openCover.definition of (T := T1, O := unionPreimage(f, X, Y, O)), isCover, isSubset) + + have(thesis) by Tautology.from(openCover.definition of (T := T1, O := preimages(f, X, Y, O)), isCover, isOpenSubset) } // Whence the existence of a finite subcover O3 - have(openCover(X, T1, unionPreimage(f, X, Y, O)) ==> ∃(O3, subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by InstantiateForall(unionPreimage(f, X, Y, O))( + have(openCover(X, T1, preimages(f, X, Y, O)) ==> ∃(O3, subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by InstantiateForall(preimages(f, X, Y, O))( xIsCompact ) - val existsO3 = have(∃(O3, subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) + val existsO3 = have(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) - have(subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { - assume(subset(O3, unionPreimage(f, X, Y, O)), openCover(X, T1, O3), finite(O3)) + have(subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { + assume(subset(O3, preimages(f, X, Y, O)), openCover(X, T1, O3), finite(O3)) sorry } // Concluding - thenHave(∃(O3, subset(O3, unionPreimage(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by LeftExists + thenHave(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by LeftExists have(thesis) by Tautology.from(lastStep, existsO3) } thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by Tautology From be4729659728c3d9f71c2efab598ebeccef791ea Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Thu, 12 Dec 2024 23:32:16 +0100 Subject: [PATCH 23/38] intermediate Value Theorem finished --- .../lisa/maths/settheory/SetTheory.scala | 5 + .../maths/settheory/SetTheoryBasics.scala | 6 + .../scala/lisa/maths/topology/Instances.scala | 204 +++++++++--------- 3 files changed, 113 insertions(+), 102 deletions(-) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala index 78b8e0b71..2ae17ea1a 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheory.scala @@ -1035,6 +1035,11 @@ object SetTheory extends lisa.Main { */ val setDifference = DEF(x, y) --> The(z, ∀(t, in(t, z) <=> (in(t, x) /\ !in(t, y))))(setDifferenceUniqueness) + val setDifferenceMembership = Theorem(in(t, setDifference(x, y)) <=> (in(t, x) /\ !in(t, y))) { + have(∀(t, in(t, setDifference(x, y)) <=> (in(t, x) /\ !in(t, y)))) by InstantiateForall(setDifference(x, y))(setDifference.definition) + thenHave(thesis) by InstantiateForall(t) + } + /** * Theorem --- Intersection of a non-empty Class is a Set * diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala index 075ba392f..37b6e46e3 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -147,6 +147,12 @@ object SetTheoryBasics extends lisa.Main { thenHave(thesis) by Tautology } + val differenceShrinks = Theorem(setDifference(x, y) ⊆ x) { + have(in(z, setDifference(x, y)) ==> in(z, x)) by Tautology.from(setDifferenceMembership of (t := z)) + thenHave(forall(z, in(z, setDifference(x, y)) ==> in(z, x))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := setDifference(x, y), y := x)) + } + /** * Lemma --- Range introduction and elimination rules. If en element is in the image of a function, then it has a preimage inside its domain. * diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index d6d371be3..637c331f2 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -231,32 +231,17 @@ object Instances extends lisa.Main { assume(functionFrom(f, X, Y)) val first = have(preimage(f, X, Y, Y) ⊆ X) by Tautology.from(subsetReflexivity of (x := Y), preimageSubset of (A := Y)) - have(in(z, X) ==> in(z, preimage(f, X, Y, Y))) subproof { - assume(in(z, X)) - sorry - } + have(in(z, X) ==> in(z, preimage(f, X, Y, Y))) by Tautology.from( + functionFromApplication of (x := X, y := Y, a := z), + functionFrom.definition of (x := X, y := Y), + preimageMembership of (x := z, B := Y), + subsetReflexivity of (x := Y) + ) thenHave(forall(z, in(z, X) ==> in(z, preimage(f, X, Y, Y)))) by RightForall - sorry - // have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := X, y := preimage(f, X, Y, Y))) + val second = have(X ⊆ preimage(f, X, Y, Y)) by Tautology.from(lastStep, subsetAxiom of (x := X, y := preimage(f, X, Y, Y))) + have(thesis) by Tautology.from(first, second, equalityBySubset of (x := X, y := preimage(f, X, Y, Y))) } - inline def preimagesFormula = x ∈ s <=> (x ∈ X /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) - - val preimagesUniqueness = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, preimagesFormula)) - ) { - have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(X, lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) - thenHave(thesis) by Weakening - } - - val preimages = DEF(f, X, Y, A) --> TheConditional(s, forall(x, preimagesFormula))(preimagesUniqueness) - - /*val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A))) { - assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) - have(forall(x, x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A)))) by InstantiateForall(unionPreimage(f, X, Y, A))(unionPreimage.definition) - thenHave(thesis) by InstantiateForall(x) - }*/ - val preimageSetUnion = Theorem( functionFrom(f, X, Y) /\ subset(A, Y) /\ @@ -325,6 +310,62 @@ object Instances extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := preimage(f, X, Y, setUnion(A, B)), y := setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B))))) } + val preimageDifference = Theorem( + (functionFrom(f, X, Y), subset(A, Y)) + |- setDifference(X, preimage(f, X, Y, A)) === preimage(f, X, Y, setDifference(Y, A)) + ) { + assume(functionFrom(f, X, Y), subset(A, Y)) + + have(forall(t, t ∈ setDifference(Y, A) <=> (in(t, Y) /\ !in(t, A)))) by InstantiateForall(setDifference(Y, A))(setDifference.definition of (x := Y, y := A)) + val defDiffY = thenHave(z ∈ setDifference(Y, A) <=> (in(z, Y) /\ !in(z, A))) by InstantiateForall(z) + + val forward = have(x ∈ setDifference(X, preimage(f, X, Y, A)) ==> x ∈ preimage(f, X, Y, setDifference(Y, A))) subproof { + assume(x ∈ setDifference(X, preimage(f, X, Y, A))) + + have(in(x, X) /\ !in(x, preimage(f, X, Y, A))) by Tautology.from(setDifferenceMembership of (t := x, x := X, y := preimage(f, X, Y, A))) + have(in(x, X) /\ !in(app(f, x), A)) by Tautology.from(lastStep, preimageMembership of (B := A)) + // (x ∈ X /\ app(f, x) ∈ setDifference(Y, A)) + have(in(x, X) /\ in(app(f, x), setDifference(Y, A))) by Tautology.from( + lastStep, + functionFromApplication of (x := X, y := Y, a := x), + functionFrom.definition of (x := X, y := Y), + setDifferenceMembership of (t := app(f, x), x := Y, y := A) + ) + have(thesis) by Tautology.from(lastStep, differenceShrinks of (x := Y, y := A), preimageMembership of (B := setDifference(Y, A))) + } + + val backward = have(x ∈ preimage(f, X, Y, setDifference(Y, A)) ==> x ∈ setDifference(X, preimage(f, X, Y, A))) subproof { + assume(x ∈ preimage(f, X, Y, setDifference(Y, A))) + have(x ∈ X /\ app(f, x) ∈ Y /\ !(app(f, x) ∈ A)) by Tautology.from( + preimageMembership of (B := setDifference(Y, A)), + setDifferenceMembership of (t := app(f, x), x := Y, y := A), + differenceShrinks of (x := Y, y := A) + ) + have(thesis) by Tautology.from(lastStep, preimageMembership of (B := A, t := x), setDifferenceMembership of (t := x, x := X, y := preimage(f, X, Y, A))) + } + + have(x ∈ setDifference(X, preimage(f, X, Y, A)) <=> x ∈ preimage(f, X, Y, setDifference(Y, A))) by RightIff(forward, backward) + thenHave(∀(x, x ∈ setDifference(X, preimage(f, X, Y, A)) <=> x ∈ preimage(f, X, Y, setDifference(Y, A)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A))))) + } + + inline def preimagesFormula = x ∈ s <=> (x ∈ X /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) + + val preimagesUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, preimagesFormula)) + ) { + have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(X, lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) + thenHave(thesis) by Weakening + } + + val preimages = DEF(f, X, Y, A) --> TheConditional(s, forall(x, preimagesFormula))(preimagesUniqueness) + + /*val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A))) { + assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) + have(forall(x, x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A)))) by InstantiateForall(unionPreimage(f, X, Y, A))(unionPreimage.definition) + thenHave(thesis) by InstantiateForall(x) + }*/ + val preimageUnionThm = Theorem( (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- preimage(f, X, Y, union(A)) === union(preimages(f, X, Y, A)) @@ -358,30 +399,6 @@ object Instances extends lisa.Main { have(thesis) by Cut(surjfunc, funceq) } - val preimageDifference = Theorem( - (functionFrom(f, X, Y), subset(A, Y)) - |- setDifference(X, preimage(f, X, Y, A)) === preimage(f, X, Y, setDifference(Y, A)) - ) { - assume(functionFrom(f, X, Y), subset(A, Y)) - - have(forall(t, t ∈ setDifference(Y, A) <=> (in(t, Y) /\ !in(t, A)))) by InstantiateForall(setDifference(Y, A))(setDifference.definition of (x := Y, y := A)) - val defDiffY = thenHave(z ∈ setDifference(Y, A) <=> (in(z, Y) /\ !in(z, A))) by InstantiateForall(z) - - val forward = have(x ∈ setDifference(X, preimage(f, X, Y, A)) ==> x ∈ preimage(f, X, Y, setDifference(Y, A))) subproof { - assume(x ∈ setDifference(X, preimage(f, X, Y, A))) - sorry - } - - val backward = have(x ∈ preimage(f, X, Y, setDifference(Y, A)) ==> x ∈ setDifference(X, preimage(f, X, Y, A))) subproof { - assume(x ∈ preimage(f, X, Y, setDifference(Y, A))) - sorry - } - - have(x ∈ setDifference(X, preimage(f, X, Y, A)) <=> x ∈ preimage(f, X, Y, setDifference(Y, A))) by RightIff(forward, backward) - thenHave(∀(x, x ∈ setDifference(X, preimage(f, X, Y, A)) <=> x ∈ preimage(f, X, Y, setDifference(Y, A)))) by RightForall - andThen(Substitution.applySubst(extensionalityAxiom of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A))))) - } - val directImageEmptySet = Theorem( (functionFrom(f, X, Y)) |- directImage(f, X, Y, emptySet) === emptySet @@ -443,55 +460,6 @@ object Instances extends lisa.Main { have(thesis) by Tautology.from(subsetAxiom of (x := directImage(f, X, Y, A), y := functionRange(f)), lastStep) } - val directImageX = Theorem( - functionFrom(f, X, Y) - |- directImage(f, X, Y, X) === functionRange(f) - ) { - assume(functionFrom(f, X, Y)) - - have(subset(X, X) |- forall(z, z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X)))) by InstantiateForall(directImage(f, X, Y, X))(directImage.definition of (A := X)) - thenHave(subset(X, X) |- z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X))) by InstantiateForall(z) - val defIm = have(z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X))) by Tautology.from( - lastStep, - subsetReflexivity of (x := X) - ) - - val forward = have(z ∈ directImage(f, X, Y, X) ==> z ∈ functionRange(f)) by Tautology.from( - directImageSubset of (A := X), - subsetReflexivity of (x := X), - subsetTactic of (x := directImage(f, X, Y, X), y := functionRange(f)) - ) - - val backward = have(z ∈ functionRange(f) ==> z ∈ directImage(f, X, Y, X)) subproof { - assume(z ∈ functionRange(f)) - - have(subset(functionRange(f), Y)) by Tautology.from(functionImpliesRangeSubsetOfCodomain of (x := X, y := Y)) - val zInY = have(z ∈ Y) by Tautology.from(lastStep, subsetTactic of (x := functionRange(f), y := Y)) - - have(x ∈ functionDomain(f) /\ (app(f, x) === z) |- x ∈ X /\ (app(f, x) === z)) by Tautology.from( - functionFromImpliesDomainEq of (x := X, y := Y), - replaceEqualityContainsRight of (x := functionDomain(f), y := X, z := x) - ) - thenHave(x ∈ functionDomain(f) /\ (app(f, x) === z) |- ∃(x, (app(f, x) === z) /\ x ∈ X)) by RightExists - have(x ∈ functionDomain(f) /\ (app(f, x) === z) |- z ∈ directImage(f, X, Y, X)) by Tautology.from( - lastStep, - defIm, - zInY - ) - thenHave(∃(x, x ∈ functionDomain(f) /\ (app(f, x) === z)) |- z ∈ directImage(f, X, Y, X)) by LeftExists - - have(thesis) by Tautology.from( - lastStep, - functionRangeMembership of (y := z), - functionFromImpliesFunctional of (x := X, y := Y) - ) - } - - have(z ∈ directImage(f, X, Y, X) <=> z ∈ functionRange(f)) by RightIff(forward, backward) - thenHave(∀(z, z ∈ directImage(f, X, Y, X) <=> z ∈ functionRange(f))) by RightForall - andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, X), y := functionRange(f)))) - } - val applyDirectImage = Theorem( A === B |- directImage(f, X, Y, A) === directImage(f, X, Y, B) ) { @@ -537,21 +505,53 @@ object Instances extends lisa.Main { directImagePreimage ) - val backward = have(x ∈ A ==> x ∈ directImage(f, X, Y, preimage(f, X, Y, A))) subproof { - assume(x ∈ A) - sorry + val backward = have(y ∈ A ==> y ∈ directImage(f, X, Y, preimage(f, X, Y, A))) subproof { + assume(y ∈ A) + have(x ∈ functionDomain(f) /\ (app(f, x) === y) |- (app(f, x) === y) /\ x ∈ preimage(f, X, Y, A)) by Tautology.from( + functionFromImpliesDomainEq of (x := X, y := Y), + replaceEqualityContainsRight of (x := functionDomain(f), y := X, z := x), + replaceEqualityContainsLeft of (x := app(f, x), z := A), + preimageMembership of (B := A) + ) + thenHave(x ∈ functionDomain(f) /\ (app(f, x) === y) |- ∃(x, (app(f, x) === y) /\ x ∈ preimage(f, X, Y, A))) by RightExists + have(x ∈ functionDomain(f) /\ (app(f, x) === y) |- y ∈ directImage(f, X, Y, preimage(f, X, Y, A))) by Tautology.from( + lastStep, + subsetTactic of (x := A, y := Y, z := y), + directImageMembership of (A := preimage(f, X, Y, A)), + preimageSubset + ) + thenHave(∃(x, x ∈ functionDomain(f) /\ (app(f, x) === y)) |- y ∈ directImage(f, X, Y, preimage(f, X, Y, A))) by LeftExists + have(thesis) by Tautology.from( + lastStep, + functionRangeMembership, + subsetTactic of (x := A, y := Y, z := y), + surjectiveImpliesRangeIsCodomain of (x := X, y := Y), + replaceEqualityContainsRight of (x := Y, y := functionRange(f), z := y), + functionFromImpliesFunctional of (x := X, y := Y) + ) } - have(x ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> x ∈ A) by RightIff(forward, backward) + have(x ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> x ∈ A) by RightIff(forward, backward of (y := x)) thenHave(∀(x, x ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> x ∈ A)) by RightForall andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A))) } + val directImageX = Theorem( + functionFrom(f, X, Y) + |- directImage(f, X, Y, X) === functionRange(f) + ) { + assume(functionFrom(f, X, Y)) + + } + val imageSurjective = Theorem( (functionFrom(f, X, Y), surjective(f, X, Y)) |- directImage(f, X, Y, X) === Y ) { - // Should be fairly easy using surjectiveImpliesRangeIsCodomain and directImageX - sorry + have(thesis) by Tautology.from( + surjectiveImpliesRangeIsCodomain of (x := X, y := Y), + directImageX, + equalityTransitivity of (x := Y, y := functionRange(f), z := directImage(f, X, Y, X)) + ) } val indiscreteIsTopology = Theorem( From 428b78ee620dbcb37b5a929293c97c74cfb0745f Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Fri, 13 Dec 2024 00:20:31 +0100 Subject: [PATCH 24/38] huge progress on singleton proof --- .../lisa/maths/topology/SingletonSet.scala | 67 +++++++------------ 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala index f8d09926e..cb02dc3ee 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala @@ -138,7 +138,7 @@ object SingletonSet extends lisa.Main { ) { val form = formulaVariable val memb = have(in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S)))) by Tautology.from(singletonSetsMembershipRaw) - have(in(x, S) |- in(singleton(x), singletonSets(S))) subproof { + val forward = have(in(x, S) |- in(singleton(x), singletonSets(S))) subproof { assume(in(x, S)) have(t === singleton(x) |- ((t === singleton(x)) /\ in(x, S))) by Tautology thenHave(t === singleton(x) |- exists(x, ((t === singleton(x)) /\ in(x, S)))) by RightExists @@ -147,41 +147,18 @@ object SingletonSet extends lisa.Main { thenHave((singleton(x) === singleton(x)) ==> in(singleton(x), singletonSets(S))) by InstantiateForall(singleton(x)) have(thesis) by Tautology.from(lastStep) } - have(in(singleton(x), singletonSets(S)) |- in(x, S)) subproof { + val backward = have(in(singleton(x), singletonSets(S)) |- in(x, S)) subproof { assume(in(singleton(x), singletonSets(S))) - have(() |- exists(y, ((singleton(x) === singleton(y)) /\ in(y, S)))) by Tautology.from(singletonSetsMembershipRaw of (t := singleton(x), y := x)) - thenHave(((singleton(x) === singleton(y)) <=> (x === y)) |- exists(y, (x === y) /\ in(y, S))) by RightSubstIff - .withParametersSimple( - List(((singleton(x) === singleton(y)), (x === y))), - lambda(form, (exists(y, form /\ in(y, S)))) - ) - - // thenHave(exists(y, (x === y) /\ in(y, S))) by Restate(singletonExtensionality) - /* - val removeExists = have((exists(y, in(y, S) /\ (t === singleton(y))), t === singleton(x)) |- in(x, S)) subproof { - have((in(y, S), t === singleton(x), t === singleton(y)) |- (in(y, S), t === singleton(x), t === singleton(y))) - have((in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by Tautology.from( - lastStep, - singletonExtensionality, - equalityTransitivity of (x := singleton(x), y := t, z := singleton(y)), - replaceEqualityContainsLeft of (z := S) - ) - // thenHave(exists(y, in(y, S) /\ (t === singleton(x)) /\ (t === singleton(y))) |- (in(x, S))) by LeftExists - sorry - /*have(exists(y, in(y, S) /\ (t === singleton(y))) /\ (t === singleton(x)) |- (in(x, S))) by Tautology.from( - lastStep, - existentialConjunctionWithClosedFormula of (x := y, p := forall(z, in(z, z) <=> in(z, z))) - ) - // Substitution.ApplyRules(existentialConjunctionWithClosedFormula) - thenHave(thesis) by Tautology*/ + have(exists(y, ((singleton(x) === singleton(y)) /\ in(y, S))) |- in(x, S)) subproof { + have(((singleton(x) === singleton(y)) /\ in(y, S)) |- in(x, S)) by Tautology.from(replaceEqualityContainsLeft of (z := S), singletonExtensionality) + thenHave(thesis) by LeftExists } - // have((t === singleton(x), in(t, singletonSets(S))) |- (t === singleton(x), exists(x, ((t === singleton(x)) /\ in(x, S))))) by Tautology.from(singletonSetsMembershipRaw of (x := y)) - sorry - have((t === singleton(x), in(t, singletonSets(S))) |- in(x, S)) by Tautology.from(lastStep, removeExists) - have(in(singleton(x), singletonSets(S)) |- in(x, S)) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := t, y := singleton(x), z := singletonSets(S))) - */ - sorry + have(thesis) by Tautology.from(lastStep, singletonSetsMembershipRaw of (t := singleton(x), y := x)) } + have(thesis) by Tautology.from(forward, backward) + } + + val unionSingletonSets = Theorem(union(singletonSets(S)) === S) { sorry } @@ -190,6 +167,8 @@ object SingletonSet extends lisa.Main { ) { assume(∀(x, x ∈ X ==> singleton(x) ∈ T), topology(X, T)) val topo = have(nonEmpty(X) /\ setOfSubsets(X, T) /\ containsExtremes(X, T) /\ containsUnion(T) /\ containsIntersection(T)) by Tautology.from(topology.definition) + have(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by Tautology.from(topo, containsUnion.definition) + val contUnion = thenHave(singletonSets(S) ⊆ T ==> (union(singletonSets(S)) ∈ T)) by InstantiateForall(singletonSets(S)) have(∀(x, x ∈ X ==> singleton(x) ∈ T)) by Tautology val singleDef = thenHave((x ∈ X) ==> (singleton(x) ∈ T)) by InstantiateForall(x) have(T === powerSet(X)) subproof { @@ -200,18 +179,22 @@ object SingletonSet extends lisa.Main { // For any S ⊆ X we have S = U{x} -> S ∈ T by unionDef have((S ⊆ X) |- S ∈ T) subproof { assume(S ⊆ X) - // prove union(cartesianProduct(S, S)) ⊆ T - // -> S = union(union(cartesianProduct(S, S))) in T have(forall(z, in(z, S) ==> in(z, X))) by Tautology.from(subsetAxiom of (x := S, y := X)) thenHave(in(z, S) ==> in(z, X)) by InstantiateForall(z) have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(lastStep, singleDef of (x := z)) - // have(in(z, S) /\ forall(a, in(z, S) <=> in(singleton(z), V)) |- in(singleton(z), V)) - // have(in(z, S) ==> in(singleton(z), T)) by Tautology.from(sorry) - // have(in(singleton(z), singleton(S)) ==> in(singleton(z), T)) - // have(singleton(S) ⊆ T) - // have(union(singleton(S)) ∈ T) - // have(S ∈ T) - sorry + have(in(singleton(z), singletonSets(S)) ==> in(singleton(z), T)) by Tautology.from(lastStep, singletonSetsMembership of (x := z)) + + have(((t === singleton(z)) /\ in(z, S)) |- in(t, singletonSets(S)) ==> in(t, T)) by Tautology.from( + lastStep, + replaceEqualityContainsLeft of (x := t, y := singleton(z), z := singletonSets(S)), + replaceEqualityContainsLeft of (x := t, y := singleton(z), z := T) + ) + thenHave(exists(z, (t === singleton(z)) /\ in(z, S)) |- in(t, singletonSets(S)) ==> in(t, T)) by LeftExists + have(in(t, singletonSets(S)) ==> in(t, T)) by Tautology.from(lastStep, singletonSetsMembershipRaw of (x := z)) + thenHave(forall(t, in(t, singletonSets(S)) ==> in(t, T))) by RightForall + have(singletonSets(S) ⊆ T) by Tautology.from(lastStep, subsetAxiom of (x := singletonSets(S), y := T)) + have(union(singletonSets(S)) ∈ T) by Tautology.from(lastStep, contUnion) + have(thesis) by Tautology.from(lastStep, unionSingletonSets, replaceEqualityContainsLeft of (x := union(singletonSets(S)), y := S, z := T)) } have(in(S, powerSet(X)) ==> in(S, T)) by Tautology.from(lastStep, powerAxiom of (x := S, y := X)) thenHave(forall(S, in(S, powerSet(X)) ==> in(S, T))) by RightForall From f9567278efc19e9ea89d500d5c6c00aba6705985 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Fri, 13 Dec 2024 15:14:25 +0100 Subject: [PATCH 25/38] Almost finish Heine's theorem --- .../scala/lisa/maths/topology/Instances.scala | 280 ++++++++++++++++-- 1 file changed, 251 insertions(+), 29 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index d6d371be3..9bd6a3684 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -240,22 +240,39 @@ object Instances extends lisa.Main { // have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := X, y := preimage(f, X, Y, Y))) } - inline def preimagesFormula = x ∈ s <=> (x ∈ X /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) + inline def preimagesFormula = x ∈ s <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) val preimagesUniqueness = Theorem( (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, preimagesFormula)) ) { - have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(X, lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) + have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(powerSet(X), lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) thenHave(thesis) by Weakening } val preimages = DEF(f, X, Y, A) --> TheConditional(s, forall(x, preimagesFormula))(preimagesUniqueness) - /*val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A))) { + val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) { assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) - have(forall(x, x ∈ unionPreimage(f, X, Y, A) <=> (x ∈ X /\ app(f, x) ∈ union(A)))) by InstantiateForall(unionPreimage(f, X, Y, A))(unionPreimage.definition) + have(forall(x, x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))))) by InstantiateForall(preimages(f, X, Y, A))(preimages.definition) thenHave(thesis) by InstantiateForall(x) - }*/ + } + + inline def directImagesFormula = y ∈ s <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))) + + val directImagesUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X)) |- ∃!(s, forall(y, directImagesFormula)) + ) { + have(∃!(s, forall(y, directImagesFormula))) by UniqueComprehension(powerSet(Y), lambda(y, ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) + thenHave(thesis) by Weakening + } + + val directImages = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImagesFormula))(directImagesUniqueness) + + val directImagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(X)) |- y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) { + assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(X)) + have(forall(y, y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))))) by InstantiateForall(directImages(f, X, Y, A))(directImages.definition) + thenHave(thesis) by InstantiateForall(y) + } val preimageSetUnion = Theorem( functionFrom(f, X, Y) /\ @@ -326,8 +343,8 @@ object Instances extends lisa.Main { } val preimageUnionThm = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- - preimage(f, X, Y, union(A)) === union(preimages(f, X, Y, A)) + (functionFrom(f, X, Y), B ⊆ powerSet(Y)) |- + preimage(f, X, Y, union(B)) === union(preimages(f, X, Y, B)) ) { sorry } @@ -898,26 +915,68 @@ object Instances extends lisa.Main { openCover(X, T, O) ==> ∃( O2, // Another subcovering - subset(O2, O) /\ - openCover(X, T, O2) /\ - finite(O2) + subset(O2, O) /\ cover(X, O2) /\ finite(O2) ) ) + val coverDirectImage = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) |- cover(directImage(f, X, Y, X), directImages(f, X, Y, A)) + ) { + assume(functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) + + sorry + } + + /* Any subset of an open cover is an open cover */ + val subsetOpenCover = Theorem( + (openCover(X, T, O), subset(O2, O), cover(X, O2)) |- openCover(X, T, O2) + ) { + assume(openCover(X, T, O), O2 ⊆ O, cover(X, O2)) + + have(thesis) by Tautology.from( + openCover.definition of (O := O2), + openCover.definition, + subsetTransitivity of (a := O2, b := O, c := T) + ) + } + + /* The preimages of some set in P(Y) are in P(X) */ + val preimagesInPowerSet = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- preimages(f, X, Y, A) ⊆ powerSet(X) + ) { + assume(functionFrom(f, X, Y), A ⊆ powerSet(Y)) + + have(x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X)) by Tautology.from( + preimagesMembership of (A := A, x := x) + ) + thenHave(forall(x, x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimages(f, X, Y, A), y := powerSet(X))) + } + + /* The set of direct images is finite */ + val directImageFinite = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X), finite(A)) |- finite(directImages(f, X, Y, A)) + ) { + assume(functionFrom(f, X, Y), A ⊆ powerSet(X), finite(A)) + // TODO: Needs to have a notion of finiteness to complete the proof + // Normally it should just be because there is a bijection between directImages(f, X, Y, A) and A, and A is finite + sorry + } + val heineBorelThm = Theorem((compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- compact(Y, T2)) { assume(compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) - val xIsCompact = have(forall(O, openCover(X, T1, O) ==> ∃(O2, subset(O2, O) /\ openCover(X, T1, O2) /\ finite(O2)))) by Tautology.from( + val xIsCompact = have(forall(O, openCover(X, T1, O) ==> ∃(O2, subset(O2, O) /\ cover(X, O2) /\ finite(O2)))) by Tautology.from( compact.definition of (T := T1) ) val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) - have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { + have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) subproof { assume(openCover(Y, T2, O)) have(forall(O, (O ⊆ T2) ==> (union(O) ∈ T2))) by Tautology.from( @@ -931,31 +990,99 @@ object Instances extends lisa.Main { lastStep ) + val oInPowerSet = have(O ⊆ powerSet(Y)) by Tautology.from( + openCover.definition of (X := Y, T := T2), + yIsTop, + topology.definition of (X := Y, T := T2), + setOfSubsets.definition of (X := Y, T := T2), + subsetTransitivity of (a := O, b := T2, c := powerSet(Y)) + ) + val unionOSubsetY = have(union(O) ⊆ Y) by Tautology.from( + oInPowerSet, + subsetClosedUnion of (x := O, y := Y) + ) + // We have an open cover of X, that's preimages(f, X, Y, O) val isOpenCover = have(openCover(X, T1, preimages(f, X, Y, O))) subproof { // Firstly, it's a cover val isCover = have(cover(X, preimages(f, X, Y, O))) subproof { have(o ∈ preimages(f, X, Y, O) ==> o ⊆ X) subproof { - sorry + assume(o ∈ preimages(f, X, Y, O)) + have(o ∈ powerSet(X)) by Tautology.from( + preimagesMembership of (A := O, x := o), + fIsFunction, + oInPowerSet + ) + have(o ⊆ X) by Tautology.from(lastStep, powerAxiom of (x := o, y := X)) } val firstPart = thenHave(∀(o, o ∈ preimages(f, X, Y, O) ==> o ⊆ X)) by RightForall - val secondPart = have(X ⊆ union(preimages(f, X, Y, O))) subproof { - sorry + + // The covering part + have(x ∈ X ==> x ∈ union(preimages(f, X, Y, O))) subproof { + assume(x ∈ X) + // Function application + have(app(f, x) ∈ Y) by Tautology.from( + fIsFunction, + lastStep, + functionFromApplication of (x := X, y := Y, a := x), + functionFrom.definition of (x := X, y := Y) + ) + // Since Y is covered by O + have(app(f, x) ∈ union(O)) by Tautology.from( + lastStep, + openCover.definition of (X := Y, T := T2), + cover.definition of (X := Y), + subsetTactic of (x := Y, y := union(O), z := app(f, x)) + ) + // That's the definition of being in the preimage! + have(x ∈ preimage(f, X, Y, union(O))) by Tautology.from( + lastStep, + preimageMembership of (B := union(O)), + fIsFunction, + unionOSubsetY + ) + // use that preimage(f, X, Y, union(O)) ⊆ union(preimages(f, X, Y, O)) + have(x ∈ union(preimages(f, X, Y, O))) by Tautology.from( + lastStep, + preimageUnionThm of (B := O), + replaceEqualityContainsRight of (x := preimage(f, X, Y, union(O)), y := union(preimages(f, X, Y, O)), z := x), + fIsFunction, + oInPowerSet + ) } + thenHave(forall(x, x ∈ X ==> x ∈ union(preimages(f, X, Y, O)))) by RightForall + val secondPart = have(subset(X, union(preimages(f, X, Y, O)))) by Tautology.from( + lastStep, + subsetAxiom of (x := X, y := union(preimages(f, X, Y, O))) + ) + have(thesis) by Tautology.from(firstPart, secondPart, cover.definition of (O := preimages(f, X, Y, O))) } // Also, its elements are open have(z ∈ preimages(f, X, Y, O) ==> z ∈ T1) subproof { assume(z ∈ preimages(f, X, Y, O)) - // have(exists(o, o ∈ O /\ z == preimage(f, X, Y, o))) by - /*have(O ⊆ powerSet(Y) |- z ∈ preimage(f, X, Y, union(O))) by Tautology.from( + val existsa = have(∃(a, a ∈ O /\ (z === preimage(f, X, Y, a)))) by Tautology.from( + lastStep, + preimagesMembership of (A := O, x := z), fIsFunction, - preimageUnionThm of (A := O), - replaceEqualityContainsRight of (x := preimage(f, X, Y, union(O)), y := preimages(f, X, Y, O)), - lastStep - )*/ - sorry + oInPowerSet + ) + have(a ∈ O /\ (z === preimage(f, X, Y, a)) |- z ∈ T1) subproof { + assume(a ∈ O /\ (z === preimage(f, X, Y, a))) + val aInT2 = have(a ∈ T2) by Tautology.from( + openCover.definition of (X := Y, T := T2), + subsetTactic of (x := O, y := T2, z := a) + ) + have(a ∈ T2 ==> preimage(f, X, Y, a) ∈ T1) by InstantiateForall(a)(isContinuous) + have(preimage(f, X, Y, a) ∈ T1) by Tautology.from( + aInT2, + lastStep + ) + have(z ∈ T1) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := preimage(f, X, Y, a), z := T1)) + } + thenHave(∃(a, a ∈ O /\ (z === preimage(f, X, Y, a))) |- z ∈ T1) by LeftExists + have(thesis) by Tautology.from(existsa, lastStep) } thenHave(forall(z, z ∈ preimages(f, X, Y, O) ==> z ∈ T1)) by RightForall val isOpenSubset = have(preimages(f, X, Y, O) ⊆ T1) by Tautology.from( @@ -967,22 +1094,117 @@ object Instances extends lisa.Main { } // Whence the existence of a finite subcover O3 - have(openCover(X, T1, preimages(f, X, Y, O)) ==> ∃(O3, subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by InstantiateForall(preimages(f, X, Y, O))( + have(openCover(X, T1, preimages(f, X, Y, O)) ==> ∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3))) by InstantiateForall(preimages(f, X, Y, O))( xIsCompact ) - val existsO3 = have(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) + val existsO3 = have(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) + + have( + O3 ⊆ preimages(f, X, Y, O) /\ cover(X, O3) /\ finite(O3) + |- + subset(directImages(f, X, Y, O3), O) /\ cover(Y, directImages(f, X, Y, O3)) /\ finite(directImages(f, X, Y, O3)) + ) subproof { + assume(O3 ⊆ preimages(f, X, Y, O), cover(X, O3), finite(O3)) + + val o3InPowerSet = have(O3 ⊆ powerSet(X)) subproof { + have(preimages(f, X, Y, O) ⊆ powerSet(X)) by Tautology.from( + fIsFunction, + oInPowerSet, + preimagesInPowerSet of (A := O) + ) + have(thesis) by Tautology.from(lastStep, subsetTransitivity of (a := O3, b := preimages(f, X, Y, O), c := powerSet(X))) + } + + // So it's a subset + have(z ∈ directImages(f, X, Y, O3) ==> z ∈ O) subproof { + assume(z ∈ directImages(f, X, Y, O3)) + + have(z ∈ powerSet(Y) /\ ∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a)))) by Tautology.from( + directImagesMembership of (A := O3, y := z), + fIsFunction, + o3InPowerSet + ) + + have(a ∈ O3 /\ (z === directImage(f, X, Y, a)) |- z ∈ O) subproof { + assume(a ∈ O3, z === directImage(f, X, Y, a)) + val aInPreimages = have(a ∈ preimages(f, X, Y, O)) by Tautology.from( + lastStep, + subsetTactic of (x := O3, y := preimages(f, X, Y, O), z := a) + ) + have(b ∈ O /\ (a === preimage(f, X, Y, b)) |- directImage(f, X, Y, a) ∈ O) subproof { + assume(b ∈ O, a === preimage(f, X, Y, b)) + have(b ⊆ Y) by Tautology.from( + oInPowerSet, + subsetTactic of (z := b, x := O, y := powerSet(Y)), + powerAxiom of (x := b, y := Y) + ) + val statement = have(directImage(f, X, Y, preimage(f, X, Y, b)) === b) by Tautology.from( + lastStep, + directImagePreimageSurjective of (A := b), + fIsFunction + ) + thenHave(directImage(f, X, Y, a) === b) by RightSubstEq.withParametersSimple(List((a, preimage(f, X, Y, b))), lambda(x, directImage(f, X, Y, x) === b)) + have(thesis) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := directImage(f, X, Y, a), y := b, z := O)) + } + thenHave(exists(b, b ∈ O /\ (a === preimage(f, X, Y, b))) |- directImage(f, X, Y, a) ∈ O) by LeftExists + // use that (functionFrom(f, X, Y), O ⊆ powerSet(Y)) |- a ∈ preimages(f, X, Y, O) ==> (∃(b, b ∈ O /\ (a === preimage(f, X, Y, b))))) + have(directImage(f, X, Y, a) ∈ O) by Tautology.from(lastStep, aInPreimages, preimagesMembership of (A := O, x := a), fIsFunction, oInPowerSet) + // Conclude since z === directImage(f, X, Y, a) + have(z ∈ O) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := directImage(f, X, Y, a), z := O)) + } + + thenHave(∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a))) |- z ∈ O) by LeftExists - have(subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) subproof { - assume(subset(O3, preimages(f, X, Y, O)), openCover(X, T1, O3), finite(O3)) + // Since z is in directImages, then we precisely have ∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a))) by `directImagesMembership` + have(thesis) by Tautology.from(lastStep, directImagesMembership of (A := O3, y := z), fIsFunction, o3InPowerSet) + } + thenHave(forall(z, z ∈ directImages(f, X, Y, O3) ==> z ∈ O)) by RightForall + val isSubset = have(directImages(f, X, Y, O3) ⊆ O) by Tautology.from(lastStep, subsetAxiom of (x := directImages(f, X, Y, O3), y := O)) + + // That is also covering Y + // We use that f is surjective to get that directImage(f, X, Y, X) = Y + val replacement = have(directImage(f, X, Y, X) === Y) by Tautology.from(imageSurjective, fIsFunction) + val coveringStatement = have(cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) by Tautology.from( + coverDirectImage of (A := O3), + fIsFunction, + o3InPowerSet + ) + have( + ((directImage(f, X, Y, X) === Y), cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) + |- cover(Y, directImages(f, X, Y, O3)) + ) subproof { + have( + ((directImage(f, X, Y, X) === Y), cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) + |- cover(directImage(f, X, Y, X), directImages(f, X, Y, O3)) + ) by Tautology + thenHave(thesis) by RightSubstEq.withParametersSimple(List((directImage(f, X, Y, X), Y)), lambda(x, cover(x, directImages(f, X, Y, O3)))) + } + val covering = have(cover(Y, directImages(f, X, Y, O3))) by Tautology.from(lastStep, coveringStatement, replacement) + + // Finally it's finite since O3 is + val finiteCover = have(finite(directImages(f, X, Y, O3))) by Tautology.from( + directImageFinite of (A := O3), + fIsFunction, + o3InPowerSet + ) + + have(thesis) by Tautology.from(isSubset, finiteCover, covering) + } + + have( + subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3) + |- + ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)) + ) subproof { sorry } // Concluding - thenHave(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ openCover(X, T1, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by LeftExists + thenHave(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by LeftExists have(thesis) by Tautology.from(lastStep, existsO3) } - thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2))) by Tautology - val yIsCompact = thenHave(forall(O, openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ openCover(Y, T2, O2) /\ finite(O2)))) by RightForall + thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by Tautology + val yIsCompact = thenHave(forall(O, openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)))) by RightForall have(thesis) by Tautology.from(yIsCompact, yIsTop, compact.definition of (X := Y, T := T2)) } From f71404185ad8e299c12f710122b190ce1d773a4e Mon Sep 17 00:00:00 2001 From: Dobrin Bashev Date: Fri, 13 Dec 2024 19:06:56 +0100 Subject: [PATCH 26/38] Add proof of union(singletonSets(S)) === S --- .../lisa/maths/topology/SingletonSet.scala | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala index cb02dc3ee..16f7cdfb5 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala @@ -159,7 +159,44 @@ object SingletonSet extends lisa.Main { } val unionSingletonSets = Theorem(union(singletonSets(S)) === S) { - sorry + + val elementInSingleton = have(in(x, singleton(x))) subproof { + have(thesis) by Tautology.from(pairAxiom of (x := x, y := x, z := x)) + } + + val singletonSetsDefInstantiated = have(in(z, singletonSets(S)) <=> exists(x, in(x, S) /\ (z === singleton(x)))) subproof { + have((singletonSets(S) === singletonSets(S)) <=> ∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by + InstantiateForall(singletonSets(S))(singletonSets.definition) + thenHave(∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by Tautology + thenHave(thesis) by InstantiateForall(z) + } + + val fwd = have(in(x, union(singletonSets(S))) |- in(x, S)) subproof { + have(in(z, S) |- in(z, S)) by Restate + val introduceZ = thenHave((in(z, S), (x === z)) |- in(x, S)) by Substitution.ApplyRules(x === z) + have((in(z, S), in(x, singleton(z))) |- in(x, S)) by Tautology.from(introduceZ, singletonHasNoExtraElements of (y := x, x := z)) + thenHave((in(z, S), in(x, y), (y === singleton(z))) |- in(x, S)) by Substitution.ApplyRules(y === singleton(z)) + thenHave((in(z, S) /\ (y === singleton(z)), in(x, y)) |- in(x, S)) by Tautology + thenHave((exists(z, in(z, S) /\ (y === singleton(z))), in(x, y)) |- in(x, S)) by LeftExists + val existsZ = thenHave(exists(z, in(z, S) /\ (y === singleton(z))) /\ in(x, y) |- in(x, S)) by Tautology + have(in(y, singletonSets(S)) /\ in(x, y) |- in(x, S)) by Tautology.from(existsZ, singletonSetsDefInstantiated of (x := z, z := y)) + val existsY = thenHave(exists(y, in(y, singletonSets(S)) /\ in(x, y)) |- in(x, S)) by LeftExists + have(thesis) by Tautology.from(existsY, unionAxiom of (z := x, x := singletonSets(S))) + } + + val bwd = have(in(x, S) |- in(x, union(singletonSets(S)))) subproof { + assume(in(x, S)) + have(in(x, S) /\ (singleton(x) === singleton(x))) by Restate + val existsY = thenHave(exists(y, in(y, S) /\ (singleton(x) === singleton(y)))) by RightExists + have(in(singleton(x), singletonSets(S)) /\ in(x, singleton(x))) by + Tautology.from(existsY, singletonSetsDefInstantiated of (x := y, z := singleton(x)), elementInSingleton) + val prepForUnion = thenHave(exists(y, in(y, singletonSets(S)) /\ in(x, y))) by RightExists + have(thesis) by Tautology.from(prepForUnion, unionAxiom of (z := x, x := singletonSets(S))) + } + + have(in(x, union(singletonSets(S))) <=> in(x, S)) by Tautology.from(fwd, bwd) + val prepForExt = thenHave(forall(x, in(x, union(singletonSets(S))) <=> in(x, S))) by RightForall + have(thesis) by Tautology.from(prepForExt, extensionalityAxiom of (z := x, x := union(singletonSets(S)), y := S)) } val ifContainsSingletonIsDiscrete = Theorem( From 4d5c150068fc48ba22491d70615d6b85ead51cab Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Sun, 15 Dec 2024 19:42:00 +0100 Subject: [PATCH 27/38] topology cleanup --- .../maths/topology/DiscreteTopology.scala | 180 ++++++++++++++++++ .../lisa/maths/topology/SingletonSet.scala | 37 ++-- 2 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 lisa-topology/src/main/scala/lisa/maths/topology/DiscreteTopology.scala diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/DiscreteTopology.scala b/lisa-topology/src/main/scala/lisa/maths/topology/DiscreteTopology.scala new file mode 100644 index 000000000..fba614a79 --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/topology/DiscreteTopology.scala @@ -0,0 +1,180 @@ +package lisa.maths.topology + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* + +import lisa.automation.kernel.CommonTactics.Definition + +import lisa.maths.topology.Topology.* +import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.SetTheoryBasics.* +import lisa.automation.kernel.CommonTactics.* + +object DiscreteTopology extends lisa.Main { + import lisa.maths.settheory.SetTheory.* + // var defs + private val x, y, z, a, b, c, t = variable + private val X, T = variable + private val A, B, Y = variable + + // A discrete Topology is is where T contains all possible subsets of X from Def 1.1.6 in the book + val discreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === powerSet(X)) + + // Proof that a discrite Topology is actually a topology (satisfies all conditions of a topology) + val discreteIsTopology = Theorem( + discreteTopology(X, T) |- topology(X, T) + ) { + assume(discreteTopology(X, T)) + val discreteDef = have(nonEmpty(X) /\ (T === powerSet(X))) by Tautology.from(discreteTopology.definition) + val ext = have(forall(z, in(z, T) <=> in(z, powerSet(X)))) by Tautology.from(extensionalityAxiom of (x := T, y := powerSet(X)), discreteDef) + + val isSub = have(setOfSubsets(X, T)) by Tautology.from(equalityBySubset of (x := T, y := powerSet(X)), discreteDef, setOfSubsets.definition) + val contEx = have(containsExtremes(X, T)) subproof { + val a1 = have(in(∅, T) <=> in(∅, powerSet(X))) by InstantiateForall(∅)(ext) + val a2 = have(∅ ∈ powerSet(X)) by Tautology.from(powerAxiom of (x := ∅, y := X), emptySetIsASubset of (x := X)) + val hasEmpty = have(∅ ∈ T) by Tautology.from(a1, a2) + + have(in(X, T) <=> in(X, powerSet(X))) by InstantiateForall(X)(ext) + have(X ∈ T) by Tautology.from(lastStep, elemInItsPowerSet of (x := X)) + have(thesis) by Tautology.from(lastStep, hasEmpty, containsExtremes.definition) + } + val contUn = have(containsUnion(T)) subproof { + have((Y ⊆ T) |- (union(Y) ∈ T)) subproof { + assume(Y ⊆ T) + have(Y ⊆ powerSet(X)) by Tautology.from(discreteDef, equalityBySubset of (x := T, y := powerSet(X)), subsetTransitivity of (a := Y, b := T, c := powerSet(X))) + have(union(Y) ∈ powerSet(X)) by Tautology.from(lastStep, subsetClosedUnion of (x := Y, y := X), powerAxiom of (x := union(Y), y := X)) + have(thesis) by Tautology.from(lastStep, discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := union(Y))) + } + thenHave((Y ⊆ T) ==> (union(Y) ∈ T)) by Tautology + thenHave(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsUnion.definition) + } + val contInt = have(containsIntersection(T)) subproof { + have((A ∈ T, B ∈ T) |- (A ∩ B ∈ T)) subproof { + assume((A ∈ T), (B ∈ T)) + val first = have(A ⊆ X) by Tautology.from(discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := A), powerAxiom of (x := A, y := X)) + val second = have(B ⊆ X) by Tautology.from(discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := B), powerAxiom of (x := B, y := X)) + have(A ∩ B ∈ powerSet(X)) by Tautology.from(first, second, subsetClosedIntersection of (x := A, y := B, z := X), powerAxiom of (x := A ∩ B, y := X)) + have(thesis) by Tautology.from(lastStep, discreteDef, replaceEqualityContainsRight of (x := T, y := powerSet(X), z := A ∩ B)) + } + have((A ∈ T /\ B ∈ T) ==> (A ∩ B ∈ T)) by Tautology.from(lastStep, containsIntersection.definition) + thenHave(forall(B, (A ∈ T /\ B ∈ T) ==> (A ∩ B ∈ T))) by RightForall + thenHave(forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> (A ∩ B ∈ T)))) by RightForall + have(thesis) by Tautology.from(lastStep, containsIntersection.definition) + } + + have(thesis) by Tautology.from(discreteDef, isSub, contEx, contUn, contInt, topology.definition) + } + + // A indiscrete Topology is is where T contains only the empty set and X. Therefore the smallest possible topology for a given X. From Def 1.1.6 in the book + val indiscreteTopology = DEF(X, T) --> nonEmpty(X) /\ (T === unorderedPair(∅, X)) + + // proof that the indiscrete Topology is acutally a topology + val indiscreteIsTopology = Theorem( + indiscreteTopology(X, T) ==> topology(X, T) + ) { + assume(indiscreteTopology(X, T)) + val indiscreteDef = have(nonEmpty(X) /\ (T === unorderedPair(∅, X))) by Tautology.from(indiscreteTopology.definition) + val emptySubs = have(∅ ∈ powerSet(X)) by Tautology.from(emptySetIsASubset of (x := X), powerAxiom of (x := emptySet, y := X)) + val fullSubs = have(X ∈ powerSet(X)) by Tautology.from(elemInItsPowerSet of (x := X)) + + val isSub = have(setOfSubsets(X, T)) subproof { + have(in(unorderedPair(∅, X), powerSet(powerSet(X)))) by Tautology.from(emptySubs, fullSubs, unorderedPairInPowerSet of (x := powerSet(X), a := emptySet, b := X)) + have(unorderedPair(∅, X) ⊆ powerSet(X)) by Tautology.from(lastStep, powerAxiom of (x := unorderedPair(∅, X), y := powerSet(X))) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualitySubsetLeft of (x := unorderedPair(∅, X), y := T, z := powerSet(X)), setOfSubsets.definition) + } + + val contEx = have(containsExtremes(X, T)) subproof { + val a = have(X ∈ T) by Tautology.from(pairAxiom of (x := emptySet, y := X, z := X), indiscreteDef, replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := X)) + val b = have(∅ ∈ T) by Tautology.from( + pairAxiom of (x := emptySet, y := X, z := emptySet), + indiscreteDef, + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := emptySet) + ) + have(thesis) by Tautology.from(a, b, containsExtremes.definition) + } + + val contUn = have(containsUnion(T)) subproof { + have((Y ⊆ T) |- union(Y) ∈ T) subproof { + assume(Y ⊆ T) + have(Y ⊆ unorderedPair(∅, X)) by Tautology.from(indiscreteDef, replaceEqualitySubsetRight of (x := T, y := unorderedPair(∅, X), z := Y)) + have(forall(z, in(z, Y) ==> in(z, unorderedPair(∅, X)))) by Tautology.from(lastStep, subsetAxiom of (x := Y, y := unorderedPair(∅, X))) + thenHave(in(z, Y) ==> in(z, unorderedPair(∅, X))) by InstantiateForall(z) + have(in(z, Y) |- ((z === ∅) \/ (z === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X)) + thenHave((in(z, Y) /\ in(a, z)) |- (((z === ∅) \/ (z === X)) /\ in(a, z))) by Tautology + thenHave((in(z, Y) /\ in(a, z)) |- ((((z === ∅) /\ in(a, z))) \/ ((z === X) /\ in(a, z)))) by Tautology + have((in(z, Y) /\ in(a, z)) |- (in(a, ∅) \/ (in(a, X) /\ (z === X)))) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := z, y := emptySet, z := a), + replaceEqualityContainsRight of (x := z, y := X, z := a) + ) + have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ (z === X) /\ in(z, Y))) by Tautology.from(lastStep, emptySetAxiom of (x := a)) + have((in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := X, z := Y)) + thenHave(exists(z, in(z, Y) /\ in(a, z)) |- (in(a, X) /\ in(X, Y))) by LeftExists + val before = have(in(a, union(Y)) ==> (in(a, X) /\ in(X, Y))) by Tautology.from(lastStep, unionAxiom of (z := a, x := Y, y := z), emptySetAxiom of (x := a)) + thenHave(in(a, union(Y)) ==> in(a, X)) by Tautology + val base = thenHave(forall(a, in(a, union(Y)) ==> in(a, X))) by RightForall + val cond1 = have(forall(a, !in(a, union(Y))) |- union(Y) === ∅) by Tautology.from(setWithNoElementsIsEmpty of (y := a, x := union(Y))) + val cond2 = have(exists(a, in(a, union(Y))) |- union(Y) === X) subproof { + val unionGrow = have(in(a, union(Y)) |- (X ⊆ union(Y))) by Tautology.from(before, unionDoesntShrink of (x := X, y := Y)) + have(in(a, union(Y)) |- (union(Y) === X)) by Tautology.from(base, unionGrow, subsetAxiom of (x := union(Y), y := X, z := a), equalityBySubset of (x := union(Y), y := X)) + thenHave(thesis) by LeftExists + } + have((union(Y) === ∅) \/ (union(Y) === X)) by Tautology.from(base, cond1, cond2) + have(union(Y) ∈ unorderedPair(∅, X)) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := union(Y))) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := union(Y))) + } + thenHave((Y ⊆ T) ==> (union(Y) ∈ T)) by Tautology + thenHave(forall(Y, (Y ⊆ T) ==> (union(Y) ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsUnion.definition) + } + + val contInt = have(containsIntersection(T)) subproof { + have((A ∈ T /\ B ∈ T) |- (A ∩ B ∈ T)) subproof { + assume((A ∈ T /\ B ∈ T)) + have(A ∈ unorderedPair(∅, X) /\ B ∈ unorderedPair(∅, X)) by Tautology.from( + indiscreteDef, + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := A), + replaceEqualityContainsRight of (x := T, y := unorderedPair(∅, X), z := B) + ) + val allPossibilities = + have(((A === ∅) \/ (A === X)) /\ ((B === ∅) \/ (B === X))) by Tautology.from(lastStep, pairAxiom of (x := emptySet, y := X, z := A), pairAxiom of (x := emptySet, y := X, z := B)) + val aEmpty = have((A === ∅) |- (A ∩ B) === ∅) subproof { + assume(A === emptySet) + have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) + have(!in(t, setIntersection(A, B))) by Tautology.from(lastStep, replaceEqualityContainsRight of (x := emptySet, y := A, z := t), emptySetAxiom of (x := t)) + thenHave(forall(t, !in(t, setIntersection(A, B)))) by RightForall + have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (y := t, x := setIntersection(A, B))) + } + val oneEmpty = have(((A === ∅) \/ (B === ∅)) |- ((A ∩ B) === ∅)) by Tautology.from( + aEmpty, + aEmpty of (A := B, B := A), + setIntersectionCommutativity of (x := A, y := B), + equalityTransitivity of (x := setIntersection(A, B), y := setIntersection(B, A), z := emptySet) + ) + val bothFull = have((A === X, B === X) |- A ∩ B === X) subproof { + assume(((A === X) /\ (B === X))) + have(in(t, setIntersection(A, B)) <=> (in(t, A) /\ in(t, B))) by Tautology.from(setIntersectionMembership of (x := A, y := B)) + have(in(t, setIntersection(A, B)) <=> in(t, X)) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := X, y := A, z := t), + replaceEqualityContainsRight of (x := X, y := B, z := t) + ) + thenHave(forall(t, in(t, setIntersection(A, B)) <=> in(t, X))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := setIntersection(A, B), y := X, z := t)) + } + have((A ∩ B === ∅) \/ (A ∩ B === X)) by Tautology.from(allPossibilities, oneEmpty, bothFull) + have(in(A ∩ B, unorderedPair(∅, X))) by Tautology.from(lastStep, pairAxiom of (z := setIntersection(A, B), x := emptySet, y := X)) + have(thesis) by Tautology.from(lastStep, indiscreteDef, replaceEqualityContainsRight of (x := unorderedPair(∅, X), y := T, z := setIntersection(A, B))) + } + thenHave((A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T) by Tautology + thenHave(forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T)) by RightForall + thenHave(forall(A, forall(B, (A ∈ T /\ B ∈ T) ==> A ∩ B ∈ T))) by RightForall + have(thesis) by Tautology.from(lastStep, containsIntersection.definition) + } + + have(thesis) by Tautology.from(indiscreteDef, isSub, contEx, contUn, contInt, topology.definition) + } + +} diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala index 16f7cdfb5..69eb946f3 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala @@ -160,43 +160,34 @@ object SingletonSet extends lisa.Main { val unionSingletonSets = Theorem(union(singletonSets(S)) === S) { - val elementInSingleton = have(in(x, singleton(x))) subproof { - have(thesis) by Tautology.from(pairAxiom of (x := x, y := x, z := x)) - } - - val singletonSetsDefInstantiated = have(in(z, singletonSets(S)) <=> exists(x, in(x, S) /\ (z === singleton(x)))) subproof { - have((singletonSets(S) === singletonSets(S)) <=> ∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by - InstantiateForall(singletonSets(S))(singletonSets.definition) - thenHave(∀(t, in(t, singletonSets(S)) <=> exists(x, in(x, S) /\ (t === singleton(x))))) by Tautology - thenHave(thesis) by InstantiateForall(z) - } + val elementInSingleton = have(in(x, singleton(x))) by Tautology.from(pairAxiom of (x := x, y := x, z := x)) val fwd = have(in(x, union(singletonSets(S))) |- in(x, S)) subproof { have(in(z, S) |- in(z, S)) by Restate - val introduceZ = thenHave((in(z, S), (x === z)) |- in(x, S)) by Substitution.ApplyRules(x === z) - have((in(z, S), in(x, singleton(z))) |- in(x, S)) by Tautology.from(introduceZ, singletonHasNoExtraElements of (y := x, x := z)) + thenHave((in(z, S), (x === z)) |- in(x, S)) by Substitution.ApplyRules(x === z) + have((in(z, S), in(x, singleton(z))) |- in(x, S)) by Tautology.from(lastStep, singletonHasNoExtraElements of (y := x, x := z)) thenHave((in(z, S), in(x, y), (y === singleton(z))) |- in(x, S)) by Substitution.ApplyRules(y === singleton(z)) thenHave((in(z, S) /\ (y === singleton(z)), in(x, y)) |- in(x, S)) by Tautology thenHave((exists(z, in(z, S) /\ (y === singleton(z))), in(x, y)) |- in(x, S)) by LeftExists - val existsZ = thenHave(exists(z, in(z, S) /\ (y === singleton(z))) /\ in(x, y) |- in(x, S)) by Tautology - have(in(y, singletonSets(S)) /\ in(x, y) |- in(x, S)) by Tautology.from(existsZ, singletonSetsDefInstantiated of (x := z, z := y)) - val existsY = thenHave(exists(y, in(y, singletonSets(S)) /\ in(x, y)) |- in(x, S)) by LeftExists - have(thesis) by Tautology.from(existsY, unionAxiom of (z := x, x := singletonSets(S))) + thenHave(exists(z, in(z, S) /\ (y === singleton(z))) /\ in(x, y) |- in(x, S)) by Tautology + have(in(y, singletonSets(S)) /\ in(x, y) |- in(x, S)) by Tautology.from(lastStep, singletonSetsMembershipRaw of (x := z, t := y)) + thenHave(exists(y, in(y, singletonSets(S)) /\ in(x, y)) |- in(x, S)) by LeftExists + have(thesis) by Tautology.from(lastStep, unionAxiom of (z := x, x := singletonSets(S))) } val bwd = have(in(x, S) |- in(x, union(singletonSets(S)))) subproof { assume(in(x, S)) have(in(x, S) /\ (singleton(x) === singleton(x))) by Restate - val existsY = thenHave(exists(y, in(y, S) /\ (singleton(x) === singleton(y)))) by RightExists - have(in(singleton(x), singletonSets(S)) /\ in(x, singleton(x))) by - Tautology.from(existsY, singletonSetsDefInstantiated of (x := y, z := singleton(x)), elementInSingleton) - val prepForUnion = thenHave(exists(y, in(y, singletonSets(S)) /\ in(x, y))) by RightExists - have(thesis) by Tautology.from(prepForUnion, unionAxiom of (z := x, x := singletonSets(S))) + thenHave(exists(y, in(y, S) /\ (singleton(x) === singleton(y)))) by RightExists + have(in(singleton(x), singletonSets(S)) /\ in(x, singleton(x))) by + Tautology.from(lastStep, singletonSetsMembershipRaw of (x := y, t := singleton(x)), elementInSingleton) + thenHave(exists(y, in(y, singletonSets(S)) /\ in(x, y))) by RightExists + have(thesis) by Tautology.from(lastStep, unionAxiom of (z := x, x := singletonSets(S))) } have(in(x, union(singletonSets(S))) <=> in(x, S)) by Tautology.from(fwd, bwd) - val prepForExt = thenHave(forall(x, in(x, union(singletonSets(S))) <=> in(x, S))) by RightForall - have(thesis) by Tautology.from(prepForExt, extensionalityAxiom of (z := x, x := union(singletonSets(S)), y := S)) + thenHave(forall(x, in(x, union(singletonSets(S))) <=> in(x, S))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (z := x, x := union(singletonSets(S)), y := S)) } val ifContainsSingletonIsDiscrete = Theorem( From 239839ef9f7962e6cdd42b64a553d3bdfe071b28 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Wed, 18 Dec 2024 15:57:53 +0100 Subject: [PATCH 28/38] Fix --- .../src/main/scala/lisa/maths/topology/SingletonSet.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala index 69eb946f3..7e5f3dd0d 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala @@ -5,6 +5,7 @@ import lisa.maths.Quantifiers.* import lisa.automation.kernel.CommonTactics.Definition +import lisa.maths.topology.DiscreteTopology.* import lisa.maths.topology.Topology.* import lisa.maths.topology.Instances.* import lisa.maths.settheory.* From 193175fadd69f9bec20c155811b63ce995fb7406 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Wed, 18 Dec 2024 15:58:17 +0100 Subject: [PATCH 29/38] Finish content of Heine's theorem --- .../src/main/scala/lisa/maths/topology/Instances.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala index 4368562ef..75f57837e 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala @@ -981,13 +981,11 @@ object Instances extends lisa.Main { have(thesis) by Tautology.from(isSubset, finiteCover, covering) } - have( + thenHave( subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)) - ) subproof { - sorry - } + ) by RightExists // Concluding thenHave(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by LeftExists From 8ea523a59f5e8c15c5b9f13cab4ff091aaf8516c Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Wed, 18 Dec 2024 16:34:18 +0100 Subject: [PATCH 30/38] Split Instances into several files --- .../settheory/functions/DirectPreimages.scala | 517 ++---------------- .../maths/settheory/functions/package.scala | 23 + .../lisa/maths/topology/Compactness.scala | 336 ++++++++++++ .../lisa/maths/topology/Continuity.scala | 145 +++++ .../lisa/maths/topology/SingletonSet.scala | 1 - 5 files changed, 535 insertions(+), 487 deletions(-) rename lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala => lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala (58%) create mode 100644 lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala create mode 100644 lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala similarity index 58% rename from lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala rename to lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala index 75f57837e..70bc69197 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Instances.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala @@ -1,26 +1,48 @@ -package lisa.maths.topology +package lisa.maths.settheory.functions +import lisa.automation.kernel.CommonTactics.Definition import lisa.automation.settheory.SetTheoryTactics.* import lisa.maths.Quantifiers.* - -import lisa.automation.kernel.CommonTactics.Definition - -import lisa.maths.topology.Topology.* -import lisa.maths.settheory.* -import lisa.maths.settheory.SetTheory.* import lisa.maths.settheory.SetTheoryBasics.* import lisa.automation.kernel.CommonTactics.* import lisa.maths.settheory.functions.Functionals.* import lisa.automation.settheory.SetTheoryTactics.UniqueComprehension import lisa.automation.settheory.SetTheoryTactics.TheConditional +import lisa.maths.settheory.SetTheory.* + +object DirectPreimages extends lisa.Main { -object Instances extends lisa.Main { - import lisa.maths.settheory.SetTheory.* // var defs private val x, y, z, a, b, c, t, p, f, r, s = variable private val X, T, T1, T2 = variable private val S, A, B, Y, o, O, O2, O3 = variable + /** + * Don't know why, but I need to paste it directly here otherwise we have a matcherror null error (from FunctionProperties) + */ + val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) + + /* Also copied the theorem (from FunctionProperties) */ + val surjectiveImpliesRangeIsCodomain = Theorem( + surjective(f, x, y) |- (y === functionRange(f)) + ) { + have(surjective(f, x, y) |- ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f)))) by Tautology.from(surjective.definition) + val surjDef = thenHave(surjective(f, x, y) |- in(b, y) ==> ∃(a, in(pair(a, b), f))) by InstantiateForall(b) + have(∀(t, in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f))))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) + val rangeDef = thenHave(in(b, functionRange(f)) <=> (∃(a, in(pair(a, b), f)))) by InstantiateForall(b) + + have(surjective(f, x, y) |- in(b, y) ==> in(b, functionRange(f))) by Tautology.from(surjDef, rangeDef) + thenHave(surjective(f, x, y) |- ∀(b, in(b, y) ==> in(b, functionRange(f)))) by RightForall + val surjsub = andThen(Substitution.applySubst(subsetAxiom of (x -> y, y -> functionRange(f)))) + + have((surjective(f, x, y), functionFrom(f, x, y)) |- subset(y, functionRange(f)) /\ subset(functionRange(f), y)) by RightAnd(surjsub, functionImpliesRangeSubsetOfCodomain) + val funceq = andThen(Substitution.applySubst(subsetEqualitySymmetry of (x -> y, y -> functionRange(f)))) + + val surjfunc = have(surjective(f, x, y) |- functionFrom(f, x, y)) by Tautology.from(surjective.definition) + + have(thesis) by Cut(surjfunc, funceq) + } + inline def directImageFormula = y ∈ s <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ A)) val directImageUniqueness = Theorem( @@ -133,23 +155,6 @@ object Instances extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, setUnion(A, B)), y := setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))))) } - /*inline def directImageUnionFormula = y ∈ s <=> y ∈ Y /\ ∃(A, y ∈ directImage(f, A)) - val directImageUnionUniqueness = Theorem( - functional(f) |- ∃!(s, ∀(y, directImageUnionFormula)) - ) { - have(∃!(s, ∀(y, directImageUnionFormula))) by UniqueComprehension(Y, lambda(y, ∃(A, y ∈ directImage(f, A)))) - thenHave(thesis) by Weakening - } - - val directImageUnion = DEF(f, A) --> TheConditional(s, forall(z, z ∈ s <=> ∃(A, z ∈ directImage(f, A))))(directImageUnionUniqueness) - - val directImageUnionThm = Theorem( - functional(f) /\ forall(A, A ∈ T ==> subset(A, X)) |- - union(directImage(f, T)) === directImage(f, union(A)) - ) { - sorry - }*/ - inline def preimageFormula = x ∈ s <=> (x ∈ X /\ app(f, x) ∈ B) val preimageUniqueness = Theorem( @@ -341,32 +346,6 @@ object Instances extends lisa.Main { sorry } - // Couldn't import surjectivity from FunctionProperties without an error, so here it is - val surjective = DEF(f, x, y) --> functionFrom(f, x, y) /\ ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f))) - - /** - * Theorem --- if a function is [[surjective]], its range is equal to its codomain. - */ - val surjectiveImpliesRangeIsCodomain = Theorem( - surjective(f, x, y) |- (y === functionRange(f)) - ) { - have(surjective(f, x, y) |- ∀(b, in(b, y) ==> ∃(a, in(pair(a, b), f)))) by Tautology.from(surjective.definition) - val surjDef = thenHave(surjective(f, x, y) |- in(b, y) ==> ∃(a, in(pair(a, b), f))) by InstantiateForall(b) - have(∀(t, in(t, functionRange(f)) <=> (∃(a, in(pair(a, t), f))))) by InstantiateForall(functionRange(f))(functionRange.definition of (r -> f)) - val rangeDef = thenHave(in(b, functionRange(f)) <=> (∃(a, in(pair(a, b), f)))) by InstantiateForall(b) - - have(surjective(f, x, y) |- in(b, y) ==> in(b, functionRange(f))) by Tautology.from(surjDef, rangeDef) - thenHave(surjective(f, x, y) |- ∀(b, in(b, y) ==> in(b, functionRange(f)))) by RightForall - val surjsub = andThen(Substitution.applySubst(subsetAxiom of (x -> y, y -> functionRange(f)))) - - have((surjective(f, x, y), functionFrom(f, x, y)) |- subset(y, functionRange(f)) /\ subset(functionRange(f), y)) by RightAnd(surjsub, functionImpliesRangeSubsetOfCodomain) - val funceq = andThen(Substitution.applySubst(subsetEqualitySymmetry of (x -> y, y -> functionRange(f)))) - - val surjfunc = have(surjective(f, x, y) |- functionFrom(f, x, y)) by Tautology.from(surjective.definition) - - have(thesis) by Cut(surjfunc, funceq) - } - val directImageEmptySet = Theorem( (functionFrom(f, X, Y)) |- directImage(f, X, Y, emptySet) === emptySet @@ -562,438 +541,4 @@ object Instances extends lisa.Main { equalityTransitivity of (x := Y, y := functionRange(f), z := directImage(f, X, Y, X)) ) } - - // ------------------- - // Mappings - // ------------------- - val mapping = DEF(f, X, T1, Y, T2) --> - (functionFrom(f, X, Y) /\ topology(X, T1) /\ topology(Y, T2)) - - // ------------------- - // Continuity - // ------------------- - val continuous = DEF(f, X, T1, Y, T2) --> - (mapping(f, X, T1, Y, T2) /\ forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) - - // ------------------- - // Connectedness - // ------------------- - val clopen = DEF(X, T, A) --> ( - topology(X, T) /\ - A ∈ T /\ setDifference(X, A) ∈ T - ) - - val connectedTop = DEF(X, T) --> ( - topology(X, T) /\ - forall(A, clopen(X, T, A) ==> ((A === emptySet) \/ (A === X))) - ) - - // ------------------- - // Intermediate value theorem - // ------------------- - val intermediateValueThm = Theorem((connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- connectedTop(Y, T2)) { - assume(connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) - val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) - val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) - - val xIsConnected = have(forall(A, clopen(X, T1, A) ==> ((A === emptySet) \/ (A === X)))) by Tautology.from(connectedTop.definition of (T := T1)) - val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) - - val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) - - have(clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y))) subproof { - assume(clopen(Y, T2, A)) - - val aIsSubset = have(A ⊆ Y) subproof { - have(A ∈ T2) by Tautology.from(clopen.definition of (X := Y, T := T2)) - have(A ∈ powerSet(Y)) by Tautology.from( - lastStep, - yIsTop, - topology.definition of (X := Y, T := T2), - setOfSubsets.definition of (X := Y, T := T2), - subsetTactic of (x := T2, y := powerSet(Y), z := A) - ) - have(thesis) by Tautology.from(lastStep, powerAxiom of (x := A, y := Y)) - } - - val preimageA = have(A ∈ T2 ==> preimage(f, X, Y, A) ∈ T1) by InstantiateForall(A)(isContinuous) - val yIsClopen = have(A ∈ T2 /\ setDifference(Y, A) ∈ T2) by Tautology.from(clopen.definition of (X := Y, T := T2)) - val part1 = have(preimage(f, X, Y, A) ∈ T1) by Tautology.from(yIsClopen, preimageA) - val preimageYA = have(setDifference(Y, A) ∈ T2 ==> preimage(f, X, Y, setDifference(Y, A)) ∈ T1) by InstantiateForall(setDifference(Y, A))(isContinuous) - have(preimage(f, X, Y, setDifference(Y, A)) ∈ T1) by Tautology.from(yIsClopen, preimageYA) - val part2 = have(setDifference(X, preimage(f, X, Y, A)) ∈ T1) by Tautology.from( - lastStep, - aIsSubset, - fIsFunction, - preimageDifference, - replaceEqualityContainsLeft of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A)), z := T1) - ) - - // So f^-1(A) is clopen - val inverseIsClopen = have(clopen(X, T1, preimage(f, X, Y, A))) by Tautology.from( - xIsTop, - part1, - part2, - clopen.definition of (T := T1, A := preimage(f, X, Y, A)) - ) - - // Hence (f^-1(A) === emptySet) \/ (preimage(f, X, Y, A) === X) by connectedness of X - have(clopen(X, T1, preimage(f, X, Y, A)) ==> (preimage(f, X, Y, A) === emptySet) \/ (preimage(f, X, Y, A) === X)) by InstantiateForall(preimage(f, X, Y, A))(xIsConnected) - val preImageIsConnected = have((preimage(f, X, Y, A) === emptySet) \/ (preimage(f, X, Y, A) === X)) by Tautology.from( - lastStep, - inverseIsClopen - ) - - // Use the fact that f(emptyset)=emptyset, f(f^-1(A)) = A (by surjectivity), f(X) = Y (by surjectivity) to conclude - val firstCase = have(preimage(f, X, Y, A) === emptySet |- A === emptySet) subproof { - assume(preimage(f, X, Y, A) === emptySet) - have(thesis) by Tautology.from( - lastStep, - fIsFunction, - aIsSubset, - applyDirectImage of (A := preimage(f, X, Y, A), B := emptySet), - directImagePreimageSurjective, - directImageEmptySet, - equalityTransitivity of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := directImage(f, X, Y, emptySet), z := emptySet), - equalitySymmetry of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A), - equalityTransitivity of (x := A, y := directImage(f, X, Y, preimage(f, X, Y, A)), z := emptySet) - ) - } - - val secondCase = have(preimage(f, X, Y, A) === X |- A === Y) subproof { - assume(preimage(f, X, Y, A) === X) - have(thesis) by Tautology.from( - lastStep, - fIsFunction, - aIsSubset, - applyDirectImage of (A := preimage(f, X, Y, A), B := X), - directImagePreimageSurjective, - directImageX, - surjectiveImpliesRangeIsCodomain of (x := X, y := Y), - equalitySymmetry of (x := Y, y := functionRange(f)), - equalityTransitivity of (x := directImage(f, X, Y, X), y := functionRange(f), z := Y), - equalityTransitivity of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := directImage(f, X, Y, X), z := Y), - equalitySymmetry of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A), - equalityTransitivity of (x := A, y := directImage(f, X, Y, preimage(f, X, Y, A)), z := Y) - ) - } - - have(thesis) by Tautology.from(preImageIsConnected, firstCase, secondCase) - } - - val allClopen = thenHave(forall(A, clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y)))) by RightForall - have(connectedTop(Y, T2)) by Tautology.from(allClopen, yIsTop, connectedTop.definition of (X := Y, T := T2)) - } - - // ------------------- - // Compactness - // ------------------- - - val cover = DEF(X, O) --> - forall(o, in(o, O) ==> subset(o, X)) /\ - subset(X, union(O)) - - val openCover = DEF(X, T, O) --> - cover(X, O) /\ subset(O, T) - - val finite = DEF(X) --> (X === emptySet) // TODO - - val compact = DEF(X, T) --> - topology(X, T) /\ - forall( - O, - openCover(X, T, O) ==> - ∃( - O2, // Another subcovering - subset(O2, O) /\ cover(X, O2) /\ finite(O2) - ) - ) - - val coverDirectImage = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) |- cover(directImage(f, X, Y, X), directImages(f, X, Y, A)) - ) { - assume(functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) - - sorry - } - - /* Any subset of an open cover is an open cover */ - val subsetOpenCover = Theorem( - (openCover(X, T, O), subset(O2, O), cover(X, O2)) |- openCover(X, T, O2) - ) { - assume(openCover(X, T, O), O2 ⊆ O, cover(X, O2)) - - have(thesis) by Tautology.from( - openCover.definition of (O := O2), - openCover.definition, - subsetTransitivity of (a := O2, b := O, c := T) - ) - } - - /* The preimages of some set in P(Y) are in P(X) */ - val preimagesInPowerSet = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- preimages(f, X, Y, A) ⊆ powerSet(X) - ) { - assume(functionFrom(f, X, Y), A ⊆ powerSet(Y)) - - have(x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X)) by Tautology.from( - preimagesMembership of (A := A, x := x) - ) - thenHave(forall(x, x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X))) by RightForall - have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimages(f, X, Y, A), y := powerSet(X))) - } - - /* The set of direct images is finite */ - val directImageFinite = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(X), finite(A)) |- finite(directImages(f, X, Y, A)) - ) { - assume(functionFrom(f, X, Y), A ⊆ powerSet(X), finite(A)) - // TODO: Needs to have a notion of finiteness to complete the proof - // Normally it should just be because there is a bijection between directImages(f, X, Y, A) and A, and A is finite - sorry - } - - val heineBorelThm = Theorem((compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- compact(Y, T2)) { - assume(compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) - - val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) - val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) - - val xIsCompact = have(forall(O, openCover(X, T1, O) ==> ∃(O2, subset(O2, O) /\ cover(X, O2) /\ finite(O2)))) by Tautology.from( - compact.definition of (T := T1) - ) - val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) - - val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) - - have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) subproof { - assume(openCover(Y, T2, O)) - - have(forall(O, (O ⊆ T2) ==> (union(O) ∈ T2))) by Tautology.from( - containsUnion.definition of (T := T2), - yIsTop, - topology.definition of (X := Y, T := T2) - ) - thenHave(O ⊆ T2 ==> (union(O) ∈ T2)) by InstantiateForall(O) - val unionInT2 = have(union(O) ∈ T2) by Tautology.from( - openCover.definition of (X := Y, T := T2), - lastStep - ) - - val oInPowerSet = have(O ⊆ powerSet(Y)) by Tautology.from( - openCover.definition of (X := Y, T := T2), - yIsTop, - topology.definition of (X := Y, T := T2), - setOfSubsets.definition of (X := Y, T := T2), - subsetTransitivity of (a := O, b := T2, c := powerSet(Y)) - ) - val unionOSubsetY = have(union(O) ⊆ Y) by Tautology.from( - oInPowerSet, - subsetClosedUnion of (x := O, y := Y) - ) - - // We have an open cover of X, that's preimages(f, X, Y, O) - val isOpenCover = have(openCover(X, T1, preimages(f, X, Y, O))) subproof { - // Firstly, it's a cover - val isCover = have(cover(X, preimages(f, X, Y, O))) subproof { - have(o ∈ preimages(f, X, Y, O) ==> o ⊆ X) subproof { - assume(o ∈ preimages(f, X, Y, O)) - have(o ∈ powerSet(X)) by Tautology.from( - preimagesMembership of (A := O, x := o), - fIsFunction, - oInPowerSet - ) - have(o ⊆ X) by Tautology.from(lastStep, powerAxiom of (x := o, y := X)) - } - val firstPart = thenHave(∀(o, o ∈ preimages(f, X, Y, O) ==> o ⊆ X)) by RightForall - - // The covering part - have(x ∈ X ==> x ∈ union(preimages(f, X, Y, O))) subproof { - assume(x ∈ X) - // Function application - have(app(f, x) ∈ Y) by Tautology.from( - fIsFunction, - lastStep, - functionFromApplication of (x := X, y := Y, a := x), - functionFrom.definition of (x := X, y := Y) - ) - // Since Y is covered by O - have(app(f, x) ∈ union(O)) by Tautology.from( - lastStep, - openCover.definition of (X := Y, T := T2), - cover.definition of (X := Y), - subsetTactic of (x := Y, y := union(O), z := app(f, x)) - ) - // That's the definition of being in the preimage! - have(x ∈ preimage(f, X, Y, union(O))) by Tautology.from( - lastStep, - preimageMembership of (B := union(O)), - fIsFunction, - unionOSubsetY - ) - // use that preimage(f, X, Y, union(O)) ⊆ union(preimages(f, X, Y, O)) - have(x ∈ union(preimages(f, X, Y, O))) by Tautology.from( - lastStep, - preimageUnionThm of (B := O), - replaceEqualityContainsRight of (x := preimage(f, X, Y, union(O)), y := union(preimages(f, X, Y, O)), z := x), - fIsFunction, - oInPowerSet - ) - } - thenHave(forall(x, x ∈ X ==> x ∈ union(preimages(f, X, Y, O)))) by RightForall - val secondPart = have(subset(X, union(preimages(f, X, Y, O)))) by Tautology.from( - lastStep, - subsetAxiom of (x := X, y := union(preimages(f, X, Y, O))) - ) - - have(thesis) by Tautology.from(firstPart, secondPart, cover.definition of (O := preimages(f, X, Y, O))) - } - - // Also, its elements are open - have(z ∈ preimages(f, X, Y, O) ==> z ∈ T1) subproof { - assume(z ∈ preimages(f, X, Y, O)) - val existsa = have(∃(a, a ∈ O /\ (z === preimage(f, X, Y, a)))) by Tautology.from( - lastStep, - preimagesMembership of (A := O, x := z), - fIsFunction, - oInPowerSet - ) - have(a ∈ O /\ (z === preimage(f, X, Y, a)) |- z ∈ T1) subproof { - assume(a ∈ O /\ (z === preimage(f, X, Y, a))) - val aInT2 = have(a ∈ T2) by Tautology.from( - openCover.definition of (X := Y, T := T2), - subsetTactic of (x := O, y := T2, z := a) - ) - have(a ∈ T2 ==> preimage(f, X, Y, a) ∈ T1) by InstantiateForall(a)(isContinuous) - have(preimage(f, X, Y, a) ∈ T1) by Tautology.from( - aInT2, - lastStep - ) - have(z ∈ T1) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := preimage(f, X, Y, a), z := T1)) - } - thenHave(∃(a, a ∈ O /\ (z === preimage(f, X, Y, a))) |- z ∈ T1) by LeftExists - have(thesis) by Tautology.from(existsa, lastStep) - } - thenHave(forall(z, z ∈ preimages(f, X, Y, O) ==> z ∈ T1)) by RightForall - val isOpenSubset = have(preimages(f, X, Y, O) ⊆ T1) by Tautology.from( - subsetAxiom of (x := preimages(f, X, Y, O), y := T1), - lastStep - ) - - have(thesis) by Tautology.from(openCover.definition of (T := T1, O := preimages(f, X, Y, O)), isCover, isOpenSubset) - } - - // Whence the existence of a finite subcover O3 - have(openCover(X, T1, preimages(f, X, Y, O)) ==> ∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3))) by InstantiateForall(preimages(f, X, Y, O))( - xIsCompact - ) - val existsO3 = have(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) - - have( - O3 ⊆ preimages(f, X, Y, O) /\ cover(X, O3) /\ finite(O3) - |- - subset(directImages(f, X, Y, O3), O) /\ cover(Y, directImages(f, X, Y, O3)) /\ finite(directImages(f, X, Y, O3)) - ) subproof { - assume(O3 ⊆ preimages(f, X, Y, O), cover(X, O3), finite(O3)) - - val o3InPowerSet = have(O3 ⊆ powerSet(X)) subproof { - have(preimages(f, X, Y, O) ⊆ powerSet(X)) by Tautology.from( - fIsFunction, - oInPowerSet, - preimagesInPowerSet of (A := O) - ) - have(thesis) by Tautology.from(lastStep, subsetTransitivity of (a := O3, b := preimages(f, X, Y, O), c := powerSet(X))) - } - - // So it's a subset - have(z ∈ directImages(f, X, Y, O3) ==> z ∈ O) subproof { - assume(z ∈ directImages(f, X, Y, O3)) - - have(z ∈ powerSet(Y) /\ ∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a)))) by Tautology.from( - directImagesMembership of (A := O3, y := z), - fIsFunction, - o3InPowerSet - ) - - have(a ∈ O3 /\ (z === directImage(f, X, Y, a)) |- z ∈ O) subproof { - assume(a ∈ O3, z === directImage(f, X, Y, a)) - val aInPreimages = have(a ∈ preimages(f, X, Y, O)) by Tautology.from( - lastStep, - subsetTactic of (x := O3, y := preimages(f, X, Y, O), z := a) - ) - have(b ∈ O /\ (a === preimage(f, X, Y, b)) |- directImage(f, X, Y, a) ∈ O) subproof { - assume(b ∈ O, a === preimage(f, X, Y, b)) - have(b ⊆ Y) by Tautology.from( - oInPowerSet, - subsetTactic of (z := b, x := O, y := powerSet(Y)), - powerAxiom of (x := b, y := Y) - ) - val statement = have(directImage(f, X, Y, preimage(f, X, Y, b)) === b) by Tautology.from( - lastStep, - directImagePreimageSurjective of (A := b), - fIsFunction - ) - thenHave(directImage(f, X, Y, a) === b) by RightSubstEq.withParametersSimple(List((a, preimage(f, X, Y, b))), lambda(x, directImage(f, X, Y, x) === b)) - have(thesis) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := directImage(f, X, Y, a), y := b, z := O)) - } - thenHave(exists(b, b ∈ O /\ (a === preimage(f, X, Y, b))) |- directImage(f, X, Y, a) ∈ O) by LeftExists - // use that (functionFrom(f, X, Y), O ⊆ powerSet(Y)) |- a ∈ preimages(f, X, Y, O) ==> (∃(b, b ∈ O /\ (a === preimage(f, X, Y, b))))) - have(directImage(f, X, Y, a) ∈ O) by Tautology.from(lastStep, aInPreimages, preimagesMembership of (A := O, x := a), fIsFunction, oInPowerSet) - // Conclude since z === directImage(f, X, Y, a) - have(z ∈ O) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := directImage(f, X, Y, a), z := O)) - } - - thenHave(∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a))) |- z ∈ O) by LeftExists - - // Since z is in directImages, then we precisely have ∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a))) by `directImagesMembership` - have(thesis) by Tautology.from(lastStep, directImagesMembership of (A := O3, y := z), fIsFunction, o3InPowerSet) - } - thenHave(forall(z, z ∈ directImages(f, X, Y, O3) ==> z ∈ O)) by RightForall - val isSubset = have(directImages(f, X, Y, O3) ⊆ O) by Tautology.from(lastStep, subsetAxiom of (x := directImages(f, X, Y, O3), y := O)) - - // That is also covering Y - // We use that f is surjective to get that directImage(f, X, Y, X) = Y - val replacement = have(directImage(f, X, Y, X) === Y) by Tautology.from(imageSurjective, fIsFunction) - val coveringStatement = have(cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) by Tautology.from( - coverDirectImage of (A := O3), - fIsFunction, - o3InPowerSet - ) - have( - ((directImage(f, X, Y, X) === Y), cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) - |- cover(Y, directImages(f, X, Y, O3)) - ) subproof { - have( - ((directImage(f, X, Y, X) === Y), cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) - |- cover(directImage(f, X, Y, X), directImages(f, X, Y, O3)) - ) by Tautology - thenHave(thesis) by RightSubstEq.withParametersSimple(List((directImage(f, X, Y, X), Y)), lambda(x, cover(x, directImages(f, X, Y, O3)))) - } - val covering = have(cover(Y, directImages(f, X, Y, O3))) by Tautology.from(lastStep, coveringStatement, replacement) - - // Finally it's finite since O3 is - val finiteCover = have(finite(directImages(f, X, Y, O3))) by Tautology.from( - directImageFinite of (A := O3), - fIsFunction, - o3InPowerSet - ) - - have(thesis) by Tautology.from(isSubset, finiteCover, covering) - } - - thenHave( - subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3) - |- - ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)) - ) by RightExists - - // Concluding - thenHave(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by LeftExists - have(thesis) by Tautology.from(lastStep, existsO3) - } - thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by Tautology - val yIsCompact = thenHave(forall(O, openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)))) by RightForall - - have(thesis) by Tautology.from(yIsCompact, yIsTop, compact.definition of (X := Y, T := T2)) - } } diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/package.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/package.scala index ec68adf10..074c0d757 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/package.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/package.scala @@ -71,4 +71,27 @@ package object functions { constantFunctionFunctionFrom, constantFunctionApplication } + /*export lisa.maths.settheory.functions.DirectPreimages.{ + directImage, + directImageMembership, + directImageSetUnion, + preimage, + preimageMembership, + preimageSubset, + preimageY, + preimages, + preimagesMembership, + directImages, + directImagesMembership, + preimageSetUnion, + preimageDifference, + preimageUnionThm, + directImageEmptySet, + directImageSubset, + applyDirectImage, + directImagePreimage, + directImagePreimageSurjective, + directImageX, + imageSurjective + }*/ } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala new file mode 100644 index 000000000..3285aea0a --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala @@ -0,0 +1,336 @@ +package lisa.maths.topology + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* + +import lisa.automation.kernel.CommonTactics.Definition + +import lisa.maths.topology.Topology.* +import lisa.maths.topology.Continuity.* +import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.SetTheoryBasics.* +import lisa.automation.kernel.CommonTactics.* +import lisa.automation.settheory.SetTheoryTactics.UniqueComprehension +import lisa.automation.settheory.SetTheoryTactics.TheConditional +import lisa.maths.settheory.functions.Functionals.* +import lisa.maths.settheory.functions.DirectPreimages.* + +object Compactness extends lisa.Main { + // var defs + private val x, y, z, a, b, c, t, p, f, r, s = variable + private val X, T, T1, T2 = variable + private val S, A, B, Y, o, O, O2, O3 = variable + + // ------------------- + // Compactness + // ------------------- + + val cover = DEF(X, O) --> + forall(o, in(o, O) ==> subset(o, X)) /\ + subset(X, union(O)) + + val openCover = DEF(X, T, O) --> + cover(X, O) /\ subset(O, T) + + val finite = DEF(X) --> (X === emptySet) // TODO + + val compact = DEF(X, T) --> + topology(X, T) /\ + forall( + O, + openCover(X, T, O) ==> + ∃( + O2, // Another subcovering + subset(O2, O) /\ cover(X, O2) /\ finite(O2) + ) + ) + + val coverDirectImage = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) |- cover(directImage(f, X, Y, X), directImages(f, X, Y, A)) + ) { + assume(functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) + + sorry + } + + /* Any subset of an open cover is an open cover */ + val subsetOpenCover = Theorem( + (openCover(X, T, O), subset(O2, O), cover(X, O2)) |- openCover(X, T, O2) + ) { + assume(openCover(X, T, O), O2 ⊆ O, cover(X, O2)) + + have(thesis) by Tautology.from( + openCover.definition of (O := O2), + openCover.definition, + subsetTransitivity of (a := O2, b := O, c := T) + ) + } + + /* The preimages of some set in P(Y) are in P(X) */ + val preimagesInPowerSet = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- preimages(f, X, Y, A) ⊆ powerSet(X) + ) { + assume(functionFrom(f, X, Y), A ⊆ powerSet(Y)) + + have(x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X)) by Tautology.from( + preimagesMembership of (A := A, x := x) + ) + thenHave(forall(x, x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimages(f, X, Y, A), y := powerSet(X))) + } + + /* The set of direct images is finite */ + val directImageFinite = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X), finite(A)) |- finite(directImages(f, X, Y, A)) + ) { + assume(functionFrom(f, X, Y), A ⊆ powerSet(X), finite(A)) + // TODO: Needs to have a notion of finiteness to complete the proof + // Normally it should just be because there is a bijection between directImages(f, X, Y, A) and A, and A is finite + sorry + } + + val heineBorelThm = Theorem((compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- compact(Y, T2)) { + assume(compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) + + val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) + val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) + + val xIsCompact = have(forall(O, openCover(X, T1, O) ==> ∃(O2, subset(O2, O) /\ cover(X, O2) /\ finite(O2)))) by Tautology.from( + compact.definition of (T := T1) + ) + val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) + + val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) + + have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) subproof { + assume(openCover(Y, T2, O)) + + have(forall(O, (O ⊆ T2) ==> (union(O) ∈ T2))) by Tautology.from( + containsUnion.definition of (T := T2), + yIsTop, + topology.definition of (X := Y, T := T2) + ) + thenHave(O ⊆ T2 ==> (union(O) ∈ T2)) by InstantiateForall(O) + val unionInT2 = have(union(O) ∈ T2) by Tautology.from( + openCover.definition of (X := Y, T := T2), + lastStep + ) + + val oInPowerSet = have(O ⊆ powerSet(Y)) by Tautology.from( + openCover.definition of (X := Y, T := T2), + yIsTop, + topology.definition of (X := Y, T := T2), + setOfSubsets.definition of (X := Y, T := T2), + subsetTransitivity of (a := O, b := T2, c := powerSet(Y)) + ) + val unionOSubsetY = have(union(O) ⊆ Y) by Tautology.from( + oInPowerSet, + subsetClosedUnion of (x := O, y := Y) + ) + + // We have an open cover of X, that's preimages(f, X, Y, O) + val isOpenCover = have(openCover(X, T1, preimages(f, X, Y, O))) subproof { + // Firstly, it's a cover + val isCover = have(cover(X, preimages(f, X, Y, O))) subproof { + have(o ∈ preimages(f, X, Y, O) ==> o ⊆ X) subproof { + assume(o ∈ preimages(f, X, Y, O)) + have(o ∈ powerSet(X)) by Tautology.from( + preimagesMembership of (A := O, x := o), + fIsFunction, + oInPowerSet + ) + have(o ⊆ X) by Tautology.from(lastStep, powerAxiom of (x := o, y := X)) + } + val firstPart = thenHave(∀(o, o ∈ preimages(f, X, Y, O) ==> o ⊆ X)) by RightForall + + // The covering part + have(x ∈ X ==> x ∈ union(preimages(f, X, Y, O))) subproof { + assume(x ∈ X) + // Function application + have(app(f, x) ∈ Y) by Tautology.from( + fIsFunction, + lastStep, + functionFromApplication of (x := X, y := Y, a := x), + functionFrom.definition of (x := X, y := Y) + ) + // Since Y is covered by O + have(app(f, x) ∈ union(O)) by Tautology.from( + lastStep, + openCover.definition of (X := Y, T := T2), + cover.definition of (X := Y), + subsetTactic of (x := Y, y := union(O), z := app(f, x)) + ) + // That's the definition of being in the preimage! + have(x ∈ preimage(f, X, Y, union(O))) by Tautology.from( + lastStep, + preimageMembership of (B := union(O)), + fIsFunction, + unionOSubsetY + ) + // use that preimage(f, X, Y, union(O)) ⊆ union(preimages(f, X, Y, O)) + have(x ∈ union(preimages(f, X, Y, O))) by Tautology.from( + lastStep, + preimageUnionThm of (B := O), + replaceEqualityContainsRight of (x := preimage(f, X, Y, union(O)), y := union(preimages(f, X, Y, O)), z := x), + fIsFunction, + oInPowerSet + ) + } + thenHave(forall(x, x ∈ X ==> x ∈ union(preimages(f, X, Y, O)))) by RightForall + val secondPart = have(subset(X, union(preimages(f, X, Y, O)))) by Tautology.from( + lastStep, + subsetAxiom of (x := X, y := union(preimages(f, X, Y, O))) + ) + + have(thesis) by Tautology.from(firstPart, secondPart, cover.definition of (O := preimages(f, X, Y, O))) + } + + // Also, its elements are open + have(z ∈ preimages(f, X, Y, O) ==> z ∈ T1) subproof { + assume(z ∈ preimages(f, X, Y, O)) + val existsa = have(∃(a, a ∈ O /\ (z === preimage(f, X, Y, a)))) by Tautology.from( + lastStep, + preimagesMembership of (A := O, x := z), + fIsFunction, + oInPowerSet + ) + have(a ∈ O /\ (z === preimage(f, X, Y, a)) |- z ∈ T1) subproof { + assume(a ∈ O /\ (z === preimage(f, X, Y, a))) + val aInT2 = have(a ∈ T2) by Tautology.from( + openCover.definition of (X := Y, T := T2), + subsetTactic of (x := O, y := T2, z := a) + ) + have(a ∈ T2 ==> preimage(f, X, Y, a) ∈ T1) by InstantiateForall(a)(isContinuous) + have(preimage(f, X, Y, a) ∈ T1) by Tautology.from( + aInT2, + lastStep + ) + have(z ∈ T1) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := preimage(f, X, Y, a), z := T1)) + } + thenHave(∃(a, a ∈ O /\ (z === preimage(f, X, Y, a))) |- z ∈ T1) by LeftExists + have(thesis) by Tautology.from(existsa, lastStep) + } + thenHave(forall(z, z ∈ preimages(f, X, Y, O) ==> z ∈ T1)) by RightForall + val isOpenSubset = have(preimages(f, X, Y, O) ⊆ T1) by Tautology.from( + subsetAxiom of (x := preimages(f, X, Y, O), y := T1), + lastStep + ) + + have(thesis) by Tautology.from(openCover.definition of (T := T1, O := preimages(f, X, Y, O)), isCover, isOpenSubset) + } + + // Whence the existence of a finite subcover O3 + have(openCover(X, T1, preimages(f, X, Y, O)) ==> ∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3))) by InstantiateForall(preimages(f, X, Y, O))( + xIsCompact + ) + val existsO3 = have(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3))) by Tautology.from(lastStep, isOpenCover) + + have( + O3 ⊆ preimages(f, X, Y, O) /\ cover(X, O3) /\ finite(O3) + |- + subset(directImages(f, X, Y, O3), O) /\ cover(Y, directImages(f, X, Y, O3)) /\ finite(directImages(f, X, Y, O3)) + ) subproof { + assume(O3 ⊆ preimages(f, X, Y, O), cover(X, O3), finite(O3)) + + val o3InPowerSet = have(O3 ⊆ powerSet(X)) subproof { + have(preimages(f, X, Y, O) ⊆ powerSet(X)) by Tautology.from( + fIsFunction, + oInPowerSet, + preimagesInPowerSet of (A := O) + ) + have(thesis) by Tautology.from(lastStep, subsetTransitivity of (a := O3, b := preimages(f, X, Y, O), c := powerSet(X))) + } + + // So it's a subset + have(z ∈ directImages(f, X, Y, O3) ==> z ∈ O) subproof { + assume(z ∈ directImages(f, X, Y, O3)) + + have(z ∈ powerSet(Y) /\ ∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a)))) by Tautology.from( + directImagesMembership of (A := O3, y := z), + fIsFunction, + o3InPowerSet + ) + + have(a ∈ O3 /\ (z === directImage(f, X, Y, a)) |- z ∈ O) subproof { + assume(a ∈ O3, z === directImage(f, X, Y, a)) + val aInPreimages = have(a ∈ preimages(f, X, Y, O)) by Tautology.from( + lastStep, + subsetTactic of (x := O3, y := preimages(f, X, Y, O), z := a) + ) + have(b ∈ O /\ (a === preimage(f, X, Y, b)) |- directImage(f, X, Y, a) ∈ O) subproof { + assume(b ∈ O, a === preimage(f, X, Y, b)) + have(b ⊆ Y) by Tautology.from( + oInPowerSet, + subsetTactic of (z := b, x := O, y := powerSet(Y)), + powerAxiom of (x := b, y := Y) + ) + val statement = have(directImage(f, X, Y, preimage(f, X, Y, b)) === b) by Tautology.from( + lastStep, + directImagePreimageSurjective of (A := b), + fIsFunction + ) + thenHave(directImage(f, X, Y, a) === b) by RightSubstEq.withParametersSimple(List((a, preimage(f, X, Y, b))), lambda(x, directImage(f, X, Y, x) === b)) + have(thesis) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := directImage(f, X, Y, a), y := b, z := O)) + } + thenHave(exists(b, b ∈ O /\ (a === preimage(f, X, Y, b))) |- directImage(f, X, Y, a) ∈ O) by LeftExists + // use that (functionFrom(f, X, Y), O ⊆ powerSet(Y)) |- a ∈ preimages(f, X, Y, O) ==> (∃(b, b ∈ O /\ (a === preimage(f, X, Y, b))))) + have(directImage(f, X, Y, a) ∈ O) by Tautology.from(lastStep, aInPreimages, preimagesMembership of (A := O, x := a), fIsFunction, oInPowerSet) + // Conclude since z === directImage(f, X, Y, a) + have(z ∈ O) by Tautology.from(lastStep, replaceEqualityContainsLeft of (x := z, y := directImage(f, X, Y, a), z := O)) + } + + thenHave(∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a))) |- z ∈ O) by LeftExists + + // Since z is in directImages, then we precisely have ∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a))) by `directImagesMembership` + have(thesis) by Tautology.from(lastStep, directImagesMembership of (A := O3, y := z), fIsFunction, o3InPowerSet) + } + thenHave(forall(z, z ∈ directImages(f, X, Y, O3) ==> z ∈ O)) by RightForall + val isSubset = have(directImages(f, X, Y, O3) ⊆ O) by Tautology.from(lastStep, subsetAxiom of (x := directImages(f, X, Y, O3), y := O)) + + // That is also covering Y + // We use that f is surjective to get that directImage(f, X, Y, X) = Y + val replacement = have(directImage(f, X, Y, X) === Y) by Tautology.from(imageSurjective, fIsFunction) + val coveringStatement = have(cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) by Tautology.from( + coverDirectImage of (A := O3), + fIsFunction, + o3InPowerSet + ) + have( + ((directImage(f, X, Y, X) === Y), cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) + |- cover(Y, directImages(f, X, Y, O3)) + ) subproof { + have( + ((directImage(f, X, Y, X) === Y), cover(directImage(f, X, Y, X), directImages(f, X, Y, O3))) + |- cover(directImage(f, X, Y, X), directImages(f, X, Y, O3)) + ) by Tautology + thenHave(thesis) by RightSubstEq.withParametersSimple(List((directImage(f, X, Y, X), Y)), lambda(x, cover(x, directImages(f, X, Y, O3)))) + } + val covering = have(cover(Y, directImages(f, X, Y, O3))) by Tautology.from(lastStep, coveringStatement, replacement) + + // Finally it's finite since O3 is + val finiteCover = have(finite(directImages(f, X, Y, O3))) by Tautology.from( + directImageFinite of (A := O3), + fIsFunction, + o3InPowerSet + ) + + have(thesis) by Tautology.from(isSubset, finiteCover, covering) + } + + thenHave( + subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3) + |- + ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)) + ) by RightExists + + // Concluding + thenHave(∃(O3, subset(O3, preimages(f, X, Y, O)) /\ cover(X, O3) /\ finite(O3)) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by LeftExists + have(thesis) by Tautology.from(lastStep, existsO3) + } + thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by Tautology + val yIsCompact = thenHave(forall(O, openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)))) by RightForall + + have(thesis) by Tautology.from(yIsCompact, yIsTop, compact.definition of (X := Y, T := T2)) + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala new file mode 100644 index 000000000..d9f1cc62a --- /dev/null +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala @@ -0,0 +1,145 @@ +package lisa.maths.topology + +import lisa.automation.settheory.SetTheoryTactics.* +import lisa.maths.Quantifiers.* + +import lisa.automation.kernel.CommonTactics.Definition + +import lisa.maths.topology.Topology.* +import lisa.maths.settheory.* +import lisa.maths.settheory.SetTheory.* +import lisa.maths.settheory.SetTheoryBasics.* +import lisa.automation.kernel.CommonTactics.* +import lisa.automation.settheory.SetTheoryTactics.UniqueComprehension +import lisa.automation.settheory.SetTheoryTactics.TheConditional +import lisa.maths.settheory.functions.Functionals.* +import lisa.maths.settheory.functions.DirectPreimages.* + +object Continuity extends lisa.Main { + // var defs + private val x, y, z, a, b, c, t, p, f, r, s = variable + private val X, T, T1, T2 = variable + private val S, A, B, Y, o, O, O2, O3 = variable + + // ------------------- + // Mappings + // ------------------- + val mapping = DEF(f, X, T1, Y, T2) --> + (functionFrom(f, X, Y) /\ topology(X, T1) /\ topology(Y, T2)) + + // ------------------- + // Continuity + // ------------------- + val continuous = DEF(f, X, T1, Y, T2) --> + (mapping(f, X, T1, Y, T2) /\ forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) + + // ------------------- + // Connectedness + // ------------------- + val clopen = DEF(X, T, A) --> ( + topology(X, T) /\ + A ∈ T /\ setDifference(X, A) ∈ T + ) + + val connectedTop = DEF(X, T) --> ( + topology(X, T) /\ + forall(A, clopen(X, T, A) ==> ((A === emptySet) \/ (A === X))) + ) + + // ------------------- + // Intermediate value theorem + // ------------------- + val intermediateValueThm = Theorem((connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- connectedTop(Y, T2)) { + assume(connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) + val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) + val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) + + val xIsConnected = have(forall(A, clopen(X, T1, A) ==> ((A === emptySet) \/ (A === X)))) by Tautology.from(connectedTop.definition of (T := T1)) + val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) + + val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) + + have(clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y))) subproof { + assume(clopen(Y, T2, A)) + + val aIsSubset = have(A ⊆ Y) subproof { + have(A ∈ T2) by Tautology.from(clopen.definition of (X := Y, T := T2)) + have(A ∈ powerSet(Y)) by Tautology.from( + lastStep, + yIsTop, + topology.definition of (X := Y, T := T2), + setOfSubsets.definition of (X := Y, T := T2), + subsetTactic of (x := T2, y := powerSet(Y), z := A) + ) + have(thesis) by Tautology.from(lastStep, powerAxiom of (x := A, y := Y)) + } + + val preimageA = have(A ∈ T2 ==> preimage(f, X, Y, A) ∈ T1) by InstantiateForall(A)(isContinuous) + val yIsClopen = have(A ∈ T2 /\ setDifference(Y, A) ∈ T2) by Tautology.from(clopen.definition of (X := Y, T := T2)) + val part1 = have(preimage(f, X, Y, A) ∈ T1) by Tautology.from(yIsClopen, preimageA) + val preimageYA = have(setDifference(Y, A) ∈ T2 ==> preimage(f, X, Y, setDifference(Y, A)) ∈ T1) by InstantiateForall(setDifference(Y, A))(isContinuous) + have(preimage(f, X, Y, setDifference(Y, A)) ∈ T1) by Tautology.from(yIsClopen, preimageYA) + val part2 = have(setDifference(X, preimage(f, X, Y, A)) ∈ T1) by Tautology.from( + lastStep, + aIsSubset, + fIsFunction, + preimageDifference, + replaceEqualityContainsLeft of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A)), z := T1) + ) + + // So f^-1(A) is clopen + val inverseIsClopen = have(clopen(X, T1, preimage(f, X, Y, A))) by Tautology.from( + xIsTop, + part1, + part2, + clopen.definition of (T := T1, A := preimage(f, X, Y, A)) + ) + + // Hence (f^-1(A) === emptySet) \/ (preimage(f, X, Y, A) === X) by connectedness of X + have(clopen(X, T1, preimage(f, X, Y, A)) ==> (preimage(f, X, Y, A) === emptySet) \/ (preimage(f, X, Y, A) === X)) by InstantiateForall(preimage(f, X, Y, A))(xIsConnected) + val preImageIsConnected = have((preimage(f, X, Y, A) === emptySet) \/ (preimage(f, X, Y, A) === X)) by Tautology.from( + lastStep, + inverseIsClopen + ) + + // Use the fact that f(emptyset)=emptyset, f(f^-1(A)) = A (by surjectivity), f(X) = Y (by surjectivity) to conclude + val firstCase = have(preimage(f, X, Y, A) === emptySet |- A === emptySet) subproof { + assume(preimage(f, X, Y, A) === emptySet) + have(thesis) by Tautology.from( + lastStep, + fIsFunction, + aIsSubset, + applyDirectImage of (A := preimage(f, X, Y, A), B := emptySet), + directImagePreimageSurjective, + directImageEmptySet, + equalityTransitivity of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := directImage(f, X, Y, emptySet), z := emptySet), + equalitySymmetry of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A), + equalityTransitivity of (x := A, y := directImage(f, X, Y, preimage(f, X, Y, A)), z := emptySet) + ) + } + + val secondCase = have(preimage(f, X, Y, A) === X |- A === Y) subproof { + assume(preimage(f, X, Y, A) === X) + have(thesis) by Tautology.from( + lastStep, + fIsFunction, + aIsSubset, + applyDirectImage of (A := preimage(f, X, Y, A), B := X), + directImagePreimageSurjective, + directImageX, + surjectiveImpliesRangeIsCodomain of (x := X, y := Y), + equalitySymmetry of (x := Y, y := functionRange(f)), + equalityTransitivity of (x := directImage(f, X, Y, X), y := functionRange(f), z := Y), + equalityTransitivity of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := directImage(f, X, Y, X), z := Y), + equalitySymmetry of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A), + equalityTransitivity of (x := A, y := directImage(f, X, Y, preimage(f, X, Y, A)), z := Y) + ) + } + + have(thesis) by Tautology.from(preImageIsConnected, firstCase, secondCase) + } + + val allClopen = thenHave(forall(A, clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y)))) by RightForall + have(connectedTop(Y, T2)) by Tautology.from(allClopen, yIsTop, connectedTop.definition of (X := Y, T := T2)) + } +} diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala index 7e5f3dd0d..5991782ed 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala @@ -7,7 +7,6 @@ import lisa.automation.kernel.CommonTactics.Definition import lisa.maths.topology.DiscreteTopology.* import lisa.maths.topology.Topology.* -import lisa.maths.topology.Instances.* import lisa.maths.settheory.* import lisa.maths.settheory.SetTheory.* import lisa.maths.settheory.SetTheoryBasics.* From d032f97ecaa6bc52b61adc3b807476d024d826c4 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Wed, 18 Dec 2024 16:47:25 +0100 Subject: [PATCH 31/38] Document DirectPreimages --- .../settheory/functions/DirectPreimages.scala | 155 ++++++++++++++---- 1 file changed, 121 insertions(+), 34 deletions(-) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala index 70bc69197..a5004b37f 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala @@ -43,8 +43,10 @@ object DirectPreimages extends lisa.Main { have(thesis) by Cut(surjfunc, funceq) } + // The formula for direct image that will be used throughout the definitions and theorems of uniqueness inline def directImageFormula = y ∈ s <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ A)) + // This direct image is unique val directImageUniqueness = Theorem( (functionFrom(f, X, Y), subset(A, X)) |- ∃!(s, forall(y, directImageFormula)) ) { @@ -52,14 +54,25 @@ object DirectPreimages extends lisa.Main { thenHave(thesis) by Weakening } + /** + * Direct image by a function + * f(A) = { y ∈ Y | ∃x ∈ A, f(x) = y } + */ val directImage = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImageFormula))(directImageUniqueness) + /* + * Useful statement about membership in the direct image + */ val directImageMembership = Theorem((functionFrom(f, X, Y), subset(A, X)) |- y ∈ directImage(f, X, Y, A) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ A))) { assume(functionFrom(f, X, Y) /\ subset(A, X)) have(forall(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) thenHave(thesis) by InstantiateForall(y) } + /** + * Theorem + * f(A ∪ B) = f(A) ∪ f(B) + */ val directImageSetUnion = Theorem( functionFrom(f, X, Y) /\ subset(A, X) /\ @@ -164,14 +177,25 @@ object DirectPreimages extends lisa.Main { thenHave(thesis) by Weakening } + /** + * Preimage by a function + * f^(-1)(B) = { x ∈ X | f(x) ∈ B } + */ val preimage = DEF(f, X, Y, B) --> TheConditional(s, forall(x, preimageFormula))(preimageUniqueness) + /** + * Useful statement about membership in the preimage + */ val preimageMembership = Theorem((functionFrom(f, X, Y), subset(B, Y)) |- x ∈ preimage(f, X, Y, B) <=> (x ∈ X /\ app(f, x) ∈ B)) { assume(functionFrom(f, X, Y) /\ subset(B, Y)) have(forall(x, x ∈ preimage(f, X, Y, B) <=> (x ∈ X /\ app(f, x) ∈ B))) by InstantiateForall(preimage(f, X, Y, B))(preimage.definition) thenHave(thesis) by InstantiateForall(x) } + /** + * Theorem -- the preimage is always in the domain + * f^(-1)(A) ⊆ X + */ val preimageSubset = Theorem( (functionFrom(f, X, Y), subset(A, Y)) |- preimage(f, X, Y, A) ⊆ X ) { @@ -181,6 +205,10 @@ object DirectPreimages extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimage(f, X, Y, A), y := X)) } + /** + * Theorem -- the preimage of the codomain is the domain + * f^(-1)(Y) = X + */ val preimageY = Theorem( functionFrom(f, X, Y) |- preimage(f, X, Y, Y) === X ) { @@ -198,40 +226,10 @@ object DirectPreimages extends lisa.Main { have(thesis) by Tautology.from(first, second, equalityBySubset of (x := X, y := preimage(f, X, Y, Y))) } - inline def preimagesFormula = x ∈ s <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) - - val preimagesUniqueness = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, preimagesFormula)) - ) { - have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(powerSet(X), lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) - thenHave(thesis) by Weakening - } - - val preimages = DEF(f, X, Y, A) --> TheConditional(s, forall(x, preimagesFormula))(preimagesUniqueness) - - val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) { - assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) - have(forall(x, x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))))) by InstantiateForall(preimages(f, X, Y, A))(preimages.definition) - thenHave(thesis) by InstantiateForall(x) - } - - inline def directImagesFormula = y ∈ s <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))) - - val directImagesUniqueness = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(X)) |- ∃!(s, forall(y, directImagesFormula)) - ) { - have(∃!(s, forall(y, directImagesFormula))) by UniqueComprehension(powerSet(Y), lambda(y, ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) - thenHave(thesis) by Weakening - } - - val directImages = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImagesFormula))(directImagesUniqueness) - - val directImagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(X)) |- y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) { - assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(X)) - have(forall(y, y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))))) by InstantiateForall(directImages(f, X, Y, A))(directImages.definition) - thenHave(thesis) by InstantiateForall(y) - } - + /** + * Theorem -- the preimage of the union is the union of the preimages (case with only two subsets) + * f^(-1)(A ∪ B) = f^(-1)(A) ∪ f^(-1)(B) + */ val preimageSetUnion = Theorem( functionFrom(f, X, Y) /\ subset(A, Y) /\ @@ -300,6 +298,60 @@ object DirectPreimages extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := preimage(f, X, Y, setUnion(A, B)), y := setUnion(preimage(f, X, Y, A), preimage(f, X, Y, B))))) } + /** + * ************** + * Set of preimages of a set of subsets + * Useful for the lemma about the preimage of the union + * *************** + */ + inline def preimagesFormula = x ∈ s <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) + + val preimagesUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, preimagesFormula)) + ) { + have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(powerSet(X), lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) + thenHave(thesis) by Weakening + } + + /** + * Set of preimages of a set of subsets + * { f^(-1)(A_i) | A_i ∈ A } + */ + val preimages = DEF(f, X, Y, A) --> TheConditional(s, forall(x, preimagesFormula))(preimagesUniqueness) + + /** + * Useful statement about membership in the preimages + */ + val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) { + assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) + have(forall(x, x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))))) by InstantiateForall(preimages(f, X, Y, A))(preimages.definition) + thenHave(thesis) by InstantiateForall(x) + } + + inline def directImagesFormula = y ∈ s <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))) + + val directImagesUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X)) |- ∃!(s, forall(y, directImagesFormula)) + ) { + have(∃!(s, forall(y, directImagesFormula))) by UniqueComprehension(powerSet(Y), lambda(y, ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) + thenHave(thesis) by Weakening + } + + /** + * Set of direct images of a set of subsets + * { f(A_i) | A_i ∈ A } + */ + val directImages = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImagesFormula))(directImagesUniqueness) + + /** + * Useful statement about membership in the direct images + */ + val directImagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(X)) |- y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) { + assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(X)) + have(forall(y, y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))))) by InstantiateForall(directImages(f, X, Y, A))(directImages.definition) + thenHave(thesis) by InstantiateForall(y) + } + val preimageDifference = Theorem( (functionFrom(f, X, Y), subset(A, Y)) |- setDifference(X, preimage(f, X, Y, A)) === preimage(f, X, Y, setDifference(Y, A)) @@ -339,6 +391,13 @@ object DirectPreimages extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A))))) } + /** + * Theorem -- generalization of `preimageSetUnion` + * The preimage of arbitrary union is the union of preimages + * f^(-1)(⋃B) = ⋃(f^(-1)(B)) + * + * This is why we needed the definition of `preimages`! + */ val preimageUnionThm = Theorem( (functionFrom(f, X, Y), B ⊆ powerSet(Y)) |- preimage(f, X, Y, union(B)) === union(preimages(f, X, Y, B)) @@ -346,6 +405,10 @@ object DirectPreimages extends lisa.Main { sorry } + /** + * Theorem -- direct image of the empty set + * f(∅) = ∅ + */ val directImageEmptySet = Theorem( (functionFrom(f, X, Y)) |- directImage(f, X, Y, emptySet) === emptySet @@ -370,6 +433,10 @@ object DirectPreimages extends lisa.Main { have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (x := directImage(f, X, Y, emptySet))) } + /** + * Theorem -- the direct image is always in the codomain + * f(A) ⊆ f(X) + */ val directImageSubset = Theorem( (functionFrom(f, X, Y), subset(A, X)) |- directImage(f, X, Y, A) ⊆ functionRange(f) @@ -407,6 +474,11 @@ object DirectPreimages extends lisa.Main { have(thesis) by Tautology.from(subsetAxiom of (x := directImage(f, X, Y, A), y := functionRange(f)), lastStep) } + /** + * Theorem -- congruence/substitution property for the direct image + * + * Needed as a lemma for other proofs + */ val applyDirectImage = Theorem( A === B |- directImage(f, X, Y, A) === directImage(f, X, Y, B) ) { @@ -419,6 +491,10 @@ object DirectPreimages extends lisa.Main { have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B))) } + /** + * Theorem -- the direct image of the preimage is always a subset of the original set + * f(f^(-1)(A)) ⊆ A + */ val directImagePreimage = Theorem( (functionFrom(f, X, Y), subset(A, Y)) |- directImage(f, X, Y, preimage(f, X, Y, A)) ⊆ A @@ -441,6 +517,9 @@ object DirectPreimages extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A)) } + /** + * Theorem -- refinement of `directImagePreimage` in case of surjective functions (we have equality!) + */ val directImagePreimageSurjective = Theorem( (functionFrom(f, X, Y), surjective(f, X, Y), subset(A, Y)) |- directImage(f, X, Y, preimage(f, X, Y, A)) === A @@ -483,6 +562,10 @@ object DirectPreimages extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A))) } + /** + * Theorem -- the direct image of the domain is the function range + * f(X) = functionRange(f) + */ val directImageX = Theorem( functionFrom(f, X, Y) |- directImage(f, X, Y, X) === functionRange(f) @@ -532,6 +615,10 @@ object DirectPreimages extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, X), y := functionRange(f)))) } + /** + * Theorem -- the direct image of the domain is precisely Y if the function is surjective + * f(X) = Y + */ val imageSurjective = Theorem( (functionFrom(f, X, Y), surjective(f, X, Y)) |- directImage(f, X, Y, X) === Y ) { From a4ac69d3bace67b967339175a4b120f8594ecf63 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Wed, 18 Dec 2024 16:53:09 +0100 Subject: [PATCH 32/38] Documentation --- .../lisa/maths/topology/Compactness.scala | 32 ++++++++++++++++--- .../lisa/maths/topology/Continuity.scala | 20 ++++++++++-- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala index 3285aea0a..f256b6fc6 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala @@ -26,15 +26,22 @@ object Compactness extends lisa.Main { // Compactness // ------------------- + // A cover of X is a set of subsets of X that covers X (i.e. their union is a superset of X) val cover = DEF(X, O) --> forall(o, in(o, O) ==> subset(o, X)) /\ subset(X, union(O)) + // Open cover is a cover that is a set of open sets! val openCover = DEF(X, T, O) --> cover(X, O) /\ subset(O, T) - val finite = DEF(X) --> (X === emptySet) // TODO + // TODO: we need a notion of finiteness/cardinality for that + val finite = DEF(X) --> (X === emptySet) + /* + * Compact spaces + * A topological space is compact if every open cover has a finite subcover + */ val compact = DEF(X, T) --> topology(X, T) /\ forall( @@ -46,6 +53,9 @@ object Compactness extends lisa.Main { ) ) + /** + * Intermediate lemma for Heine-Borel theorem + */ val coverDirectImage = Theorem( (functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) |- cover(directImage(f, X, Y, X), directImages(f, X, Y, A)) ) { @@ -54,7 +64,10 @@ object Compactness extends lisa.Main { sorry } - /* Any subset of an open cover is an open cover */ + /** + * Lemma -- + * Any subset of an open cover is an open cover + */ val subsetOpenCover = Theorem( (openCover(X, T, O), subset(O2, O), cover(X, O2)) |- openCover(X, T, O2) ) { @@ -67,7 +80,10 @@ object Compactness extends lisa.Main { ) } - /* The preimages of some set in P(Y) are in P(X) */ + /** + * Lemma -- + * The preimages of some set in P(Y) are in P(X) + */ val preimagesInPowerSet = Theorem( (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- preimages(f, X, Y, A) ⊆ powerSet(X) ) { @@ -80,7 +96,10 @@ object Compactness extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimages(f, X, Y, A), y := powerSet(X))) } - /* The set of direct images is finite */ + /** + * Lemma -- + * The set of direct images is finite + */ val directImageFinite = Theorem( (functionFrom(f, X, Y), A ⊆ powerSet(X), finite(A)) |- finite(directImages(f, X, Y, A)) ) { @@ -90,6 +109,11 @@ object Compactness extends lisa.Main { sorry } + /** + * Heine-Borel theorem + * + * The image of a compact space by a continuous, surjective mapping is a compact space + */ val heineBorelThm = Theorem((compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- compact(Y, T2)) { assume(compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala index d9f1cc62a..34e10f680 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala @@ -24,31 +24,45 @@ object Continuity extends lisa.Main { // ------------------- // Mappings // ------------------- + /* + * A mapping is a function between topological spaces + */ val mapping = DEF(f, X, T1, Y, T2) --> (functionFrom(f, X, Y) /\ topology(X, T1) /\ topology(Y, T2)) // ------------------- // Continuity // ------------------- + /** + * A function f is continuous if the preimage of any open set in the codomain is open in the domain + */ val continuous = DEF(f, X, T1, Y, T2) --> (mapping(f, X, T1, Y, T2) /\ forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) // ------------------- // Connectedness // ------------------- + /* + * A set A is clopen if it is both open and closed + */ val clopen = DEF(X, T, A) --> ( topology(X, T) /\ A ∈ T /\ setDifference(X, A) ∈ T ) + /** + * A topological space is connected if the only clopen sets are the empty set and the whole space + */ val connectedTop = DEF(X, T) --> ( topology(X, T) /\ forall(A, clopen(X, T, A) ==> ((A === emptySet) \/ (A === X))) ) - // ------------------- - // Intermediate value theorem - // ------------------- + /** + * Intermediate value theorem + * + * The image of a connected space by a continuous, surjective mapping is a connected space + */ val intermediateValueThm = Theorem((connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- connectedTop(Y, T2)) { assume(connectedTop(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) From 80ea0dcb2bf15b7b95a77f93aa8a2d1a3bddaf67 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Wed, 18 Dec 2024 17:00:34 +0100 Subject: [PATCH 33/38] Delete Main.scala --- lisa-topology/src/main/scala/lisa/Main.scala | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 lisa-topology/src/main/scala/lisa/Main.scala diff --git a/lisa-topology/src/main/scala/lisa/Main.scala b/lisa-topology/src/main/scala/lisa/Main.scala deleted file mode 100644 index 3492c4857..000000000 --- a/lisa-topology/src/main/scala/lisa/Main.scala +++ /dev/null @@ -1,3 +0,0 @@ -package lisa - -import lisa.maths.topology From 191a35d0fd13c253b26829bd7bfab1be3d3c4603 Mon Sep 17 00:00:00 2001 From: Fabrice Egger Date: Wed, 18 Dec 2024 17:01:10 +0100 Subject: [PATCH 34/38] added documentation for set theory --- .../maths/settheory/SetTheoryBasics.scala | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala index 1f23990ec..544371e6b 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -28,6 +28,10 @@ object SetTheoryBasics extends lisa.Main { * Theorems about basic sets */ + /* + Theorem: Equality by Subset + x is equal to y iff x ⊆ y and y ⊆ x + */ val equalityBySubset = Theorem((x === y) <=> (x ⊆ y /\ y ⊆ x)) { val forward = have(x === y |- x ⊆ y /\ y ⊆ x) subproof { assume(x === y) @@ -54,6 +58,10 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(forward, backward) } + /* + Theorem: Equality replaces contains right side + if x is equal to y then we have z ∈ x iff z ∈ y for any z + */ val replaceEqualityContainsRight = Theorem((x === y) ==> ((z ∈ x) <=> (z ∈ y))) { have((x === y) |- ((z ∈ x) <=> (z ∈ y))) subproof { assume(x === y) @@ -63,6 +71,10 @@ object SetTheoryBasics extends lisa.Main { thenHave(thesis) by Tautology } + /* + Theorem: Equality replaces contains left side + if x is equal to y then we have x ∈ z iff y ∈ z for any z + */ val replaceEqualityContainsLeft = Theorem((x === y) ==> ((x ∈ z) <=> (y ∈ z))) { have(((x === y), (y ∈ z)) |- (x ∈ z)) subproof { have((y ∈ z, (x === y)) |- (y ∈ z)) by Tautology @@ -71,6 +83,10 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(lastStep, lastStep of (x := y, y := x)) } + /* + Theorem: Equality replaces subset right side + if x is equal to y then we have z ⊆ x iff z ⊆ y for any z + */ val replaceEqualitySubsetRight = Theorem((x === y) ==> ((z ⊆ x) <=> (z ⊆ y))) { val side = have(((x === y), (z ⊆ x)) |- (z ⊆ y)) subproof { assume((x === y) /\ (z ⊆ x)) @@ -79,6 +95,10 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(side, side of (x := y, y := x)) } + /* + Theorem: Equality replaces subset left side + if x is equal to y then we have x ⊆ z iff y ⊆ z for any z + */ val replaceEqualitySubsetLeft = Theorem((x === y) ==> ((x ⊆ z) <=> (y ⊆ z))) { val side = have(((x === y), (x ⊆ z)) |- (y ⊆ z)) subproof { assume((x === y) /\ (x ⊆ z)) @@ -91,6 +111,10 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(side, side of (x := y, y := x)) } + /* + Theorem: Subset is closed under intersection + if x ⊆ z and y ⊆ z we have x ∩ y ⊆ z + */ val subsetClosedIntersection = Theorem((x ⊆ z, y ⊆ z) |- (x ∩ y) ⊆ z) { assume(x ⊆ z, y ⊆ z) have(forall(t, in(t, x) ==> in(t, z))) by Tautology.from(subsetAxiom of (y := z, z := t)) @@ -104,6 +128,10 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := setIntersection(x, y), y := z)) } + /* + Theorem: Intersection means subset of both elements + if z ⊆ x ∩ y we know z ⊆ x and z ⊆ y + */ val subsetUnderIntersection = Theorem((z ⊆ (x ∩ y)) |- (z ⊆ x /\ z ⊆ y)) { assume(z ⊆ (x ∩ y)) have(forall(t, in(t, z) ==> in(t, x ∩ y))) by Tautology.from(subsetAxiom of (x := z, y := x ∩ y, z := t)) @@ -118,6 +146,10 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(first, second) } + /* + Theorem: Subset is closed under setUnion + if x ⊆ z and y ⊆ z we have x u y ⊆ z + */ val subsetClosedSetUnion = Theorem((x ⊆ z, y ⊆ z) |- setUnion(x, y) ⊆ z) { assume(x ⊆ z, y ⊆ z) have(forall(t, in(t, x) ==> in(t, z))) by Tautology.from(subsetAxiom of (y := z, z := t)) @@ -131,6 +163,11 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := setUnion(x, y), y := z)) } + /* + Theorem: Subset is closed under union + The union of any (finite or infinite) number of sets in y is still in y + if x ⊆ powerSet(y) we have union(x) ⊆ y + */ val subsetClosedUnion = Theorem((x ⊆ powerSet(y)) |- union(x) ⊆ y) { assume(x ⊆ powerSet(y)) have(forall(z, in(z, x) ==> in(z, powerSet(y)))) by Tautology.from(subsetAxiom of (y := powerSet(y))) @@ -144,6 +181,11 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := union(x))) } + /* + Theorem: Union doesnt shrink + The union of any (finite or infinite) number of sets where one set is x has to include x + if x ∈ y we have x ⊆ unnion(y) + */ val unionDoesntShrink = Theorem((x ∈ y) |- (x ⊆ union(y))) { assume(in(x, y)) thenHave(in(z, x) |- (in(z, x) /\ in(x, y))) by Tautology @@ -153,6 +195,10 @@ object SetTheoryBasics extends lisa.Main { have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := x, y := union(y))) } + /* + Theorem: Subset propagates over elementOf + if z ∈ x and x ⊆ y we have z ∈ y + */ val subsetTactic = Theorem((x ⊆ y, z ∈ x) |- z ∈ y) { assume(x ⊆ y, z ∈ x) @@ -161,6 +207,10 @@ object SetTheoryBasics extends lisa.Main { thenHave(thesis) by Tautology } + /* + Theorem: Difference shrinks + x \ y ⊆ x + */ val differenceShrinks = Theorem(setDifference(x, y) ⊆ x) { have(in(z, setDifference(x, y)) ==> in(z, x)) by Tautology.from(setDifferenceMembership of (t := z)) thenHave(forall(z, in(z, setDifference(x, y)) ==> in(z, x))) by RightForall From c28f1325391bde71a11204779e301eda69a5471a Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Wed, 18 Dec 2024 20:01:25 +0100 Subject: [PATCH 35/38] Complete the proof of `coverDirectImage` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And transform `forall` to `∀` everywhere we can --- .../settheory/functions/DirectPreimages.scala | 201 +++++++++++------- .../lisa/maths/topology/Compactness.scala | 54 ++++- .../lisa/maths/topology/Continuity.scala | 10 +- 3 files changed, 171 insertions(+), 94 deletions(-) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala index a5004b37f..f0d64951a 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala @@ -48,9 +48,9 @@ object DirectPreimages extends lisa.Main { // This direct image is unique val directImageUniqueness = Theorem( - (functionFrom(f, X, Y), subset(A, X)) |- ∃!(s, forall(y, directImageFormula)) + (functionFrom(f, X, Y), subset(A, X)) |- ∃!(s, ∀(y, directImageFormula)) ) { - have(∃!(s, forall(y, directImageFormula))) by UniqueComprehension(Y, lambda(y, ∃(x, (app(f, x) === y) /\ x ∈ A))) + have(∃!(s, ∀(y, directImageFormula))) by UniqueComprehension(Y, lambda(y, ∃(x, (app(f, x) === y) /\ x ∈ A))) thenHave(thesis) by Weakening } @@ -58,14 +58,14 @@ object DirectPreimages extends lisa.Main { * Direct image by a function * f(A) = { y ∈ Y | ∃x ∈ A, f(x) = y } */ - val directImage = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImageFormula))(directImageUniqueness) + val directImage = DEF(f, X, Y, A) --> TheConditional(s, ∀(y, directImageFormula))(directImageUniqueness) /* * Useful statement about membership in the direct image */ val directImageMembership = Theorem((functionFrom(f, X, Y), subset(A, X)) |- y ∈ directImage(f, X, Y, A) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ A))) { assume(functionFrom(f, X, Y) /\ subset(A, X)) - have(forall(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) + have(∀(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) thenHave(thesis) by InstantiateForall(y) } @@ -87,13 +87,13 @@ object DirectPreimages extends lisa.Main { val subsetAorB = have(subset(setUnion(A, B), X)) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := X)) - have(forall(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) + have(∀(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) val defA = thenHave(z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) - have(forall(z, z ∈ directImage(f, X, Y, B) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, X, Y, B))(directImage.definition of (A := B)) + have(∀(z, z ∈ directImage(f, X, Y, B) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ B)))) by InstantiateForall(directImage(f, X, Y, B))(directImage.definition of (A := B)) val defB = thenHave(z ∈ directImage(f, X, Y, B) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ B))) by InstantiateForall(z) have( subset(setUnion(A, B), X) |- - forall( + ∀( z, z ∈ directImage(f, X, Y, setUnion(A, B)) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ setUnion(A, B))) @@ -168,12 +168,39 @@ object DirectPreimages extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, setUnion(A, B)), y := setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))))) } + /** + * Theorem -- the direct image is monotonic + * + * If A ⊆ B then f(A) ⊆ f(B) + */ + val directImageMonotonicity = Theorem( + (functionFrom(f, X, Y), A ⊆ B, B ⊆ X) |- directImage(f, X, Y, A) ⊆ directImage(f, X, Y, B) + ) { + assume(functionFrom(f, X, Y), A ⊆ B, B ⊆ X) + + val aSubsetOfX = have(A ⊆ X) by Tautology.from(subsetTransitivity of (a := A, b := B, c := X)) + + have((app(f, x) === z) /\ x ∈ A |- (app(f, x) === z) /\ x ∈ B) by Tautology.from(subsetTactic of (x := A, y := B, z := x)) + thenHave((app(f, x) === z) /\ x ∈ A |- ∃(x, (app(f, x) === z) /\ x ∈ B)) by RightExists + thenHave(∃(x, (app(f, x) === z) /\ x ∈ A) |- ∃(x, (app(f, x) === z) /\ x ∈ B)) by LeftExists + have(z ∈ directImage(f, X, Y, A) |- z ∈ directImage(f, X, Y, B)) by Tautology.from( + lastStep, + aSubsetOfX, + directImageMembership of (y := z), + directImageMembership of (y := z, A := B) + ) + + thenHave(z ∈ directImage(f, X, Y, A) ==> z ∈ directImage(f, X, Y, B)) by Tautology + thenHave(∀(z, z ∈ directImage(f, X, Y, A) ==> z ∈ directImage(f, X, Y, B))) by RightForall + have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B))) + } + inline def preimageFormula = x ∈ s <=> (x ∈ X /\ app(f, x) ∈ B) val preimageUniqueness = Theorem( - (functionFrom(f, X, Y), subset(B, Y)) |- ∃!(s, forall(x, preimageFormula)) + (functionFrom(f, X, Y), subset(B, Y)) |- ∃!(s, ∀(x, preimageFormula)) ) { - have(∃!(s, forall(x, preimageFormula))) by UniqueComprehension(X, lambda(x, app(f, x) ∈ B)) + have(∃!(s, ∀(x, preimageFormula))) by UniqueComprehension(X, lambda(x, app(f, x) ∈ B)) thenHave(thesis) by Weakening } @@ -181,14 +208,14 @@ object DirectPreimages extends lisa.Main { * Preimage by a function * f^(-1)(B) = { x ∈ X | f(x) ∈ B } */ - val preimage = DEF(f, X, Y, B) --> TheConditional(s, forall(x, preimageFormula))(preimageUniqueness) + val preimage = DEF(f, X, Y, B) --> TheConditional(s, ∀(x, preimageFormula))(preimageUniqueness) /** * Useful statement about membership in the preimage */ val preimageMembership = Theorem((functionFrom(f, X, Y), subset(B, Y)) |- x ∈ preimage(f, X, Y, B) <=> (x ∈ X /\ app(f, x) ∈ B)) { assume(functionFrom(f, X, Y) /\ subset(B, Y)) - have(forall(x, x ∈ preimage(f, X, Y, B) <=> (x ∈ X /\ app(f, x) ∈ B))) by InstantiateForall(preimage(f, X, Y, B))(preimage.definition) + have(∀(x, x ∈ preimage(f, X, Y, B) <=> (x ∈ X /\ app(f, x) ∈ B))) by InstantiateForall(preimage(f, X, Y, B))(preimage.definition) thenHave(thesis) by InstantiateForall(x) } @@ -201,7 +228,7 @@ object DirectPreimages extends lisa.Main { ) { assume(functionFrom(f, X, Y) /\ subset(A, Y)) have(in(z, preimage(f, X, Y, A)) ==> in(z, X)) by Tautology.from(preimageMembership of (B := A, x := z)) - thenHave(forall(z, in(z, preimage(f, X, Y, A)) ==> in(z, X))) by RightForall + thenHave(∀(z, in(z, preimage(f, X, Y, A)) ==> in(z, X))) by RightForall have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimage(f, X, Y, A), y := X)) } @@ -221,7 +248,7 @@ object DirectPreimages extends lisa.Main { preimageMembership of (x := z, B := Y), subsetReflexivity of (x := Y) ) - thenHave(forall(z, in(z, X) ==> in(z, preimage(f, X, Y, Y)))) by RightForall + thenHave(∀(z, in(z, X) ==> in(z, preimage(f, X, Y, Y)))) by RightForall val second = have(X ⊆ preimage(f, X, Y, Y)) by Tautology.from(lastStep, subsetAxiom of (x := X, y := preimage(f, X, Y, Y))) have(thesis) by Tautology.from(first, second, equalityBySubset of (x := X, y := preimage(f, X, Y, Y))) } @@ -244,13 +271,13 @@ object DirectPreimages extends lisa.Main { val subsetAorB = have(subset(setUnion(A, B), Y)) by Tautology.from(unionOfTwoSubsets of (a := A, b := B, c := Y)) - have(forall(z, z ∈ preimage(f, X, Y, A) <=> (z ∈ X /\ app(f, z) ∈ A))) by InstantiateForall(preimage(f, X, Y, A))(preimage.definition of (B := A)) + have(∀(z, z ∈ preimage(f, X, Y, A) <=> (z ∈ X /\ app(f, z) ∈ A))) by InstantiateForall(preimage(f, X, Y, A))(preimage.definition of (B := A)) val defA = thenHave(z ∈ preimage(f, X, Y, A) <=> (z ∈ X /\ app(f, z) ∈ A)) by InstantiateForall(z) - have(forall(z, z ∈ preimage(f, X, Y, B) <=> (z ∈ X /\ app(f, z) ∈ B))) by InstantiateForall(preimage(f, X, Y, B))(preimage.definition) + have(∀(z, z ∈ preimage(f, X, Y, B) <=> (z ∈ X /\ app(f, z) ∈ B))) by InstantiateForall(preimage(f, X, Y, B))(preimage.definition) val defB = thenHave(z ∈ preimage(f, X, Y, B) <=> (z ∈ X /\ app(f, z) ∈ B)) by InstantiateForall(z) have( subset(setUnion(A, B), Y) |- - forall( + ∀( z, z ∈ preimage(f, X, Y, setUnion(A, B)) <=> (z ∈ X /\ app(f, z) ∈ setUnion(A, B)) @@ -299,66 +326,16 @@ object DirectPreimages extends lisa.Main { } /** - * ************** - * Set of preimages of a set of subsets - * Useful for the lemma about the preimage of the union - * *************** - */ - inline def preimagesFormula = x ∈ s <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) - - val preimagesUniqueness = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, forall(x, preimagesFormula)) - ) { - have(∃!(s, forall(x, preimagesFormula))) by UniqueComprehension(powerSet(X), lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) - thenHave(thesis) by Weakening - } - - /** - * Set of preimages of a set of subsets - * { f^(-1)(A_i) | A_i ∈ A } - */ - val preimages = DEF(f, X, Y, A) --> TheConditional(s, forall(x, preimagesFormula))(preimagesUniqueness) - - /** - * Useful statement about membership in the preimages - */ - val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) { - assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) - have(forall(x, x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))))) by InstantiateForall(preimages(f, X, Y, A))(preimages.definition) - thenHave(thesis) by InstantiateForall(x) - } - - inline def directImagesFormula = y ∈ s <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))) - - val directImagesUniqueness = Theorem( - (functionFrom(f, X, Y), A ⊆ powerSet(X)) |- ∃!(s, forall(y, directImagesFormula)) - ) { - have(∃!(s, forall(y, directImagesFormula))) by UniqueComprehension(powerSet(Y), lambda(y, ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) - thenHave(thesis) by Weakening - } - - /** - * Set of direct images of a set of subsets - * { f(A_i) | A_i ∈ A } + * Theorem -- the complement of the preimage is the preimage of the complement + * X \ f^(-1)(A) = f^(-1)(Y \ A) */ - val directImages = DEF(f, X, Y, A) --> TheConditional(s, forall(y, directImagesFormula))(directImagesUniqueness) - - /** - * Useful statement about membership in the direct images - */ - val directImagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(X)) |- y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) { - assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(X)) - have(forall(y, y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))))) by InstantiateForall(directImages(f, X, Y, A))(directImages.definition) - thenHave(thesis) by InstantiateForall(y) - } - val preimageDifference = Theorem( (functionFrom(f, X, Y), subset(A, Y)) |- setDifference(X, preimage(f, X, Y, A)) === preimage(f, X, Y, setDifference(Y, A)) ) { assume(functionFrom(f, X, Y), subset(A, Y)) - have(forall(t, t ∈ setDifference(Y, A) <=> (in(t, Y) /\ !in(t, A)))) by InstantiateForall(setDifference(Y, A))(setDifference.definition of (x := Y, y := A)) + have(∀(t, t ∈ setDifference(Y, A) <=> (in(t, Y) /\ !in(t, A)))) by InstantiateForall(setDifference(Y, A))(setDifference.definition of (x := Y, y := A)) val defDiffY = thenHave(z ∈ setDifference(Y, A) <=> (in(z, Y) /\ !in(z, A))) by InstantiateForall(z) val forward = have(x ∈ setDifference(X, preimage(f, X, Y, A)) ==> x ∈ preimage(f, X, Y, setDifference(Y, A))) subproof { @@ -391,9 +368,39 @@ object DirectPreimages extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := setDifference(X, preimage(f, X, Y, A)), y := preimage(f, X, Y, setDifference(Y, A))))) } + /** + * ************** + * Set of preimages of a set of subsets + * Useful for the lemma about the preimage of the union + * *************** + */ + inline def preimagesFormula = x ∈ s <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))) + + val preimagesUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- ∃!(s, ∀(x, preimagesFormula)) + ) { + have(∃!(s, ∀(x, preimagesFormula))) by UniqueComprehension(powerSet(X), lambda(x, ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) + thenHave(thesis) by Weakening + } + + /** + * Set of preimages of a set of subsets + * { f^(-1)(A_i) | A_i ∈ A } + */ + val preimages = DEF(f, X, Y, A) --> TheConditional(s, ∀(x, preimagesFormula))(preimagesUniqueness) + + /** + * Useful statement about membership in the preimages + */ + val preimagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(Y)) |- x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a))))) { + assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(Y)) + have(∀(x, x ∈ preimages(f, X, Y, A) <=> (x ∈ powerSet(X) /\ ∃(a, a ∈ A /\ (x === preimage(f, X, Y, a)))))) by InstantiateForall(preimages(f, X, Y, A))(preimages.definition) + thenHave(thesis) by InstantiateForall(x) + } + /** * Theorem -- generalization of `preimageSetUnion` - * The preimage of arbitrary union is the union of preimages + * The preimage of an arbitrary union is the union of preimages * f^(-1)(⋃B) = ⋃(f^(-1)(B)) * * This is why we needed the definition of `preimages`! @@ -405,6 +412,44 @@ object DirectPreimages extends lisa.Main { sorry } + inline def directImagesFormula = y ∈ s <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))) + + val directImagesUniqueness = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X)) |- ∃!(s, ∀(y, directImagesFormula)) + ) { + have(∃!(s, ∀(y, directImagesFormula))) by UniqueComprehension(powerSet(Y), lambda(y, ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) + thenHave(thesis) by Weakening + } + + /** + * Set of direct images of a set of subsets + * { f(A_i) | A_i ∈ A } + */ + val directImages = DEF(f, X, Y, A) --> TheConditional(s, ∀(y, directImagesFormula))(directImagesUniqueness) + + /** + * Useful statement about membership in the direct images + */ + val directImagesMembership = Theorem((functionFrom(f, X, Y), A ⊆ powerSet(X)) |- y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))))) { + assume(functionFrom(f, X, Y) /\ A ⊆ powerSet(X)) + have(∀(y, y ∈ directImages(f, X, Y, A) <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))))) by InstantiateForall(directImages(f, X, Y, A))(directImages.definition) + thenHave(thesis) by InstantiateForall(y) + } + + /** + * Theorem -- generalization of `directImageSetUnion` + * The direct image of an arbitrary union is the union of the direct images + * f(⋃A) = ⋃(f(A)) + * + * This is why we needed the definition of `directImages`! + */ + val directImageUnionThm = Theorem( + (functionFrom(f, X, Y), A ⊆ powerSet(X)) |- + directImage(f, X, Y, union(A)) === union(directImages(f, X, Y, A)) + ) { + sorry + } + /** * Theorem -- direct image of the empty set * f(∅) = ∅ @@ -415,7 +460,7 @@ object DirectPreimages extends lisa.Main { ) { assume(functionFrom(f, X, Y)) - have(subset(emptySet, X) |- forall(z, z ∈ directImage(f, X, Y, emptySet) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ emptySet)))) by InstantiateForall(directImage(f, X, Y, emptySet))( + have(subset(emptySet, X) |- ∀(z, z ∈ directImage(f, X, Y, emptySet) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ emptySet)))) by InstantiateForall(directImage(f, X, Y, emptySet))( directImage.definition of (A := emptySet) ) thenHave(subset(emptySet, X) |- y ∈ directImage(f, X, Y, emptySet) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ emptySet))) by InstantiateForall(y) @@ -443,10 +488,10 @@ object DirectPreimages extends lisa.Main { ) { assume(functionFrom(f, X, Y), subset(A, X)) - have(forall(y, y ∈ relationRange(f) <=> ∃(x, in(pair(x, y), f)))) by InstantiateForall(relationRange(f))(relationRange.definition of (r := f)) + have(∀(y, y ∈ relationRange(f) <=> ∃(x, in(pair(x, y), f)))) by InstantiateForall(relationRange(f))(relationRange.definition of (r := f)) val defRange = thenHave(z ∈ relationRange(f) <=> ∃(x, in(pair(x, z), f))) by InstantiateForall(z) - have(forall(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) + have(∀(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) val defA = thenHave(z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) have(z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f)) subproof { @@ -470,7 +515,7 @@ object DirectPreimages extends lisa.Main { have(thesis) by Tautology.from(lastStep, defA) } - thenHave(forall(z, z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f))) by RightForall + thenHave(∀(z, z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f))) by RightForall have(thesis) by Tautology.from(subsetAxiom of (x := directImage(f, X, Y, A), y := functionRange(f)), lastStep) } @@ -487,7 +532,7 @@ object DirectPreimages extends lisa.Main { thenHave(thesis) by RightSubstEq.withParametersSimple(List((A, B)), lambda(x, in(z, directImage(f, X, Y, x)))) } have(A === B |- in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B))) by Tautology.from(lastStep, lastStep of (A := B, B := A)) - thenHave(A === B |- forall(z, in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B)))) by RightForall + thenHave(A === B |- ∀(z, in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B)))) by RightForall have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B))) } @@ -500,7 +545,7 @@ object DirectPreimages extends lisa.Main { |- directImage(f, X, Y, preimage(f, X, Y, A)) ⊆ A ) { assume(functionFrom(f, X, Y), subset(A, Y)) - have(subset(preimage(f, X, Y, A), X) |- forall(z, z ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A))))) by InstantiateForall( + have(subset(preimage(f, X, Y, A), X) |- ∀(z, z ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A))))) by InstantiateForall( directImage(f, X, Y, preimage(f, X, Y, A)) )(directImage.definition of (A := preimage(f, X, Y, A))) thenHave(subset(preimage(f, X, Y, A), X) |- z ∈ directImage(f, X, Y, preimage(f, X, Y, A)) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A)))) by InstantiateForall(z) @@ -513,7 +558,7 @@ object DirectPreimages extends lisa.Main { thenHave(exists(x, (app(f, x) === z) /\ x ∈ preimage(f, X, Y, A)) |- in(z, A)) by LeftExists have(thesis) by Tautology.from(lastStep, imageMember) } - thenHave(forall(z, in(z, directImage(f, X, Y, preimage(f, X, Y, A))) ==> in(z, A))) by RightForall + thenHave(∀(z, in(z, directImage(f, X, Y, preimage(f, X, Y, A))) ==> in(z, A))) by RightForall have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := directImage(f, X, Y, preimage(f, X, Y, A)), y := A)) } @@ -572,7 +617,7 @@ object DirectPreimages extends lisa.Main { ) { assume(functionFrom(f, X, Y)) - have(subset(X, X) |- forall(z, z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X)))) by InstantiateForall(directImage(f, X, Y, X))(directImage.definition of (A := X)) + have(subset(X, X) |- ∀(z, z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X)))) by InstantiateForall(directImage(f, X, Y, X))(directImage.definition of (A := X)) thenHave(subset(X, X) |- z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X))) by InstantiateForall(z) val defIm = have(z ∈ directImage(f, X, Y, X) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ X))) by Tautology.from( lastStep, diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala index f256b6fc6..ba5fdbebd 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala @@ -28,7 +28,7 @@ object Compactness extends lisa.Main { // A cover of X is a set of subsets of X that covers X (i.e. their union is a superset of X) val cover = DEF(X, O) --> - forall(o, in(o, O) ==> subset(o, X)) /\ + ∀(o, in(o, O) ==> subset(o, X)) /\ subset(X, union(O)) // Open cover is a cover that is a set of open sets! @@ -44,7 +44,7 @@ object Compactness extends lisa.Main { */ val compact = DEF(X, T) --> topology(X, T) /\ - forall( + ∀( O, openCover(X, T, O) ==> ∃( @@ -55,13 +55,45 @@ object Compactness extends lisa.Main { /** * Intermediate lemma for Heine-Borel theorem + * about covering the directImage with directImages */ val coverDirectImage = Theorem( (functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) |- cover(directImage(f, X, Y, X), directImages(f, X, Y, A)) ) { assume(functionFrom(f, X, Y), A ⊆ powerSet(X), cover(X, A)) - sorry + // Part 1 + have(X ⊆ union(A)) by Tautology.from(cover.definition of (X := X, O := A)) + have(directImage(f, X, Y, X) ⊆ directImage(f, X, Y, union(A))) by Tautology.from( + directImageMonotonicity of (A := X, B := union(A)), + subsetClosedUnion of (x := A, y := X), + lastStep + ) + val isSubset = have(directImage(f, X, Y, X) ⊆ union(directImages(f, X, Y, A))) by Tautology.from( + lastStep, + directImageUnionThm, + replaceEqualitySubsetRight of (z := directImage(f, X, Y, X), x := union(directImages(f, X, Y, A)), y := directImage(f, X, Y, union(A))) + ) + + // Part 2 + have(a ∈ A /\ (o === directImage(f, X, Y, a)) |- o ⊆ directImage(f, X, Y, X)) by Tautology.from( + subsetTactic of (x := A, y := powerSet(X), z := a), + powerAxiom of (x := a, y := X), + directImageMonotonicity of (A := a, B := X), + subsetReflexivity of (x := X), + equalityBySubset of (x := o, y := directImage(f, X, Y, a)), + subsetTransitivity of (a := o, b := directImage(f, X, Y, a), c := directImage(f, X, Y, X)) + ) + thenHave(∃(a, a ∈ A /\ (o === directImage(f, X, Y, a))) |- o ⊆ directImage(f, X, Y, X)) by LeftExists + have(o ∈ directImages(f, X, Y, A) |- o ⊆ directImage(f, X, Y, X)) by Tautology.from( + lastStep, + directImagesMembership of (y := o) + ) + thenHave(o ∈ directImages(f, X, Y, A) ==> o ⊆ directImage(f, X, Y, X)) by Tautology + val elementsSubsets = thenHave(∀(o, o ∈ directImages(f, X, Y, A) ==> o ⊆ directImage(f, X, Y, X))) by RightForall + + // Conclude + have(thesis) by Tautology.from(isSubset, elementsSubsets, cover.definition of (X := directImage(f, X, Y, X), O := directImages(f, X, Y, A))) } /** @@ -92,7 +124,7 @@ object Compactness extends lisa.Main { have(x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X)) by Tautology.from( preimagesMembership of (A := A, x := x) ) - thenHave(forall(x, x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X))) by RightForall + thenHave(∀(x, x ∈ preimages(f, X, Y, A) ==> x ∈ powerSet(X))) by RightForall have(thesis) by Tautology.from(lastStep, subsetAxiom of (x := preimages(f, X, Y, A), y := powerSet(X))) } @@ -120,17 +152,17 @@ object Compactness extends lisa.Main { val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) - val xIsCompact = have(forall(O, openCover(X, T1, O) ==> ∃(O2, subset(O2, O) /\ cover(X, O2) /\ finite(O2)))) by Tautology.from( + val xIsCompact = have(∀(O, openCover(X, T1, O) ==> ∃(O2, subset(O2, O) /\ cover(X, O2) /\ finite(O2)))) by Tautology.from( compact.definition of (T := T1) ) - val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) + val isContinuous = have(∀(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) have(openCover(Y, T2, O) |- ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) subproof { assume(openCover(Y, T2, O)) - have(forall(O, (O ⊆ T2) ==> (union(O) ∈ T2))) by Tautology.from( + have(∀(O, (O ⊆ T2) ==> (union(O) ∈ T2))) by Tautology.from( containsUnion.definition of (T := T2), yIsTop, topology.definition of (X := Y, T := T2) @@ -201,7 +233,7 @@ object Compactness extends lisa.Main { oInPowerSet ) } - thenHave(forall(x, x ∈ X ==> x ∈ union(preimages(f, X, Y, O)))) by RightForall + thenHave(∀(x, x ∈ X ==> x ∈ union(preimages(f, X, Y, O)))) by RightForall val secondPart = have(subset(X, union(preimages(f, X, Y, O)))) by Tautology.from( lastStep, subsetAxiom of (x := X, y := union(preimages(f, X, Y, O))) @@ -235,7 +267,7 @@ object Compactness extends lisa.Main { thenHave(∃(a, a ∈ O /\ (z === preimage(f, X, Y, a))) |- z ∈ T1) by LeftExists have(thesis) by Tautology.from(existsa, lastStep) } - thenHave(forall(z, z ∈ preimages(f, X, Y, O) ==> z ∈ T1)) by RightForall + thenHave(∀(z, z ∈ preimages(f, X, Y, O) ==> z ∈ T1)) by RightForall val isOpenSubset = have(preimages(f, X, Y, O) ⊆ T1) by Tautology.from( subsetAxiom of (x := preimages(f, X, Y, O), y := T1), lastStep @@ -309,7 +341,7 @@ object Compactness extends lisa.Main { // Since z is in directImages, then we precisely have ∃(a, a ∈ O3 /\ (z === directImage(f, X, Y, a))) by `directImagesMembership` have(thesis) by Tautology.from(lastStep, directImagesMembership of (A := O3, y := z), fIsFunction, o3InPowerSet) } - thenHave(forall(z, z ∈ directImages(f, X, Y, O3) ==> z ∈ O)) by RightForall + thenHave(∀(z, z ∈ directImages(f, X, Y, O3) ==> z ∈ O)) by RightForall val isSubset = have(directImages(f, X, Y, O3) ⊆ O) by Tautology.from(lastStep, subsetAxiom of (x := directImages(f, X, Y, O3), y := O)) // That is also covering Y @@ -353,7 +385,7 @@ object Compactness extends lisa.Main { have(thesis) by Tautology.from(lastStep, existsO3) } thenHave(openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2))) by Tautology - val yIsCompact = thenHave(forall(O, openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)))) by RightForall + val yIsCompact = thenHave(∀(O, openCover(Y, T2, O) ==> ∃(O2, subset(O2, O) /\ cover(Y, O2) /\ finite(O2)))) by RightForall have(thesis) by Tautology.from(yIsCompact, yIsTop, compact.definition of (X := Y, T := T2)) } diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala index 34e10f680..b7c7e6993 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Continuity.scala @@ -37,7 +37,7 @@ object Continuity extends lisa.Main { * A function f is continuous if the preimage of any open set in the codomain is open in the domain */ val continuous = DEF(f, X, T1, Y, T2) --> - (mapping(f, X, T1, Y, T2) /\ forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) + (mapping(f, X, T1, Y, T2) /\ ∀(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) // ------------------- // Connectedness @@ -55,7 +55,7 @@ object Continuity extends lisa.Main { */ val connectedTop = DEF(X, T) --> ( topology(X, T) /\ - forall(A, clopen(X, T, A) ==> ((A === emptySet) \/ (A === X))) + ∀(A, clopen(X, T, A) ==> ((A === emptySet) \/ (A === X))) ) /** @@ -68,8 +68,8 @@ object Continuity extends lisa.Main { val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) val yIsTop = have(topology(Y, T2)) by Tautology.from(continuous.definition, mapping.definition) - val xIsConnected = have(forall(A, clopen(X, T1, A) ==> ((A === emptySet) \/ (A === X)))) by Tautology.from(connectedTop.definition of (T := T1)) - val isContinuous = have(forall(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) + val xIsConnected = have(∀(A, clopen(X, T1, A) ==> ((A === emptySet) \/ (A === X)))) by Tautology.from(connectedTop.definition of (T := T1)) + val isContinuous = have(∀(O, O ∈ T2 ==> preimage(f, X, Y, O) ∈ T1)) by Tautology.from(continuous.definition) val fIsFunction = have(functionFrom(f, X, Y)) by Tautology.from(continuous.definition, mapping.definition) @@ -153,7 +153,7 @@ object Continuity extends lisa.Main { have(thesis) by Tautology.from(preImageIsConnected, firstCase, secondCase) } - val allClopen = thenHave(forall(A, clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y)))) by RightForall + val allClopen = thenHave(∀(A, clopen(Y, T2, A) ==> ((A === emptySet) \/ (A === Y)))) by RightForall have(connectedTop(Y, T2)) by Tautology.from(allClopen, yIsTop, connectedTop.definition of (X := Y, T := T2)) } } From 04178a024adc9d975ea12df34598e857bf32cbe8 Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Thu, 19 Dec 2024 11:31:05 +0100 Subject: [PATCH 36/38] Finish proof of `preimageUnionThm`/`directImageUnionThm` --- .../maths/settheory/SetTheoryBasics.scala | 4 + .../settheory/functions/DirectPreimages.scala | 331 ++++++++++++++---- 2 files changed, 260 insertions(+), 75 deletions(-) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala index 1f23990ec..ddf9211c8 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/SetTheoryBasics.scala @@ -213,4 +213,8 @@ object SetTheoryBasics extends lisa.Main { ) { have(thesis) by Tautology.from(equalityBySubset, equalityBySubset of (x := y, y := x)) } + + val equalityReflexivity = Theorem(x === x) { + have(thesis) by Tautology.from(equalityBySubset of (x := x, y := x)) + } } diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala index f0d64951a..9e9633470 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/functions/DirectPreimages.scala @@ -9,6 +9,7 @@ import lisa.maths.settheory.functions.Functionals.* import lisa.automation.settheory.SetTheoryTactics.UniqueComprehension import lisa.automation.settheory.SetTheoryTactics.TheConditional import lisa.maths.settheory.SetTheory.* +import javax.swing.text.html.HTML.Tag object DirectPreimages extends lisa.Main { @@ -168,6 +169,92 @@ object DirectPreimages extends lisa.Main { andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, setUnion(A, B)), y := setUnion(directImage(f, X, Y, A), directImage(f, X, Y, B))))) } + /** + * Theorem -- direct image of the empty set + * f(∅) = ∅ + */ + val directImageEmptySet = Theorem( + (functionFrom(f, X, Y)) + |- directImage(f, X, Y, emptySet) === emptySet + ) { + assume(functionFrom(f, X, Y)) + + have(subset(emptySet, X) |- ∀(z, z ∈ directImage(f, X, Y, emptySet) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ emptySet)))) by InstantiateForall(directImage(f, X, Y, emptySet))( + directImage.definition of (A := emptySet) + ) + thenHave(subset(emptySet, X) |- y ∈ directImage(f, X, Y, emptySet) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ emptySet))) by InstantiateForall(y) + val defA = have(y ∈ directImage(f, X, Y, emptySet) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ emptySet))) by Tautology.from(lastStep, emptySetIsASubset of (x := X)) + + val noElements = have(!in(y, directImage(f, X, Y, emptySet))) subproof { + assume(in(y, directImage(f, X, Y, emptySet))) + have((app(f, x) === y) /\ x ∈ emptySet |- x ∈ emptySet) by Tautology + have((app(f, x) === y) /\ x ∈ emptySet |- False) by Tautology.from(lastStep, emptySetAxiom) + thenHave(∃(x, (app(f, x) === y) /\ x ∈ emptySet) |- False) by LeftExists + have(False) by Tautology.from(lastStep, defA) + } + thenHave(∀(y, !in(y, directImage(f, X, Y, emptySet)))) by RightForall + + have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (x := directImage(f, X, Y, emptySet))) + } + + /** + * Theorem -- the direct image is always in the codomain + * f(A) ⊆ f(X) + */ + val directImageSubsetCodomain = Theorem( + (functionFrom(f, X, Y), subset(A, X)) + |- directImage(f, X, Y, A) ⊆ functionRange(f) + ) { + assume(functionFrom(f, X, Y), subset(A, X)) + + have(∀(y, y ∈ relationRange(f) <=> ∃(x, in(pair(x, y), f)))) by InstantiateForall(relationRange(f))(relationRange.definition of (r := f)) + val defRange = thenHave(z ∈ relationRange(f) <=> ∃(x, in(pair(x, z), f))) by InstantiateForall(z) + + have(∀(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) + val defA = thenHave(z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) + + have(z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f)) subproof { + assume(z ∈ directImage(f, X, Y, A)) + have(∃(x, (app(f, x) === z) /\ x ∈ A)) by Tautology.from(defA) + have(x ∈ A /\ (app(f, x) === z) |- pair(x, z) ∈ f) by Tautology.from( + pairInFunctionIsApp of (a := x, b := z), + functionFromImpliesFunctional of (x := X, y := Y), + subsetTactic of (x := A, y := X, z := x), + functionFromImpliesDomainEq of (x := X, y := Y), + replaceEqualityContainsRight of (x := functionDomain(f), y := X, z := x) + ) + thenHave(x ∈ A /\ (app(f, x) === z) |- ∃(x, pair(x, z) ∈ f)) by RightExists + have(x ∈ A /\ (app(f, x) === z) |- z ∈ relationRange(f)) by Tautology.from(lastStep, defRange) + thenHave(∃(x, x ∈ A /\ (app(f, x) === z)) |- z ∈ relationRange(f)) by LeftExists + have(∃(x, x ∈ A /\ (app(f, x) === z)) |- z ∈ relationRange(f) /\ z ∈ Y) by Tautology.from( + lastStep, + functionImpliesRangeSubsetOfCodomain of (x := X, y := Y), + subsetTactic of (x := relationRange(f), y := Y) + ) + have(thesis) by Tautology.from(lastStep, defA) + } + + thenHave(∀(z, z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f))) by RightForall + have(thesis) by Tautology.from(subsetAxiom of (x := directImage(f, X, Y, A), y := functionRange(f)), lastStep) + } + + /** + * Theorem -- congruence/substitution property for the direct image + * + * Needed as a lemma for other proofs + */ + val applyDirectImage = Theorem( + A === B |- directImage(f, X, Y, A) === directImage(f, X, Y, B) + ) { + have(((A === B), in(z, directImage(f, X, Y, A))) |- in(z, directImage(f, X, Y, B))) subproof { + have(((A === B), in(z, directImage(f, X, Y, A))) |- in(z, directImage(f, X, Y, A))) by Tautology + thenHave(thesis) by RightSubstEq.withParametersSimple(List((A, B)), lambda(x, in(z, directImage(f, X, Y, x)))) + } + have(A === B |- in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B))) by Tautology.from(lastStep, lastStep of (A := B, B := A)) + thenHave(A === B |- ∀(z, in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B)))) by RightForall + have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B))) + } + /** * Theorem -- the direct image is monotonic * @@ -409,7 +496,86 @@ object DirectPreimages extends lisa.Main { (functionFrom(f, X, Y), B ⊆ powerSet(Y)) |- preimage(f, X, Y, union(B)) === union(preimages(f, X, Y, B)) ) { - sorry + assume(functionFrom(f, X, Y), B ⊆ powerSet(Y)) + + val forward = have(x ∈ preimage(f, X, Y, union(B)) ==> x ∈ union(preimages(f, X, Y, B))) subproof { + assume(x ∈ preimage(f, X, Y, union(B))) + + val yInY = have(x ∈ X /\ app(f, x) ∈ y /\ y ∈ B |- y ⊆ Y) by Tautology.from( + subsetTactic of (x := B, y := powerSet(Y), z := y), + powerAxiom of (x := y, y := Y) + ) + + have(x ∈ X /\ app(f, x) ∈ y /\ y ∈ B |- y ∈ B /\ (preimage(f, X, Y, y) === preimage(f, X, Y, y))) by Tautology.from( + lastStep, + equalityReflexivity of (x := preimage(f, X, Y, y)) + ) + thenHave(x ∈ X /\ app(f, x) ∈ y /\ y ∈ B |- ∃(a, a ∈ B /\ (preimage(f, X, Y, y) === preimage(f, X, Y, a)))) by RightExists + have(x ∈ X /\ app(f, x) ∈ y /\ y ∈ B |- preimage(f, X, Y, y) ∈ preimages(f, X, Y, B)) by Tautology.from( + lastStep, + preimagesMembership of (A := B, x := preimage(f, X, Y, y)), + yInY, + preimageSubset of (A := y), + powerAxiom of (x := preimage(f, X, Y, y), y := X) + ) + have(x ∈ X /\ app(f, x) ∈ y /\ y ∈ B |- x ∈ preimage(f, X, Y, y) /\ preimage(f, X, Y, y) ∈ preimages(f, X, Y, B)) by Tautology.from( + lastStep, + preimageMembership of (B := y), + yInY + ) + thenHave(x ∈ X /\ app(f, x) ∈ y /\ y ∈ B |- ∃(z, x ∈ z /\ z ∈ preimages(f, X, Y, B))) by RightExists + have((x ∈ X, app(f, x) ∈ y /\ y ∈ B) |- x ∈ union(preimages(f, X, Y, B))) by Tautology.from( + lastStep, + unionAxiom of (z := x, x := preimages(f, X, Y, B)) + ) + thenHave((x ∈ X, ∃(y, app(f, x) ∈ y /\ y ∈ B)) |- x ∈ union(preimages(f, X, Y, B))) by LeftExists + have(thesis) by Tautology.from( + preimageMembership of (B := union(B)), + subsetClosedUnion of (x := B, y := Y), + unionAxiom of (z := app(f, x), x := B), + lastStep + ) + } + + val backward = have(x ∈ union(preimages(f, X, Y, B)) ==> x ∈ preimage(f, X, Y, union(B))) subproof { + assume(x ∈ union(preimages(f, X, Y, B))) + + val bSubsetY = have(b ∈ B |- b ⊆ Y) by Tautology.from( + subsetTactic of (x := B, y := powerSet(Y), z := b), + powerAxiom of (x := b, y := Y) + ) + have(x ∈ X /\ app(f, x) ∈ b /\ b ∈ B |- x ∈ X /\ app(f, x) ∈ b /\ b ∈ B) by Tautology + thenHave(x ∈ X /\ app(f, x) ∈ b /\ b ∈ B |- ∃(b, x ∈ X /\ app(f, x) ∈ b /\ b ∈ B)) by RightExists + have(x ∈ X /\ app(f, x) ∈ b /\ b ∈ B |- x ∈ preimage(f, X, Y, union(B))) by Tautology.from( + lastStep, + preimageMembership of (B := union(B)), + subsetClosedUnion of (x := B, y := Y), + unionAxiom of (z := app(f, x), x := B) + ) + have((x ∈ preimage(f, X, Y, b), b ∈ B) |- x ∈ preimage(f, X, Y, union(B))) by Tautology.from( + lastStep, + preimageMembership of (B := b), + bSubsetY + ) + have((x ∈ z, (z === preimage(f, X, Y, b)) /\ b ∈ B) |- x ∈ preimage(f, X, Y, union(B))) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := z, y := preimage(f, X, Y, b), z := x) + ) + thenHave((x ∈ z, ∃(b, (z === preimage(f, X, Y, b)) /\ b ∈ B)) |- x ∈ preimage(f, X, Y, union(B))) by LeftExists + have(x ∈ z /\ z ∈ preimages(f, X, Y, B) |- x ∈ preimage(f, X, Y, union(B))) by Tautology.from( + lastStep, + preimagesMembership of (A := B, x := z) + ) + thenHave(∃(z, x ∈ z /\ z ∈ preimages(f, X, Y, B)) |- x ∈ preimage(f, X, Y, union(B))) by LeftExists + have(thesis) by Tautology.from( + lastStep, + unionAxiom of (z := x, x := preimages(f, X, Y, B)) + ) + } + + have(x ∈ preimage(f, X, Y, union(B)) <=> x ∈ union(preimages(f, X, Y, B))) by RightIff(forward, backward) + thenHave(∀(x, x ∈ preimage(f, X, Y, union(B)) <=> x ∈ union(preimages(f, X, Y, B)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := preimage(f, X, Y, union(B)), y := union(preimages(f, X, Y, B))))) } inline def directImagesFormula = y ∈ s <=> (y ∈ powerSet(Y) /\ ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a)))) @@ -447,93 +613,108 @@ object DirectPreimages extends lisa.Main { (functionFrom(f, X, Y), A ⊆ powerSet(X)) |- directImage(f, X, Y, union(A)) === union(directImages(f, X, Y, A)) ) { - sorry - } + assume(functionFrom(f, X, Y), A ⊆ powerSet(X)) - /** - * Theorem -- direct image of the empty set - * f(∅) = ∅ - */ - val directImageEmptySet = Theorem( - (functionFrom(f, X, Y)) - |- directImage(f, X, Y, emptySet) === emptySet - ) { - assume(functionFrom(f, X, Y)) + val forward = have(z ∈ directImage(f, X, Y, union(A)) ==> z ∈ union(directImages(f, X, Y, A))) subproof { + assume(z ∈ directImage(f, X, Y, union(A))) - have(subset(emptySet, X) |- ∀(z, z ∈ directImage(f, X, Y, emptySet) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ emptySet)))) by InstantiateForall(directImage(f, X, Y, emptySet))( - directImage.definition of (A := emptySet) - ) - thenHave(subset(emptySet, X) |- y ∈ directImage(f, X, Y, emptySet) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ emptySet))) by InstantiateForall(y) - val defA = have(y ∈ directImage(f, X, Y, emptySet) <=> (y ∈ Y /\ ∃(x, (app(f, x) === y) /\ x ∈ emptySet))) by Tautology.from(lastStep, emptySetIsASubset of (x := X)) + have(z ∈ Y /\ (app(f, x) === z) /\ x ∈ y /\ y ∈ A |- directImage(f, X, Y, y) ∈ directImages(f, X, Y, A) /\ z ∈ directImage(f, X, Y, y)) subproof { + assume(z ∈ Y, app(f, x) === z, x ∈ y, y ∈ A) - val noElements = have(!in(y, directImage(f, X, Y, emptySet))) subproof { - assume(in(y, directImage(f, X, Y, emptySet))) - have((app(f, x) === y) /\ x ∈ emptySet |- x ∈ emptySet) by Tautology - have((app(f, x) === y) /\ x ∈ emptySet |- False) by Tautology.from(lastStep, emptySetAxiom) - thenHave(∃(x, (app(f, x) === y) /\ x ∈ emptySet) |- False) by LeftExists - have(False) by Tautology.from(lastStep, defA) - } - thenHave(∀(y, !in(y, directImage(f, X, Y, emptySet)))) by RightForall + val ySubsetX = have(y ⊆ X) by Tautology.from( + subsetTactic of (x := A, y := powerSet(X), z := y), + powerAxiom of (x := y, y := X) + ) - have(thesis) by Tautology.from(lastStep, setWithNoElementsIsEmpty of (x := directImage(f, X, Y, emptySet))) - } + val p1 = have(directImage(f, X, Y, y) ∈ powerSet(Y)) by Tautology.from( + powerAxiom of (x := directImage(f, X, Y, y), y := Y), + directImageSubsetCodomain of (A := y), + functionImpliesRangeSubsetOfCodomain of (x := X, y := Y), + subsetTransitivity of (a := directImage(f, X, Y, y), b := functionRange(f), c := Y), + subsetTactic of (x := A, y := powerSet(X), z := y), + powerAxiom of (x := y, y := X) + ) - /** - * Theorem -- the direct image is always in the codomain - * f(A) ⊆ f(X) - */ - val directImageSubset = Theorem( - (functionFrom(f, X, Y), subset(A, X)) - |- directImage(f, X, Y, A) ⊆ functionRange(f) - ) { - assume(functionFrom(f, X, Y), subset(A, X)) + have(y ∈ A /\ (directImage(f, X, Y, y) === directImage(f, X, Y, y))) by Tautology.from( + equalityReflexivity of (x := y) + ) + val p2 = thenHave(∃(a, a ∈ A /\ (directImage(f, X, Y, y) === directImage(f, X, Y, a)))) by RightExists - have(∀(y, y ∈ relationRange(f) <=> ∃(x, in(pair(x, y), f)))) by InstantiateForall(relationRange(f))(relationRange.definition of (r := f)) - val defRange = thenHave(z ∈ relationRange(f) <=> ∃(x, in(pair(x, z), f))) by InstantiateForall(z) + val p3 = have(directImage(f, X, Y, y) ∈ directImages(f, X, Y, A)) by Tautology.from(p1, p2, directImagesMembership of (y := directImage(f, X, Y, y))) - have(∀(z, z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A)))) by InstantiateForall(directImage(f, X, Y, A))(directImage.definition) - val defA = thenHave(z ∈ directImage(f, X, Y, A) <=> (z ∈ Y /\ ∃(x, (app(f, x) === z) /\ x ∈ A))) by InstantiateForall(z) + have((app(f, x) === z) /\ x ∈ y) by Tautology + thenHave(exists(x, (app(f, x) === z) /\ x ∈ y)) by RightExists + val p4 = have(z ∈ directImage(f, X, Y, y)) by Tautology.from(lastStep, directImageMembership of (y := z, A := y), ySubsetX) - have(z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f)) subproof { - assume(z ∈ directImage(f, X, Y, A)) - have(∃(x, (app(f, x) === z) /\ x ∈ A)) by Tautology.from(defA) - have(x ∈ A /\ (app(f, x) === z) |- pair(x, z) ∈ f) by Tautology.from( - pairInFunctionIsApp of (a := x, b := z), - functionFromImpliesFunctional of (x := X, y := Y), - subsetTactic of (x := A, y := X, z := x), - functionFromImpliesDomainEq of (x := X, y := Y), - replaceEqualityContainsRight of (x := functionDomain(f), y := X, z := x) + have(thesis) by Tautology.from(p3, p4) + } + thenHave(z ∈ Y /\ (app(f, x) === z) /\ x ∈ y /\ y ∈ A |- directImage(f, X, Y, y) ∈ directImages(f, X, Y, A) /\ z ∈ directImage(f, X, Y, y)) by Tautology + thenHave(z ∈ Y /\ (app(f, x) === z) /\ x ∈ y /\ y ∈ A |- ∃(y, y ∈ directImages(f, X, Y, A) /\ z ∈ y)) by RightExists + have((z ∈ Y, (app(f, x) === z), x ∈ y /\ y ∈ A) |- z ∈ union(directImages(f, X, Y, A))) by Tautology.from( + lastStep, + unionAxiom of (x := directImages(f, X, Y, A)) ) - thenHave(x ∈ A /\ (app(f, x) === z) |- ∃(x, pair(x, z) ∈ f)) by RightExists - have(x ∈ A /\ (app(f, x) === z) |- z ∈ relationRange(f)) by Tautology.from(lastStep, defRange) - thenHave(∃(x, x ∈ A /\ (app(f, x) === z)) |- z ∈ relationRange(f)) by LeftExists - have(∃(x, x ∈ A /\ (app(f, x) === z)) |- z ∈ relationRange(f) /\ z ∈ Y) by Tautology.from( + thenHave((z ∈ Y, (app(f, x) === z), ∃(y, y ∈ A /\ x ∈ y)) |- z ∈ union(directImages(f, X, Y, A))) by LeftExists + have(z ∈ Y /\ (app(f, x) === z) /\ x ∈ union(A) |- z ∈ union(directImages(f, X, Y, A))) by Tautology.from( lastStep, - functionImpliesRangeSubsetOfCodomain of (x := X, y := Y), - subsetTactic of (x := relationRange(f), y := Y) + unionAxiom of (z := x, x := A) + ) + thenHave(∃(x, z ∈ Y /\ (app(f, x) === z) /\ x ∈ union(A)) |- z ∈ union(directImages(f, X, Y, A))) by LeftExists + have(thesis) by Tautology.from( + lastStep, + subsetClosedUnion of (x := A, y := X), + directImageMembership of (y := z, A := union(A)) ) - have(thesis) by Tautology.from(lastStep, defA) } - thenHave(∀(z, z ∈ directImage(f, X, Y, A) ==> z ∈ functionRange(f))) by RightForall - have(thesis) by Tautology.from(subsetAxiom of (x := directImage(f, X, Y, A), y := functionRange(f)), lastStep) - } - - /** - * Theorem -- congruence/substitution property for the direct image - * - * Needed as a lemma for other proofs - */ - val applyDirectImage = Theorem( - A === B |- directImage(f, X, Y, A) === directImage(f, X, Y, B) - ) { - have(((A === B), in(z, directImage(f, X, Y, A))) |- in(z, directImage(f, X, Y, B))) subproof { - have(((A === B), in(z, directImage(f, X, Y, A))) |- in(z, directImage(f, X, Y, A))) by Tautology - thenHave(thesis) by RightSubstEq.withParametersSimple(List((A, B)), lambda(x, in(z, directImage(f, X, Y, x)))) + val backward = have(z ∈ union(directImages(f, X, Y, A)) ==> z ∈ directImage(f, X, Y, union(A))) subproof { + have(z ∈ Y /\ (app(f, x) === z) /\ x ∈ a /\ a ∈ A |- z ∈ Y /\ (app(f, x) === z) /\ x ∈ a /\ a ∈ A) by Tautology + thenHave(z ∈ Y /\ (app(f, x) === z) /\ x ∈ a /\ a ∈ A |- ∃(a, z ∈ Y /\ (app(f, x) === z) /\ x ∈ a /\ a ∈ A)) by RightExists + have(z ∈ Y /\ (app(f, x) === z) /\ x ∈ a /\ a ∈ A |- z ∈ Y /\ (app(f, x) === z) /\ x ∈ union(A)) by Tautology.from( + lastStep, + unionAxiom of (z := x, x := A) + ) + thenHave(z ∈ Y /\ (app(f, x) === z) /\ x ∈ a /\ a ∈ A |- ∃(x, z ∈ Y /\ (app(f, x) === z) /\ x ∈ union(A))) by RightExists + have((z ∈ Y, (app(f, x) === z) /\ x ∈ a, a ∈ A) |- z ∈ directImage(f, X, Y, union(A))) by Tautology.from( + lastStep, + directImageMembership of (y := z, A := union(A)), + subsetClosedUnion of (x := A, y := X) + ) + thenHave((z ∈ Y, ∃(x, (app(f, x) === z) /\ x ∈ a), a ∈ A) |- z ∈ directImage(f, X, Y, union(A))) by LeftExists + have(z ∈ directImage(f, X, Y, a) /\ a ∈ A |- z ∈ directImage(f, X, Y, union(A))) by Tautology.from( + lastStep, + directImageMembership of (y := z, A := a), + subsetClosedUnion of (x := A, y := X), + subsetTactic of (x := A, y := powerSet(X), z := a), + powerAxiom of (x := a, y := X) + ) + have((z ∈ y, a ∈ A /\ (y === directImage(f, X, Y, a)), y ∈ powerSet(Y)) |- z ∈ directImage(f, X, Y, union(A))) by Tautology.from( + lastStep, + replaceEqualityContainsRight of (x := y, y := directImage(f, X, Y, a)), + directImageSubsetCodomain of (A := a), + functionImpliesRangeSubsetOfCodomain of (x := X, y := Y), + subsetTransitivity of (a := directImage(f, X, Y, a), b := functionRange(f), c := Y), + powerAxiom of (x := directImage(f, X, Y, a), y := Y), + replaceEqualityContainsLeft of (x := y, y := directImage(f, X, Y, a), z := powerSet(Y)) + ) + thenHave( + (z ∈ y, ∃(a, a ∈ A /\ (y === directImage(f, X, Y, a))), y ∈ powerSet(Y)) + |- z ∈ directImage(f, X, Y, union(A)) + ) by LeftExists + have(z ∈ y /\ y ∈ directImages(f, X, Y, A) |- z ∈ directImage(f, X, Y, union(A))) by Tautology.from( + lastStep, + directImagesMembership + ) + thenHave(∃(y, z ∈ y /\ y ∈ directImages(f, X, Y, A)) |- z ∈ directImage(f, X, Y, union(A))) by LeftExists + have(z ∈ union(directImages(f, X, Y, A)) |- z ∈ directImage(f, X, Y, union(A))) by Tautology.from( + lastStep, + unionAxiom of (x := directImages(f, X, Y, A)) + ) } - have(A === B |- in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B))) by Tautology.from(lastStep, lastStep of (A := B, B := A)) - thenHave(A === B |- ∀(z, in(z, directImage(f, X, Y, A)) <=> in(z, directImage(f, X, Y, B)))) by RightForall - have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (x := directImage(f, X, Y, A), y := directImage(f, X, Y, B))) + + have(z ∈ directImage(f, X, Y, union(A)) <=> z ∈ union(directImages(f, X, Y, A))) by RightIff(forward, backward) + thenHave(∀(z, z ∈ directImage(f, X, Y, union(A)) <=> z ∈ union(directImages(f, X, Y, A)))) by RightForall + andThen(Substitution.applySubst(extensionalityAxiom of (x := directImage(f, X, Y, union(A)), y := union(directImages(f, X, Y, A))))) } /** @@ -625,7 +806,7 @@ object DirectPreimages extends lisa.Main { ) val forward = have(z ∈ directImage(f, X, Y, X) ==> z ∈ functionRange(f)) by Tautology.from( - directImageSubset of (A := X), + directImageSubsetCodomain of (A := X), subsetReflexivity of (x := X), subsetTactic of (x := directImage(f, X, Y, X), y := functionRange(f)) ) From 6bc4f1cc2196ad11e7ae31fef612527f8472e3de Mon Sep 17 00:00:00 2001 From: Jonathan Arnoult Date: Sun, 22 Dec 2024 18:40:55 +0100 Subject: [PATCH 37/38] Acutally not Heine-Borel --- .../src/main/scala/lisa/maths/topology/Compactness.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala index ba5fdbebd..a6fcc1233 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/Compactness.scala @@ -54,7 +54,7 @@ object Compactness extends lisa.Main { ) /** - * Intermediate lemma for Heine-Borel theorem + * Intermediate lemma for theorem below * about covering the directImage with directImages */ val coverDirectImage = Theorem( @@ -142,11 +142,9 @@ object Compactness extends lisa.Main { } /** - * Heine-Borel theorem - * * The image of a compact space by a continuous, surjective mapping is a compact space */ - val heineBorelThm = Theorem((compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- compact(Y, T2)) { + val imageCompactThm = Theorem((compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) |- compact(Y, T2)) { assume(compact(X, T1), continuous(f, X, T1, Y, T2), surjective(f, X, Y)) val xIsTop = have(topology(X, T1)) by Tautology.from(continuous.definition, mapping.definition) From 4d29c57f4f01f2f2d0d5446fda33c7656e1765d3 Mon Sep 17 00:00:00 2001 From: Dobrin Bashev Date: Sun, 5 Jan 2025 16:54:59 +0100 Subject: [PATCH 38/38] Add documentation for SingletonSet file --- .../lisa/maths/topology/SingletonSet.scala | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala index 5991782ed..6e44a9ac4 100644 --- a/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala +++ b/lisa-topology/src/main/scala/lisa/maths/topology/SingletonSet.scala @@ -22,9 +22,14 @@ object SingletonSet extends lisa.Main { private val X, T = variable private val S, A, B, Y = variable - val singletonSetsUniquenes = Theorem( + /* + Theorem: singletonSets exists and it is unique + exists unique set z whose members are {x} for each x in S + */ + val singletonSetsUniqueness = Theorem( ∃!(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x))))) ) { + // prove that {x} for each x in S is member of U(SxS) val implicationProof = have(exists(x, in(x, S) /\ (t === singleton(x))) ==> in(t, union(cartesianProduct(S, S)))) subproof { val elementInSingleton = have(in(x, singleton(x))) subproof { @@ -33,13 +38,13 @@ object SingletonSet extends lisa.Main { val singletonInPowerSet = have(in(x, S) |- in(singleton(x), powerSet(S))) subproof { assume(in(x, S)) - val introduceY = thenHave((x === y) |- in(y, S)) by Substitution.ApplyRules(x === y) - val useSingleton = have(in(y, singleton(x)) |- in(y, S)) by Tautology.from(introduceY, singletonHasNoExtraElements) + thenHave((x === y) |- in(y, S)) by Substitution.ApplyRules(x === y) + have(in(y, singleton(x)) |- in(y, S)) by Tautology.from(lastStep, singletonHasNoExtraElements) thenHave(() |- (!(in(y, singleton(x))), in(y, S))) by RightNot thenHave(() |- in(y, singleton(x)) ==> in(y, S)) by Tautology - val universalY = thenHave(() |- forall(y, in(y, singleton(x)) ==> in(y, S))) by RightForall - val singletonIsSubset = have(() |- singleton(x) ⊆ S) by Tautology.from(universalY, subsetAxiom of (z := y, y := S, x := singleton(x))) - have(thesis) by Tautology.from(singletonIsSubset, powerAxiom of (x := singleton(x), y := S)) + thenHave(() |- forall(y, in(y, singleton(x)) ==> in(y, S))) by RightForall + have(() |- singleton(x) ⊆ S) by Tautology.from(lastStep, subsetAxiom of (z := y, y := S, x := singleton(x))) + have(thesis) by Tautology.from(lastStep, powerAxiom of (x := singleton(x), y := S)) } val abExist = have((singleton(t) === pair(x, x)) /\ in(x, S) |- ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) subproof { @@ -48,6 +53,7 @@ object SingletonSet extends lisa.Main { thenHave((singleton(t) === pair(x, x)) /\ in(x, S) |- exists(a, exists(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) by RightExists } + // replace the cartesianProduct with its definitions using powerSet and setUnion val cartesianInstantiated = have( in(singleton(t), cartesianProduct(S, S)) <=> (in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S)))) @@ -94,37 +100,39 @@ object SingletonSet extends lisa.Main { ) by InstantiateForall(S) } - val init = have(in(x, S) |- in(x, S)) by Restate - val inSS = have(in(x, S) |- in(x, setUnion(S, S))) by Tautology.from(init, setUnionMembership of (z := x, x := S, y := S)) - val step1 = have(in(x, S) |- in(singleton(x), powerSet(setUnion(S, S))) /\ (singleton(singleton(x)) === pair(x, x))) by - Tautology.from(inSS, singletonInPowerSet of (S := setUnion(S, S))) + have(in(x, S) |- in(x, S)) by Restate + have(in(x, S) |- in(x, setUnion(S, S))) by Tautology.from(lastStep, setUnionMembership of (z := x, x := S, y := S)) + have(in(x, S) |- in(singleton(x), powerSet(setUnion(S, S))) /\ (singleton(singleton(x)) === pair(x, x))) by + Tautology.from(lastStep, singletonInPowerSet of (S := setUnion(S, S))) thenHave((in(x, S), t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by Substitution.ApplyRules(singleton(x) === t) - val introduceT = thenHave(in(x, S) /\ (t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by Tautology - val step2 = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ (singleton(t) === pair(x, x))) by - Tautology.from(introduceT, singletonInPowerSet of (x := t, S := powerSet(setUnion(S, S)))) - val extendRHS = have( + thenHave(in(x, S) /\ (t === singleton(x)) |- in(t, powerSet(setUnion(S, S))) /\ (singleton(t) === pair(x, x))) by Tautology + have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ (singleton(t) === pair(x, x))) by + Tautology.from(lastStep, singletonInPowerSet of (x := t, S := powerSet(setUnion(S, S)))) + have( in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) /\ ((singleton(t) === pair(x, x)) /\ in(x, S)) - ) by Tautology.from(step2, elementInSingleton of (x := t)) - val existAB = have( + ) by Tautology.from(lastStep, elementInSingleton of (x := t)) + have( in(x, S) /\ (t === singleton(x)) |- in(singleton(t), powerSet(powerSet(setUnion(S, S)))) /\ in(t, singleton(t)) /\ ∃(a, ∃(b, (singleton(t) === pair(a, b)) /\ in(a, S) /\ in(b, S))) - ) by Tautology.from(extendRHS, abExist) - val cartesian = have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), cartesianProduct(S, S)) /\ in(t, singleton(t))) by - Tautology.from(existAB, cartesianInstantiated) - val introduceZ = thenHave(in(x, S) /\ (t === singleton(x)) |- exists(z, in(z, cartesianProduct(S, S)) /\ in(t, z))) by RightExists - val unionOfProduct = have((in(x, S) /\ (t === singleton(x))) |- in(t, union(cartesianProduct(S, S)))) by - Tautology.from(introduceZ, unionAxiom of (y := z, x := cartesianProduct(S, S), z := t)) + ) by Tautology.from(lastStep, abExist) + have(in(x, S) /\ (t === singleton(x)) |- in(singleton(t), cartesianProduct(S, S)) /\ in(t, singleton(t))) by + Tautology.from(lastStep, cartesianInstantiated) + thenHave(in(x, S) /\ (t === singleton(x)) |- exists(z, in(z, cartesianProduct(S, S)) /\ in(t, z))) by RightExists + have((in(x, S) /\ (t === singleton(x))) |- in(t, union(cartesianProduct(S, S)))) by + Tautology.from(lastStep, unionAxiom of (y := z, x := cartesianProduct(S, S), z := t)) thenHave(exists(x, (in(x, S) /\ (t === singleton(x)))) |- in(t, union(cartesianProduct(S, S)))) by LeftExists } + + // use unique comprehension from original set U(SxS) with the provided predicate and the membership proof have(() |- existsOne(z, forall(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))) by UniqueComprehension.fromOriginalSet( union(cartesianProduct(S, S)), lambda(t, exists(x, in(x, S) /\ (t === singleton(x)))), implicationProof ) } - val singletonSets = DEF(S) --> The(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))(singletonSetsUniquenes) + val singletonSets = DEF(S) --> The(z, ∀(t, in(t, z) <=> exists(x, in(x, S) /\ (t === singleton(x)))))(singletonSetsUniqueness) val singletonSetsMembershipRaw = Theorem( in(t, singletonSets(S)) <=> exists(x, ((t === singleton(x)) /\ in(x, S))) @@ -133,6 +141,9 @@ object SingletonSet extends lisa.Main { thenHave(thesis) by InstantiateForall(t) } + /* + Theorem: x is member of S iff {x} is member of singletonSets(S) + */ val singletonSetsMembership = Theorem( in(x, S) <=> in(singleton(x), singletonSets(S)) ) { @@ -158,10 +169,14 @@ object SingletonSet extends lisa.Main { have(thesis) by Tautology.from(forward, backward) } + /* + Theorem: union of sinletonSets(S) is equivalent to S + */ val unionSingletonSets = Theorem(union(singletonSets(S)) === S) { val elementInSingleton = have(in(x, singleton(x))) by Tautology.from(pairAxiom of (x := x, y := x, z := x)) + // prove that all elements of the union of singletonSets(S) are in S val fwd = have(in(x, union(singletonSets(S))) |- in(x, S)) subproof { have(in(z, S) |- in(z, S)) by Restate thenHave((in(z, S), (x === z)) |- in(x, S)) by Substitution.ApplyRules(x === z) @@ -175,6 +190,7 @@ object SingletonSet extends lisa.Main { have(thesis) by Tautology.from(lastStep, unionAxiom of (z := x, x := singletonSets(S))) } + // prove that all elements in S are in the union of singletonSets(S) val bwd = have(in(x, S) |- in(x, union(singletonSets(S)))) subproof { assume(in(x, S)) have(in(x, S) /\ (singleton(x) === singleton(x))) by Restate @@ -190,6 +206,9 @@ object SingletonSet extends lisa.Main { have(thesis) by Tautology.from(lastStep, extensionalityAxiom of (z := x, x := union(singletonSets(S)), y := S)) } + /* + Theorem: a topology which contains all singleton sets of the elements of the ground set is the discrete one + */ val ifContainsSingletonIsDiscrete = Theorem( (topology(X, T), ∀(x, x ∈ X ==> singleton(x) ∈ T)) |- discreteTopology(X, T) ) {