Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running solvers on TPTP + converting kernel proofs to Lisa code #208

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2d5ee19
Add first draft for kernel2tactic transformation
augustepoiroux Feb 12, 2024
1ff80d9
Add proofsteps reference
augustepoiroux Feb 12, 2024
94f9a6b
Fix some kernel2code issues when refering to previous steps
augustepoiroux Feb 12, 2024
daa2673
First stable conversion from kernel to code proofs
augustepoiroux Feb 12, 2024
49acfbf
Refactor code + thenHave
augustepoiroux Feb 12, 2024
22322b3
Handle more cases
augustepoiroux Feb 12, 2024
38a73bb
Add todo list
augustepoiroux Feb 12, 2024
386ab96
Update and clean scproof2code method
augustepoiroux Feb 12, 2024
f23659f
Refactor files
augustepoiroux Feb 12, 2024
392b1d6
Update + fix TPTP problem parser
augustepoiroux Feb 12, 2024
b1b6308
Add TPTPSolver class for solving problems from TPTP library using Tab…
augustepoiroux Feb 12, 2024
0799347
Update TPTP problem solver code
augustepoiroux Feb 12, 2024
cdd2c12
Separate the solver runner logic from the TPTP data extraction
augustepoiroux Feb 12, 2024
07c4f3d
Fix and improve kernel2code and asFront methods
augustepoiroux Feb 12, 2024
5dd0c46
Fix Sequent toString method
augustepoiroux Feb 12, 2024
860fc1f
Add proof checks in TPTPSolver
augustepoiroux Feb 12, 2024
1608480
Improve conversion from kernel to code
augustepoiroux Feb 12, 2024
d83b952
Add standalone file generator for theorems and proofs + cleanup
augustepoiroux Feb 12, 2024
dd21c0f
Add documentation to the lisa proof code generator
augustepoiroux Feb 12, 2024
15bf010
Fix TPTP parsing of atomic formula
augustepoiroux Feb 12, 2024
57a57f0
Improve the Lisa code generator from SCProof
augustepoiroux Feb 12, 2024
4a6bbbb
Export proofs extracted from TPTP to JSON
augustepoiroux Feb 12, 2024
531ba96
Fix equality infix printing + parsing issues
augustepoiroux Feb 12, 2024
81fb6da
Remove user-specific paths
augustepoiroux Feb 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.idea
.metals
.vscode
project/metals.sbt
*/metals.sbt

# build-related
.bsp
Expand All @@ -22,3 +22,9 @@ cache

# emacs backup files
*~

# data extracted
data_extract

# TPTP data
lisa-utils/src/main/resources
31 changes: 13 additions & 18 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ThisBuild / version := "0.6"
ThisBuild / version := "0.6"
ThisBuild / homepage := Some(url("https://github.com/epfl-lara/lisa"))
ThisBuild / startYear := Some(2021)
ThisBuild / organization := "ch.epfl.lara"
Expand All @@ -7,24 +7,20 @@ 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
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.6.0"




val commonSettings = Seq(
crossScalaVersions := Seq("2.12.13", "2.13.4", "3.0.1", "3.2.0"),
run / fork := true
)


val scala2 = "2.13.8"
val scala3 = "3.3.1"

