io.github.pirocks.logic.Logic.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of simple-logic-lib Show documentation
Show all versions of simple-logic-lib Show documentation
A simple library for working with first order logic and natural deduction proofs
The newest version!
package io.github.pirocks.logic
import io.github.pirocks.equivalences.MatchSubstitutions
import io.github.pirocks.util.UUIDUtil
import java.io.Serializable
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
/**
* todo to mathml needs testing
* todo, maybe remove name index. seems unnecessarily complex
*/
val nameIndex: MutableMap = mutableMapOf()
/**
* represents anything with an ast
*/
interface Formula: Serializable{
abstract val subFormulas: Array
}
interface FOLPattern: Formula {
fun matches(formula: FOLFormula, matchSubstitutions: MatchSubstitutions): Boolean{
if(formula.javaClass != javaClass){
return false
}
if(formula.subFormulas.isEmpty()){
return true
}
return (0 until formula.subFormulas.size).all {
subFormulas[it].matches(formula.subFormulas[it],matchSubstitutions)
}
}
}
data class SignatureElement(val uuid: UUID)
data class VariableValue(val variableName: VariableName, val value: SignatureElement)
class VariableName(val uuid: UUID = UUIDUtil.generateUUID(), name: String = "V" + varCount.getAndIncrement().toString()) : Serializable {
companion object {
@JvmStatic
private var varCount = AtomicInteger(0);
}
val name: String
get() = nameIndex[uuid]!!
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is VariableName) return false
if (uuid != other.uuid) return false
return true
}
override fun hashCode(): Int {
return uuid.hashCode()
}
init {
nameIndex[uuid] = name
}
}
data class Signature(val elements: Set, val predicates: Set)
class Predicate(val implmentation: (Array) -> Boolean, val uuid: UUID = UUIDUtil.generateUUID(), name: String = "P" + getAndIncrementPredicateCount().toString()) {
companion object {
@JvmStatic
private var predicateCount = 0;
fun getAndIncrementPredicateCount(): Int {
predicateCount++;
return predicateCount - 1;
}
fun newUnEvaluatableRelation(): Predicate {
return Predicate({ throw Exception("This predicate cannot be evaluated.") })
}
}
val name: String
get() = nameIndex[uuid]!!
init {
nameIndex[uuid] = name
}
}
class EqualityContext(
//from: other var
//to: our vars
val uuidVariableMappings: Map = mapOf())
class HashContext(
val variableNumberMappings: Map = mutableMapOf()
)
class EvalContext(val signature: Signature, val variables: MutableMap)
sealed class FOLFormula : Formula, FOLPattern {
internal open fun sameAs(other: FOLFormula): Boolean {
return sameAsImpl(other, EqualityContext())
}
internal open fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean {
//by default if subformulas are equivalent then these are equivalent.
if (javaClass != other.javaClass || subFormulas.size != other.subFormulas.size) {
return false
}
return subFormulas.zip(other.subFormulas).all { it.first.sameAsImpl(it.second, equalityContext) }
}
abstract fun evaluate(ev: EvalContext): Boolean
abstract fun toMathML2(): String
fun toHtml(): String = ("").replace("\\s(?!separators)".toRegex(), "").trim().trimIndent()
abstract fun toPrefixNotation(): String
override fun hashCode(): Int = hashCodeImpl()
abstract fun hashCodeImpl(hashContext: HashContext = HashContext()) : Int
override fun equals(other: Any?): Boolean {
if(other == null ) return false
if(other.javaClass != this.javaClass) return false
return this.sameAs(other as FOLFormula)
}
}
sealed class BinaryRelation(open val left: FOLFormula, open val right: FOLFormula) : FOLFormula() {
override val subFormulas: Array
get() = arrayOf(left, right)
abstract fun getOperatorAsMathML(): String
override fun toMathML2(): String = """
${left.toMathML2()}
${getOperatorAsMathML()}
${right.toMathML2()}
"""
abstract val operatorHashCode: Int;
override fun hashCodeImpl(hashContext: HashContext): Int {
var hash = operatorHashCode
hash = 31*hash + left.hashCodeImpl(hashContext)
hash = 31*hash + right.hashCodeImpl(hashContext)
return hash
}
}
sealed class Quantifier(open val child: FOLFormula, open val varName: VariableName = VariableName()) : FOLFormula() {
abstract val operatorHashCode: Int;
override fun hashCodeImpl(hashContext: HashContext): Int {
var hash = operatorHashCode
hash = 31*hash + varName.hashCode()
hash = 31*hash + child.hashCodeImpl(hashContext)
return hash
}
override val subFormulas: Array
get() = arrayOf(child)
abstract val quantifierSymbol: String
override fun toMathML2(): String = """
${quantifierSymbol}
${varName.name}
${child.toMathML2()}
"""
override fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean {
if (other.javaClass != javaClass || other !is Quantifier) {
return false
}
val newEqualityContext = EqualityContext(equalityContext.uuidVariableMappings + mutableMapOf(Pair(other.varName, varName)))
return child.sameAsImpl(other.child, newEqualityContext);
}
override fun matches(formula: FOLFormula, matchSubstitutions: MatchSubstitutions): Boolean {
if(formula.javaClass != javaClass){
return false
}
val varSubstitutions = matchSubstitutions.variableSubstitutions
varSubstitutions[varName] = (formula as Quantifier).varName;
try {
return child.matches(formula.child,matchSubstitutions)
} finally {
varSubstitutions.remove(formula.varName)
}
}
}
class True : FOLFormula() {
override fun hashCodeImpl(hashContext: HashContext): Int = 31// can be any arbitrary, reasonably sized prime
override fun toPrefixNotation(): String = "T"
override fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean = other is True;
override val subFormulas: Array
get() = arrayOf()
override fun evaluate(ev: EvalContext): Boolean = true
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return true
}
override fun hashCode(): Int {
return javaClass.hashCode()
}
override fun toMathML2(): String = "T "
}
class False : FOLFormula() {
override fun hashCodeImpl(hashContext: HashContext): Int = 43
override fun toPrefixNotation(): String = "F"
override fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean = other is False;
override val subFormulas: Array
get() = arrayOf()
override fun evaluate(ev: EvalContext): Boolean = false
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return true
}
override fun hashCode(): Int {
return javaClass.hashCode()
}
override fun toMathML2(): String = "⊥ "
}
open class PredicateAtom(val predicate: Predicate, val expectedArgs: Array) : FOLFormula() {
companion object {
fun newSimpleAtom(): PredicateAtom {
return PredicateAtom(Predicate({ throw Exception("Simple Atoms can't be evaluated") }), emptyArray())
}
}
override fun hashCodeImpl(hashContext: HashContext): Int {
var hash = predicate.uuid.hashCode()
expectedArgs.forEach {
hash = 31*hash + hashContext.variableNumberMappings[it]!!
}
return hash
}
override fun toPrefixNotation(): String = """(${predicateString()} ${expectedArgs.map { it.name }.foldRight("",{s, acc -> s + " " + acc })}"""
override fun sameAs(other: FOLFormula): Boolean {
//this should only be called when comparing to atoms. Anything wrapped in quantifiers should not call this:
assert(expectedArgs.isEmpty())
if (other !is PredicateAtom) {
return false;
} else {
assert(other.expectedArgs.isEmpty())
return predicate.uuid == other.predicate.uuid;
}
}
override fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean {
if (other !is PredicateAtom) {
return false;
}
if (other.expectedArgs.size != expectedArgs.size) {
return false
}
if (other.predicate.uuid != predicate.uuid) {
return false
}
fun translateExpectedArgs(toTranslate: Array): Array = toTranslate.map { equalityContext.uuidVariableMappings[it]!! }.toTypedArray()// okay to assert not null, because there are no free vars. So there shouldn't be unknown vars
return expectedArgs.contentDeepEquals(translateExpectedArgs(other.expectedArgs))
}
override val subFormulas: Array
get() = arrayOf()
override fun evaluate(ev: EvalContext): Boolean {
val args: Array = arrayOfNulls(expectedArgs.size)
for ((i, expectedArg) in expectedArgs.withIndex()) {
args[i] = ev.variables[expectedArg]
}
val notNullArgs: Array = Array(args.size, init = {
args[it]!!
})
return predicate.implmentation.invoke(notNullArgs)
}
private fun predicateString(): String = "predicateNumber" + predicate.hashCode().toString(16)
override fun toMathML2(): String = """${predicateString()} ${expectedArgs.map { "" + it.name + " " }.reduceRight { s: String, acc: String -> s + acc }} """
}
class And(override val left: FOLFormula, override val right: FOLFormula) : BinaryRelation(left, right) {
override val operatorHashCode: Int
get() = 31
override fun toPrefixNotation(): String = """(and ${left.toPrefixNotation()} ${right.toPrefixNotation()})"""
override fun getOperatorAsMathML(): String = "∧";
override fun toMathML2(): String = """
${left.toMathML2()}
${getOperatorAsMathML()}
${right.toMathML2()}
"""
override fun evaluate(ev: EvalContext): Boolean = left.evaluate(ev) && right.evaluate(ev)
}
class Or(override val left: FOLFormula, override val right: FOLFormula) : BinaryRelation(left, right) {
override val operatorHashCode: Int
get() = 41
override fun toPrefixNotation(): String = """(or ${left.toPrefixNotation()} ${right.toPrefixNotation()})"""
override fun getOperatorAsMathML(): String = "∨"
override fun evaluate(ev: EvalContext): Boolean = left.evaluate(ev) || right.evaluate(ev)
}
class Negation(val child: FOLFormula) : FOLFormula() {
override fun hashCodeImpl(hashContext: HashContext): Int = 107*31 + child.hashCodeImpl(hashContext)
override fun toPrefixNotation(): String = """(neg ${child.toPrefixNotation()})"""
override val subFormulas: Array
get() = arrayOf(child)
override fun toMathML2(): String = """
¬
${child.toMathML2()}
"""
override fun evaluate(ev: EvalContext): Boolean = !child.evaluate(ev)
}
typealias Not = Negation
class Implies(val given: FOLFormula, val result: FOLFormula) : BinaryRelation(given, result) {
override val operatorHashCode: Int
get() = 43
override fun toPrefixNotation(): String = """(implies ${given.toPrefixNotation()} ${result.toPrefixNotation()})"""
override fun getOperatorAsMathML(): String = "⇒"
override fun evaluate(ev: EvalContext): Boolean = !given.evaluate(ev) || result.evaluate(ev)
}
class IFF(val one: FOLFormula, val two: FOLFormula) : BinaryRelation(one, two) {
override val operatorHashCode: Int
get() = 101
override fun toPrefixNotation(): String = """(iff ${one.toPrefixNotation()} ${two.toPrefixNotation()})"""
override fun getOperatorAsMathML(): String = "⇔"
override fun evaluate(ev: EvalContext): Boolean = one.evaluate(ev) == two.evaluate(ev)
}
class ForAll(override val child: FOLFormula, override val varName: VariableName = VariableName()) : Quantifier(child, varName) {
override val operatorHashCode: Int
get() = 71*107
override fun toPrefixNotation(): String = """(forall ${varName.name} ${child.toPrefixNotation()})"""
override val quantifierSymbol: String
get() = "∀"
override fun evaluate(ev: EvalContext): Boolean = ev.signature.elements.all {
val `var` = VariableValue(varName, it)
ev.variables.put(varName, `var`)
val res = child.evaluate(ev)
ev.variables.remove(varName)
return res
}
}
class Exists(override val child: FOLFormula, override val varName: VariableName = VariableName()) : Quantifier(child, varName) {
override val operatorHashCode: Int
get() = 73*103
override fun toPrefixNotation(): String = """(exists ${varName.name} ${child.toPrefixNotation()})"""
override val quantifierSymbol: String
get() = "∃"
override fun evaluate(ev: EvalContext): Boolean = ev.signature.elements.any {
val `var` = VariableValue(varName, it)
ev.variables.put(varName, `var`)
val res = child.evaluate(ev)
ev.variables.remove(varName)
return res
}
}
//todo technically this could all done with a predicate atom
//todo maybe make atom class to abstract the redundancy here:
class EvaluatedAPatternException() : Exception("You tried to evaluate a pattern. Patterns cannot be evaluated by definition.")
sealed class PatternMember : FOLFormula(){
val uuid = UUIDUtil.generateUUID()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PatternMember) return false
if (uuid != other.uuid) return false
return true
}
override fun hashCode(): Int {
return uuid.hashCode()
}
fun defaultMatchSubstitution(formula: FOLFormula, matchSubstitutions: MatchSubstitutions): Boolean {
val actualFormula = formula
//todo duplication with AllowOnlyCertainVars and ForbidCertainVars
if (this in matchSubstitutions.matchedPatterns) {
//we already found this pattern elsewhere
//need to check if same as elsewhere
val expectedFormula = matchSubstitutions.matchedPatterns[this]!!
//todo check that the order of parameters does not need reversing
//todo this could still encounter vars from higher up the rewriting visitor
return expectedFormula.sameAsImpl(actualFormula, EqualityContext(matchSubstitutions.variableSubstitutions))
} else {
matchSubstitutions.matchedPatterns[this] = formula;
return true
}
}
}
class AllowAllVars : PatternMember(){
override fun hashCodeImpl(hashContext: HashContext): Int = 103*101
override fun toPrefixNotation(): String = """(Pattern matches anything Pattern#${super.hashCode()})"""
override fun toMathML2(): String = """PatternMatchesAnything_PatternNumber_${(super.hashCode()).toString(36)} """
override fun matches(formula: FOLFormula, matchSubstitutions: MatchSubstitutions): Boolean {
return defaultMatchSubstitution(formula, matchSubstitutions)
}
override fun sameAs(other: FOLFormula): Boolean {
TODO("Need to map patterns check sameness")
}
override fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean {
TODO("Need to translate variables to check sameness")
}
override fun evaluate(ev: EvalContext): Boolean {
throw EvaluatedAPatternException()
}
override val subFormulas: Array
get() = arrayOf()
}
class AllowOnlyCertainVars(val vars: Array) : PatternMember() {
/**
* todo duplication with predicate atom
*/
override fun hashCodeImpl(hashContext: HashContext): Int {
var hash = super.hashCode()
vars.forEach {
hash = 31*hash + hashContext.variableNumberMappings[it]!!
}
return hash
}
override fun toPrefixNotation(): String = """(Pattern Allows Vars: ${vars.map { it.name }.foldRight("",{s, acc -> s + " " + acc })}"""
override fun toMathML2(): String = """PatternAllowsVars_PatternNumber_${(super.hashCode()).toString(36)} ${vars.map { "" + it.name + " " }.reduceRight { s: String, acc: String -> s + acc }} """
override fun matches(formula: FOLFormula, matchSubstitutions: MatchSubstitutions): Boolean {
if(containsVarsOtherThan(formula, vars)){
return false
}
return defaultMatchSubstitution(formula, matchSubstitutions)
}
override fun sameAs(other: FOLFormula): Boolean {
TODO("Need to translate variables to check sameness")
}
override fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean {
TODO("Need to translate variables to check sameness")
}
override fun evaluate(ev: EvalContext): Boolean {
throw EvaluatedAPatternException()
}
override val subFormulas: Array
get() = arrayOf()
}
class ForbidCertainVars(val vars: Array) : PatternMember() {
override fun hashCodeImpl(hashContext: HashContext): Int {
var hash = super.hashCode()
vars.forEach {
hash = 31*hash + hashContext.variableNumberMappings[it]!!
}
return hash
}
override fun toPrefixNotation(): String = """(Pattern Excludes Vars: ${vars.map { it.name }.foldRight("",{s, acc -> s + " " + acc })})"""
override fun toMathML2(): String = """PatternExcludesVars_PatternNumber_${(super.hashCode()).toString(36)} ${vars.map { "" + it.name + " " }.reduceRight { s: String, acc: String -> s + acc }} """
override fun matches(formula: FOLFormula, matchSubstitutions: MatchSubstitutions): Boolean {
if(vars.any{ containsVar(formula, it) }){
return false
}
return defaultMatchSubstitution(formula, matchSubstitutions)
}
override fun sameAs(other: FOLFormula): Boolean {
TODO("Need to translate variables to check sameness")
}
override fun sameAsImpl(other: FOLFormula, equalityContext: EqualityContext): Boolean {
TODO("Need to translate variables to check sameness")
}
override fun evaluate(ev: EvalContext): Boolean {
throw EvaluatedAPatternException()
}
override val subFormulas: Array
get() = arrayOf()
}