All Downloads are FREE. Search and download functionalities are using the official Maven repository.

axle.logic.FOPL.scala Maven / Gradle / Ivy

The newest version!

package axle.logic

import spire.algebra.Eq
import spire.implicits.BooleanStructure
import spire.implicits.IntAlgebra
import spire.implicits.SeqEq
import spire.implicits.StringOrder
import spire.implicits.eqOps

object FOPL {

  def skolemFor(skolems: Map[Symbol, Set[Symbol]], s: Symbol, universally: Set[Symbol]) = {
    val newSym = Symbol("sk" + skolems.size)
    (newSym, skolems + (newSym -> universally))
  }

  implicit def symbolEq: Eq[Symbol] = new Eq[Symbol] {
    def eqv(x: Symbol, y: Symbol): Boolean = x.equals(y)
  }

  object Predicate {
    implicit def predicateEq = new Eq[Predicate] {
      def eqv(x: Predicate, y: Predicate): Boolean =
        (x.name === y.name) && (x.symbols === y.symbols)
    }
  }

  abstract class Predicate(_symbols: Symbol*) extends Function1[Map[Symbol, Any], Boolean] with Statement {

    outer =>

    def symbols: List[Symbol] = _symbols.toList
    def symbolSet: Set[Symbol] = _symbols.toSet
    def name: String

    override def toString: String = name + "(" + symbols.mkString(", ") + ")"

    def skolemize(universally: Set[Symbol], existentially: Set[Symbol], skolems: Map[Symbol, Set[Symbol]]): (Predicate, Map[Symbol, Set[Symbol]]) = {

      val symbolsSkolems = symbols.scanLeft((null.asInstanceOf[Symbol], skolems))({
        case (previous, s) =>
          if (existentially.contains(s)) skolemFor(previous._2, s, universally)
          else (s, previous._2)
      })

      val newPredicate = new Predicate(symbolsSkolems.tail.map(_._1): _*) {
        def name: String = outer.name
        def apply(symbolTable: Map[Symbol, Any]): Boolean = outer.apply(symbolTable)
      }

      (newPredicate, symbolsSkolems.last._2)
    }
  }

  object Statement {

    implicit def statementEq: Eq[Statement] = new Eq[Statement] {
      // TODO: How can I avoid this pattern match ?
      def eqv(x: Statement, y: Statement): Boolean = (x, y) match {
        case (l: Predicate, r: Predicate) => l === r
        case (l @ And(_, _), r @ And(_, _)) => l === r
        case (l @ Or(_, _), r @ Or(_, _)) => l === r
        case (l @ Iff(_, _), r @ Iff(_, _)) => l === r
        case (l @ Implies(_, _), r @ Implies(_, _)) => l === r
        case (l @ ¬(_), r @ ¬(_)) => l === r
        case (l @ ∃(_, _), r @ ∃(_, _)) => l === r
        case (l @ ∀(_, _), r @ ∀(_, _)) => l === r
        case (l @ Constant(_), r @ Constant(_)) => l === r
        case _ => false
      }
    }

  }

  trait Statement {

    def apply(symbolTable: Map[Symbol, Any]): Boolean

    def ∧(right: Statement) = And(this, right)
    def and(right: Statement) = And(this, right)

    def ∨(right: Statement) = Or(this, right)
    def or(right: Statement) = Or(this, right)

    def ⇔(right: Statement) = Iff(this, right)
    def iff(right: Statement) = Iff(this, right)

    def ⊃(right: Statement) = Implies(this, right)
    def implies(right: Statement) = Implies(this, right)
  }