Expand All @@ -35,14 +31,15 @@ val commonSettings2 = commonSettings ++ Seq(
val commonSettings3 = commonSettings ++ Seq(
scalaVersion := scala3,
scalacOptions ++= Seq(
"-language:implicitConversions",
"-language:implicitConversions"

// "-source:future", re-enable when liancheng/scalafix-organize-imports#221 is fixed

),
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test",
libraryDependencies += "com.lihaoyi" %% "sourcecode" % "0.3.0",
//libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "2.1.1",
libraryDependencies += "com.lihaoyi" %% "ujson" % "3.1.0",
// libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "2.1.1",
libraryDependencies += ("io.github.uuverifiers" %% "princess" % "2023-06-19").cross(CrossVersion.for3Use2_13),
Test / parallelExecution := false
)
Expand All @@ -56,13 +53,13 @@ lazy val scallion = githubProject("https://github.com/sankalpgambhir/scallion.gi

lazy val silex = githubProject("https://github.com/epfl-lara/silex.git", "fc07a8670a5fa8ea2dd5649a00424710274a5d18")

scallion/scalacOptions ~= (_.filterNot(Set("-Wvalue-discard")))
silex/scalacOptions ~= (_.filterNot(Set("-Wvalue-discard")))
scallion / scalacOptions ~= (_.filterNot(Set("-Wvalue-discard")))
silex / scalacOptions ~= (_.filterNot(Set("-Wvalue-discard")))

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
Expand All @@ -87,7 +84,6 @@ lazy val utils = Project(
id = "lisa-utils",
base = file("lisa-utils")
)

.settings(commonSettings3)
.dependsOn(kernel)
.dependsOn(silex)
Expand All @@ -104,7 +100,6 @@ ThisBuild / assemblyMergeStrategy := {
oldStrategy(x)
}


lazy val examples = Project(
id = "lisa-examples",
base = file("lisa-examples")
Expand Down
328 changes: 328 additions & 0 deletions lisa-examples/src/main/scala/Kernel2Code.scala

Large diffs are not rendered by default.

135 changes: 135 additions & 0 deletions lisa-examples/src/main/scala/TPTPSolver.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import java.io.File
import java.io.FileWriter

import scala.concurrent.duration._

import lisa.utils.ProofsConverter.*
import lisa.utils.RunSolver.*
import lisa.utils.tptp.*
import lisa.utils.tptp.KernelParser.*
import lisa.kernel.proof.SCProof
import lisa.kernel.proof.SequentCalculus.Sequent
import lisa.utils.ProofsShrink.optimizeProofIteratively

// TODO: separate axioms and definitions from the main sequent and give names to hypotheses

object TPTPSolver extends lisa.Main {
sealed trait ProofType
case object BySolver extends ProofType
case object Kernel extends ProofType
case object KernelOptimized extends ProofType

class ProblemSolverResults(val problem: Problem, val solverName: String, val solverStatus: String, val proofCode: String, val proofType: ProofType)

val spc = Seq("PRP", "FOF") // type of problems we want to extract and solve

// We limit the execution time to solve each problem
val timeoutTableau = .1.second
val timeoutTautology = .1.second

val exportOnlySolvedProblems = true
val exportOptimizedProofs = true
val exportBySolverProofs = true

val jsonResultsPath: String = null
if (jsonResultsPath == null) throw new Exception("Please specify a path for the JSON results file")
val TPTPProblemPath: String = null
if (TPTPProblemPath == null) throw new Exception("Please specify a path for the TPTP problems")

val d = new File(TPTPProblemPath)
val libraries = d.listFiles.filter(_.isDirectory)
val probfiles = libraries.flatMap(_.listFiles).filter(_.isFile)

// val d = new File(TPTPProblemPath + "SYN/")
// val probfiles = d.listFiles.filter(_.isFile)

var nbProblemsExtracted = 0
var nbProblemsSolved = Map("Tableau" -> 0, "Tautology" -> 0)
var nbInvalidProofs = Map("Tableau" -> 0, "Tautology" -> 0)
var results = Seq.empty[ProblemSolverResults]

for ((probfile, i) <- probfiles.zipWithIndex) {
// Progress bar
if ((i + 1) % 10 == 0 || i + 1 == probfiles.size) {
val pbarLength = 20
var pbarContent = "=" * (((i + 1) * pbarLength) / probfiles.size)
pbarContent += " " * (pbarLength - pbarContent.length)
print(s"[$pbarContent]")
print(s" -- ${i + 1}/${probfiles.size} processed files")
print(s" -- $nbProblemsExtracted extracted problems")
print(s" -- Tableau: ${nbProblemsSolved("Tableau")} solved + ${nbInvalidProofs("Tableau")} invalid")
println(s" -- Tautology: ${nbProblemsSolved("Tautology")} solved + ${nbInvalidProofs("Tautology")} invalid")
}

// Try to extract and solve the problem
try {
val md = getProblemInfos(probfile)
if (!(md.spc.contains("SAT"))) {
if (md.spc.exists(spc.contains)) {
val problem = problemToKernel(probfile, md)
val seq = problemToSequent(problem)
nbProblemsExtracted += 1

def exportResults(problem: Problem, solverName: String, solverResult: SolverResult): Unit =
val solverStatus = solverResult.getClass.getSimpleName.stripSuffix("$")
solverResult match
case Solved(proof) =>
nbProblemsSolved += (solverName -> (nbProblemsSolved(solverName) + 1))
results :+= new ProblemSolverResults(problem, solverName, solverStatus, generateStandaloneTheoremFileContent(problem.name, proof), Kernel)
if (exportOptimizedProofs)
results :+= new ProblemSolverResults(problem, solverName, solverStatus, generateStandaloneTheoremFileContent(problem.name, optimizeProofIteratively(proof)), KernelOptimized)
if (exportBySolverProofs)
val statementString = any2code(seq)
val proofCode = s"have(thesis) by $solverName"
val symbolDeclarations = generateSymbolDeclarationCode(Set(K.sequentToFormula(seq)), "private")
results :+= new ProblemSolverResults(problem, solverName, solverStatus, generateStandaloneTheoremFileContent(problem.name, statementString, proofCode, symbolDeclarations), BySolver)
case InvalidProof(proof) =>
nbInvalidProofs += (solverName -> (nbInvalidProofs(solverName) + 1))
if (!exportOnlySolvedProblems)
results :+= new ProblemSolverResults(problem, solverName, solverStatus, generateStandaloneTheoremFileContent(problem.name, proof), Kernel)
case _ =>
if (!exportOnlySolvedProblems)
results :+= new ProblemSolverResults(problem, solverName, solverStatus, "", Kernel)

// Attempting proof by Tableau
val tableauResult = proveSequent(seq, timeoutTableau, Tableau.solve)
exportResults(problem, "Tableau", tableauResult)

// Attempting proof by Tautology
def tautologySolver(s: lisa.utils.K.Sequent): Option[SCProof] = Tautology.solveSequent(s) match
case Left(proof) => Some(proof)
case _ => None
val tautologyResult = proveSequent(seq, timeoutTautology, tautologySolver)
exportResults(problem, "Tautology", tautologyResult)
}
// } else println(s"Problem $probfile not extracted because of its type: ${md.spc.mkString(",")}")
}
} catch case e => println(s"Error while processing $probfile (index $i): $e")
}

// Write results to a JSON file
val jsonResultsFile = new File(jsonResultsPath)
if (!jsonResultsFile.getParentFile.exists())
jsonResultsFile.getParentFile.mkdirs()
val jsonWriter = new java.io.PrintWriter(jsonResultsPath)
ujson.writeTo(
results.map(r =>
ujson.Obj(
"problemName" -> r.problem.name,
"problemDomain" -> r.problem.domain,
"problemStatus" -> r.problem.status,
"problemSPC" -> r.problem.spc.mkString(","),
"problemSequent" -> any2code(problemToSequent(r.problem)),
"problemFile" -> r.problem.file,
"solver" -> r.solverName,
"solverStatus" -> r.solverStatus,
"proofType" -> r.proofType.getClass.getSimpleName.stripSuffix("$"),
"solverProofCode" -> r.proofCode
)
),
jsonWriter,
indent = 2
)
jsonWriter.close()

}
9 changes: 7 additions & 2 deletions lisa-utils/src/main/scala/lisa/fol/Common.scala
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,8 @@ trait Common {
def rename(newid: Identifier): ConstantPredicateLabel[N] = ConstantPredicateLabel(newid, arity)
def freshRename(taken: Iterable[Identifier]): ConstantPredicateLabel[N] = rename(K.freshId(taken, id))
override def toString(): String = id
def mkString(args: Seq[Term]): String = if (infix) (args(0).toStringSeparated() + " " + toString() + " " + args(1).toStringSeparated()) else toString() + "(" + args.mkString(", ") + ")"
def mkString(args: Seq[Term]): String =
if (infix) ("(" + args(0).toStringSeparated() + " " + toString() + " " + args(1).toStringSeparated() + ")") else toString() + "(" + args.mkString(", ") + ")"
override def mkStringSeparated(args: Seq[Term]): String = if (infix) "(" + mkString(args) + ")" else mkString(args)
}
object ConstantPredicateLabel {
Expand Down Expand Up @@ -708,7 +709,11 @@ trait Common {
def rename(newid: Identifier): ConstantConnectorLabel[N] = throw new Error("Can't rename a constant connector label")
def freshRename(taken: Iterable[Identifier]): ConstantConnectorLabel[N] = rename(K.freshId(taken, id))
override def toString(): String = id
def mkString(args: Seq[Formula]): String = if (args.length == 2) (args(0).toString() + " " + toString() + " " + args(1).toString()) else toString() + "(" + args.mkString(", ") + ")"
def mkString(args: Seq[Formula]): String = if (args.length == 1) {
underlyingLabel match
case K.Neg => toString() + "(" + args(0).toString() + ")"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? I think if label is K.Neg then it prints the same result, but we lose the infix notation if args.length == 2 ? Also the representation is not injective because and(A) will be printed as A

Copy link
Collaborator Author

@augustepoiroux augustepoiroux Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did these changes to get printed results as close as possible to actual Lisa code.

  • When we only have one argument, and(A) is not accepted by the parser, so that's why I'm printing A instead. And Neg is a special case to handle here.
  • I think infix notation was not always accepted by Lisa parser, that's why for args.length > 1 I'm printing everything using (A and B and C)-like notation

If this is not the appropriate place to do this, or if the main purpose of this string representation is not to be as close as possible to Lisa code, tell me where I should put this :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which parser do you mean? Scala compiler or the FOL parser? If the last one, we're not using it and it anywhere and it has multiple other differences. If the first one, if it doesn't accept and(A) it can be made to accept e.g. and.multi(A).
But printing A instead of and(A) is not correct

case _ => args(0).toString()
} else "(" + args.mkString(" " + toString() + " ") + ")"
override def mkStringSeparated(args: Seq[Formula]): String = if (args.length == 2) "(" + mkString(args) + ")" else mkString(args)

}
Expand Down
6 changes: 5 additions & 1 deletion lisa-utils/src/main/scala/lisa/fol/FOLHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ object FOLHelpers {
case cl: K.SchematicConnectorLabel => asFrontLabel(cl)
def asFrontLabel[N <: Arity](cpl: K.ConstantAtomicLabel): ConstantAtomicLabelOfArity[N] = cpl.arity.asInstanceOf[N] match
case n: 0 => ConstantFormula(cpl.id)
case n: N => ConstantPredicateLabel(cpl.id, cpl.arity.asInstanceOf)
case n: N =>
if (cpl.id == Identifier("="))
ConstantPredicateLabel.infix("===", cpl.arity.asInstanceOf)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem correct, because asFrontLabel(K.equals).underlying != K.equals, which is a problem.
Probably you want to change just the pretty printing of symbols whose identifier is "=".
For example, override the toString method of the equality symbol in lisa.utils

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, I did this here because I didn't find somewhere else to do it. I will try to do that in lisa.utils then 👍

else
ConstantPredicateLabel(cpl.id, cpl.arity.asInstanceOf)
def asFrontLabel(sfl: K.SchematicFormulaLabel): SchematicAtomicLabel[?] | SchematicConnectorLabel[?] =
sfl match
case v: K.VariableFormulaLabel => asFrontLabel(v)
Expand Down
5 changes: 2 additions & 3 deletions lisa-utils/src/main/scala/lisa/fol/Sequents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,8 @@ trait Sequents extends Common with lisa.fol.Lambdas with Predef {
infix def ++?(s1: Sequent): Sequent = this addAllIfNotExists s1

override def toString =
(if left.size == 0 then "" else if left.size == 1 then left.head.toString else "( " + left.mkString(", ") + " )") +
" ⊢ " +
(if right.size == 0 then "" else if right.size == 1 then right.head.toString else "( " + right.mkString(", ") + " )")
(if (left.size == 0 && right.size <= 1) then "" else if left.size == 1 then left.head.toString + " ⊢ " else "(" + left.mkString(", ") + ") ⊢ ") +
(if right.size == 1 then right.head.toString else "(" + right.mkString(", ") + ")")

}

Expand Down
Loading
Loading