  object And {
    implicit def eqAnd: Eq[And] = new Eq[And] {
      def eqv(x: And, y: And): Boolean = (x.left === y.left && x.right === y.right)
    }
  }
  case class And(left: Statement, right: Statement) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean = left(symbolTable) && right(symbolTable)
    override def toString: String = s"($left ∧ $right)"
  }

  object Or {
    implicit def eqOr: Eq[Or] = new Eq[Or] {
      def eqv(x: Or, y: Or): Boolean = (x.left === y.left && x.right === y.right)
    }
  }
  case class Or(left: Statement, right: Statement) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean = left(symbolTable) || right(symbolTable)
    override def toString: String = s"($left ∨ $right)"
  }

  object Iff {
    implicit def eqIff: Eq[Iff] = new Eq[Iff] {
      def eqv(x: Iff, y: Iff): Boolean = (x.left === y.left && x.right === y.right)
    }
  }
  case class Iff(left: Statement, right: Statement) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean = {
      val lv = left(symbolTable)
      val rv = right(symbolTable)
      (!lv && !rv) || (lv && rv)
    }
    override def toString: String = s"($left ⇔ $right)"
  }

  object Implies {
    implicit def eqImplies: Eq[Implies] = new Eq[Implies] {
      def eqv(x: Implies, y: Implies): Boolean = (x.left === y.left && x.right === y.right)
    }
  }
  case class Implies(left: Statement, right: Statement) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean = (!left(symbolTable)) || right(symbolTable)
    override def toString: String = s"($left ⊃ $right)"
  }

  object ¬ {
    implicit def eqNeg: Eq[¬] = new Eq[¬] {
      def eqv(x: ¬, y: ¬): Boolean = (x.statement === y.statement)
    }
  }
  case class ¬(statement: Statement) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean = !statement(symbolTable)
  }

  object ElementOf {
    implicit def eqEO: Eq[ElementOf] = new Eq[ElementOf] {
      def eqv(x: ElementOf, y: ElementOf): Boolean =
        (x.symbol === y.symbol) && (x.set.intersect(y.set).size === x.set.size)
    }
  }
  case class ElementOf(symbol: Symbol, set: Set[Any]) {
    override def toString: String = s"$symbol ∈ $set"
  }

  class EnrichedSymbol(symbol: Symbol) {
    def in(set: Set[Any]) = ElementOf(symbol, set)
    def ∈(set: Set[Any]) = ElementOf(symbol, set)
  }

  implicit val enrichSymbol = (symbol: Symbol) => new EnrichedSymbol(symbol)

  object ∃ {
    implicit def eqExists: Eq[∃] = new Eq[∃] {
      def eqv(x: ∃, y: ∃): Boolean =
        (x.symbolSet === y.symbolSet) && (x.statement === y.statement)
    }
  }
  case class ∃(symbolSet: ElementOf, statement: Statement) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean =
      symbolSet.set.exists(v => statement(symbolTable + (symbolSet.symbol -> v)))
  }

  object ∀ {
    implicit def eqAll: Eq[∀] = new Eq[∀] {
      def eqv(x: ∀, y: ∀): Boolean =
        (x.symbolSet === y.symbolSet) && (x.statement === y.statement)
    }
  }
  case class ∀(symbolSet: ElementOf, statement: Statement) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean =
      symbolSet.set.forall(v => statement(symbolTable + (symbolSet.symbol -> v)))
  }

  def not(statement: Statement) = ¬(statement)
  def exists(symbolSet: ElementOf, statement: Statement) = ∃(symbolSet, statement)
  def forall(symbolSet: ElementOf, statement: Statement) = ∀(symbolSet, statement)

  object Constant {
    implicit def eqConstant: Eq[Constant] = new Eq[Constant] {
      def eqv(x: Constant, y: Constant): Boolean =
        x.b === y.b
    }
  }
  case class Constant(b: Boolean) extends Statement {
    def apply(symbolTable: Map[Symbol, Any]): Boolean = b
    override def toString: String = b.toString
  }

  implicit def foplBoolean(b: Boolean) = Constant(b)

  def noOp(s: Statement): Statement = s match {
    case And(left, right) => And(noOp(left), noOp(right))
    case Or(left, right) => Or(noOp(left), noOp(right))
    case Iff(left, right) => Iff(noOp(left), noOp(right))
    case Implies(left, right) => Implies(noOp(left), noOp(right))
    case ¬(inner) => ¬(noOp(inner))
    case ∃(sym, e) => ∃(sym, noOp(e))
    case ∀(sym, e) => ∀(sym, noOp(e))
    case _ => s
  }

  def freeVariables(s: Statement, notFree: Set[Symbol] = Set()): Set[Symbol] = s match {
    case And(left, right) => freeVariables(left, notFree).union(freeVariables(right, notFree))
    case Or(left, right) => freeVariables(left, notFree).union(freeVariables(right, notFree))
    case Iff(left, right) => freeVariables(left, notFree).union(freeVariables(right, notFree))
    case Implies(left, right) => freeVariables(left, notFree).union(freeVariables(right, notFree))
    case ¬(inner) => freeVariables(inner, notFree)
    case ∃(symbolSet, e) => freeVariables(e, notFree + symbolSet.symbol)
    case ∀(symbolSet, e) => freeVariables(e, notFree + symbolSet.symbol)
    case pred: Predicate => pred.symbolSet -- notFree
  }

  def eliminateIff(s: Statement): Statement = s match {
    case And(left, right) => And(eliminateIff(left), eliminateIff(right))
    case Or(left, right) => Or(eliminateIff(left), eliminateIff(right))
    case Iff(left, right) => {
      val leftResult = eliminateIff(left)
      val rightResult = eliminateIff(right)
      (leftResult ⊃ rightResult) ∧ (rightResult ⊃ leftResult)
    }
    case Implies(left, right) => Implies(eliminateIff(left), eliminateIff(right))
    case ¬(inner) => ¬(eliminateIff(inner))
    case ∃(sym, e) => ∃(sym, eliminateIff(e))
    case ∀(sym, e) => ∀(sym, eliminateIff(e))
    case _ => s
  }

  def eliminateImplication(s: Statement): Statement = s match {
    case And(left, right) => And(eliminateImplication(left), eliminateImplication(right))
    case Or(left, right) => Or(eliminateImplication(left), eliminateImplication(right))
    case Iff(left, right) => ??? //Iff(eliminateImplication(left), eliminateImplication(right))
    case Implies(left, right) => ¬(eliminateImplication(left)) ∨ eliminateImplication(right)
    case ¬(inner) => ¬(eliminateImplication(inner))
    case ∃(sym, e) => ∃(sym, eliminateImplication(e))
    case ∀(sym, e) => ∀(sym, eliminateImplication(e))
    case _ => s
  }

  def moveNegation(s: Statement, incoming: Boolean = false): Statement = s match {

    case And(left, right) =>
      if (incoming)
        Or(moveNegation(left, true), moveNegation(right, true))
      else
        And(moveNegation(left), moveNegation(right))

    case Or(left, right) =>
      if (incoming)
        And(moveNegation(left, true), moveNegation(right, true))
      else
        Or(moveNegation(left), moveNegation(right))

    case Iff(left, right) => ??? // Iff(moveNegation(left), moveNegation(right))

    case Implies(left, right) => ??? //Implies(moveNegation(left), moveNegation(right))

    case ¬(inner) => if (incoming) moveNegation(inner) else moveNegation(inner, true)

    case ∃(symbolSet, e) =>
      if (incoming)
        ∀(symbolSet, moveNegation(e, true))
      else
        ∃(symbolSet, moveNegation(e))

    case ∀(symbolSet, e) =>
      if (incoming)
        ∃(symbolSet, moveNegation(e, true))
      else
        ∀(symbolSet, moveNegation(e))

    case _ => if (incoming) ¬(s) else s
  }

  // TODO: the skolem constants should actually be functions of the universally quantified vars
  // TODO: create a monadic context for skolem count

  def _skolemize(s: Statement, universally: Set[Symbol], existentially: Set[Symbol], skolems: Map[Symbol, Set[Symbol]]): (Statement, Map[Symbol, Set[Symbol]]) = s match {
    case And(left, right) => {
      val (leftSkolemized, leftSkolems) = _skolemize(left, universally, existentially, skolems)
      val (rightSkolemized, rightSkolems) = _skolemize(right, universally, existentially, leftSkolems)
      (And(leftSkolemized, rightSkolemized), rightSkolems)
    }
    case Or(left, right) => {
      val (leftSkolemized, leftSkolems) = _skolemize(left, universally, existentially, skolems)
      val (rightSkolemized, rightSkolems) = _skolemize(right, universally, existentially, leftSkolems)
      (Or(leftSkolemized, rightSkolemized), rightSkolems)
    }
    case ∃(symbolSet, e) => _skolemize(e, universally, existentially + symbolSet.symbol, skolems)
    case ∀(symbolSet, e) => _skolemize(e, universally + symbolSet.symbol, existentially, skolems)
    case ¬(p: Predicate) => {
      val (innerSkolemized, innerSkolems) = p.skolemize(universally, existentially, skolems)
      (¬(innerSkolemized), innerSkolems)
    }
    case p: Predicate => p.skolemize(universally, existentially, skolems)
    case _ => ???
  }

  def skolemize(s: Statement): (Statement, Map[Symbol, Set[Symbol]]) = _skolemize(s, Set(), Set(), Map())

  def distribute(s: Statement) = _distribute(s)._1

  def _distribute(s: Statement): (Statement, Boolean) = s match {
    case And(l, r) => {
      val (ld, lc) = _distribute(l)
      val (rd, rc) = _distribute(r)
      (And(ld, rd), lc || rc)
    }
    case Or(l, And(rl, rr)) => {
      val (ld, lc) = _distribute(l)
      val (rld, rlc) = _distribute(rl)
      val (rrd, rrc) = _distribute(rr)
      (And(Or(ld, rld), Or(ld, rrd)), true)
    }
    case Or(And(ll, lr), r) => {
      val (lld, llc) = _distribute(ll)
      val (rd, rc) = _distribute(r)
      val (lrd, lrc) = _distribute(lr)
      (And(Or(lld, rd), Or(lrd, rd)), true)
    }
    case Or(l, r) => {
      val (ld, lc) = _distribute(l)
      val (rd, rc) = _distribute(r)
      if (lc || rc) _distribute(Or(ld, rd)) else (Or(ld, rd), false)
    }
    case Iff(left, right) => ??? // Iff(distribute(left), distribute(right))
    case Implies(left, right) => ??? // Implies(distribute(left), distribute(right))
    case ¬(inner) => {
      val (id, ic) = _distribute(inner)
      (¬(id), ic)
    }
    case ∃(sym, e) => {
      val (ed, ec) = _distribute(e)
      (∃(sym, ed), ec)
    }
    case ∀(sym, e) => {
      val (ed, ec) = _distribute(e)
      (∀(sym, ed), ec)
    }
    case _ => (s, false)
  }

  def _flatten(s: Statement): (Statement, Boolean) = s match {

    case And(And(ll, lr), r) =>
      (And(_flatten(ll)._1, _flatten(And(_flatten(lr)._1, _flatten(r)._1))._1), true)

    case And(left, right) => {
      val (lf, lc) = _flatten(left)
      val (rf, rc) = _flatten(right)
      (And(lf, rf), lc || rc)
    }

    case Or(Or(ll, lr), r) =>
      (Or(_flatten(ll)._1, _flatten(Or(_flatten(lr)._1, _flatten(r)._1))._1), true)

    case Or(left, right) => {
      val (lf, lc) = _flatten(left)
      val (rf, rc) = _flatten(right)
      (Or(lf, rf), lc || rc)
    }

    case Iff(left, right) => ???
    case Implies(left, right) => ???

    case ¬(inner) => {
      // Note: only makes sense when inner is an atom
      val (innerFlat, innerChanged) = _flatten(inner)
      (¬(innerFlat), innerChanged)
    }

    case ∃(sym, e) => ???
    case ∀(sym, e) => ???
    case _ => (s, false)
  }

  def flatten(s: Statement): Statement = _flatten(s)._1

  def conjunctiveNormalForm(s: Statement): (Statement, Map[Symbol, Set[Symbol]]) = {
    val (skolemized, skolems) = skolemize(moveNegation(eliminateImplication(eliminateIff(s))))
    (flatten(distribute(skolemized)), skolems)
  }

  def disjunctList(s: Statement): List[Statement] = s match {
    case Or(head, tail) => head :: disjunctList(tail)
    case _ => List(s)
  }

  def disjoin(cs: List[Statement]): Statement = cs.reduceOption(Or(_, _)).getOrElse(false)

  def conjunctList(s: Statement): List[Statement] = s match {
    case And(head, tail) => head :: conjunctList(tail)
    case _ => List(s)
  }

  def conjoin(cs: List[Statement]): Statement = cs.reduceOption(And(_, _)).getOrElse(true)

  def atomicDisjunctsToImplication(s: Statement): Statement =
    disjunctList(s)
      .partition(a => a match { case ¬(_) => true case _ => false }) match {
        case (negatives, positives) =>
          conjoin(negatives.map({ case ¬(x) => x })) ⊃ disjoin(positives)
      }

  def implicativeNormalForm(s: Statement): List[Statement] =
    conjunctList(s).map(atomicDisjunctsToImplication(_))

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy