Commit 6fc323ff authored by Arthur Bit-Monnot's avatar Arthur Bit-Monnot

Introduce anonymous Ids in planning model.

parent ed706b98
name := "dahu"
scalafixSettings
//scalafixSettings
lazy val commonSettings = Seq(
organization := "com.github.arthur-bit-monnot",
scalaVersion := "2.12.4",
scalaVersion := "2.12.6",
crossPaths := true,
// To sync with Maven central
publishMavenStyle := true,
......@@ -37,9 +37,9 @@ lazy val commonSettings = Seq(
"-language:higherKinds",
"-language:existentials",
// experimental option to speed up the build require 2.12.5
// "-Ycache-plugin-class-loader:last-modified",
// "-Ycache-macro-class-loader:last-modified",
// "-Ybackend-parallelism", "3"
"-Ycache-plugin-class-loader:last-modified",
"-Ycache-macro-class-loader:last-modified",
"-Ybackend-parallelism", "3"
// "-opt:simplify-jumps",
// "-opt:compact-locals",
// "-opt:copy-propagation",
......
......@@ -371,7 +371,7 @@ abstract class AnmlParser(val initialContext: Ctx)(implicit predef: Predef) {
val timedAssertion: Parser[TimedAssertion] = {
// variable that hold the first two parsed token to facilitate type checking logic
var id: String = null
var id: Id = null
var fluent: TimedExpr = null
def compatibleTypes(t1: Type, t2: Type): Boolean = t1.isSubtypeOf(t2) || t2.isSubtypeOf(t1)
......@@ -381,10 +381,10 @@ abstract class AnmlParser(val initialContext: Ctx)(implicit predef: Predef) {
staticExpr.namedFilter(expr => compatibleTypes(fluent.typ, expr.typ), "has-compatible-type")
/** Reads an identifier or construct a default one otherwise */
val assertionId: Parser[String] =
val assertionId: Parser[Id] =
(freeIdent ~ ":" ~/ Pass).?.map {
case Some(id) => id
case None => defaultId()
case Some(id) => Id(ctx.scope, id)
case None => ctx.scope.makeNewId()
}
assertionId.sideEffect(id = _).silent ~
......@@ -449,9 +449,9 @@ class AnmlModuleParser(val initialModel: Model) extends AnmlParser(initialModel)
(instanceKW ~/ declaredType ~/ distinctFreeIdents(Nil, ",", ";"))
.map {
case (typ, instanceNames) => instanceNames.map(name => new Instance(ctx.id(name), typ))
case (typ, instanceNames) => instanceNames.map(name => Instance(ctx.id(name), typ))
}
}.map(instances => instances.map(new InstanceDeclaration(_)))
}.map(instances => instances.map(InstanceDeclaration))
/** Parser that to read the kind and type of a function declaration. For instance:
* "fluent T", "constant T", "function T", "variable T", "predicate" where T is a type already declared.
......@@ -475,16 +475,16 @@ class AnmlModuleParser(val initialModel: Model) extends AnmlParser(initialModel)
(functionKindAndType ~ freeIdent ~ argList ~ ";")
.map {
case ("fluent", typ, svName, args) =>
new FluentTemplate(ctx.id(svName), typ, args.map {
case (name, argType) => new Arg(new Id(ctx.scope + svName, name), argType)
FluentTemplate(ctx.id(svName), typ, args.map {
case (name, argType) => Arg(Id(ctx.scope + svName, name), argType)
})
case ("constant", typ, svName, args) =>
new ConstantTemplate(ctx.id(svName), typ, args.map {
case (name, argType) => new Arg(new Id(ctx.scope + svName, name), argType)
ConstantTemplate(ctx.id(svName), typ, args.map {
case (name, argType) => Arg(Id(ctx.scope + svName, name), argType)
})
case _ => sys.error("Match failed")
}
.map(new FunctionDeclaration(_))
.map(FunctionDeclaration(_))
}
/** Extract the functions declared in a type. This consumes the whole type declaration.
......@@ -555,7 +555,7 @@ class AnmlActionParser(superParser: AnmlModuleParser)(implicit predef: Predef)
case m: Model => m
case _ => sys.error("Starting to parse an action while the context is not a model.")
}
val emptyAct = new ActionTemplate(actionName, container)
val emptyAct = ActionTemplate(ctx.scope / actionName, container)
emptyAct +
LocalVarDeclaration(Timepoint(Id(emptyAct.scope, "start"))) +
LocalVarDeclaration(Timepoint(Id(emptyAct.scope, "end")))
......
......@@ -44,7 +44,7 @@ class FullToCoreTest extends FunSuite {
def checkInvariantsInCore(m: CoreModel): Unit = {
val declarations = m.collect { case x: Declaration[_] => x }
assert(declarations.distinct.size == declarations.size,
"\nDuplicate declarations in: \n" + declarations.mkString("\n"))
Predef.assert(declarations.distinct.size == declarations.size,
"\nDuplicate declarations in: \n" + declarations.mkString("\n"))
}
}
......@@ -8,8 +8,32 @@ import spire.implicits._
package object common {
final case class Id(scope: Scope, name: String) {
sealed trait Id {
def scope: Scope
def name: String
def isAnonymous: Boolean
}
object Id {
def apply(scope: Scope, name: String): Named = Named(scope, name)
def apply(scope: Scope): Anonymous = new Anonymous(scope)
def unapply(id: Id): Option[(Scope, String)] = id match {
case Named(s, n) => Some((s, n))
case _ => None
}
}
final case class Named(scope: Scope, name: String) extends Id {
override def toString: String = scope.toScopedString(name)
override def isAnonymous: Boolean = false
}
final class Anonymous(val scope: Scope) extends Id {
val name: String = Anonymous.defaultId()
override def toString: String = scope.toScopedString(name)
override def isAnonymous: Boolean = true
}
object Anonymous {
private val reservedPrefix = "__"
private[this] var nextID = 0
private def defaultId(): String = reservedPrefix + { nextID += 1; nextID - 1 }
}
object Timepoint {
......@@ -21,7 +45,7 @@ package object common {
def +(nestedScope: String): InnerScope = InnerScope(this, nestedScope)
def /(name: String): Id = Id(this, name)
def makeNewId(): Id = Id(this, model.defaultId())
def makeNewId(): Anonymous = Id(this)
def toScopedString(name: String): String
}
object RootScope extends Scope {
......
package dahu.planning.model
import dahu.planning.model.common._
import dahu.planning.model.common.{Id, _}
package object core {
......@@ -151,7 +151,6 @@ package object core {
case LocalVarDeclaration(tp @ LocalVar(Id(`scope`, "end"), predef.Time)) => tp
}
.getOrElse(sys.error("No end timepoint in this action"))
}
/** Instance of an action template */
......
package dahu.planning.model
import cats.Functor
import dahu.planning.model.common.Type.BooleanType
import dahu.planning.model.common._
import dahu.planning.model.common.operators.{BinaryOperator, UnaryOperator}
......@@ -102,10 +103,12 @@ package object full {
def wrapped: Seq[Block]
}
abstract class TimedAssertion(parent: Option[Ctx], name: String)(implicit predef: Predef)
sealed abstract class TimedAssertion(parent: Option[Ctx], id: Id)(implicit predef: Predef)
extends Ctx
with Block {
override val scope: InnerScope = parent.map(_.scope).getOrElse(RootScope) + name
def fluent: TimedExpr
override val scope: InnerScope = parent.map(_.scope).getOrElse(RootScope) + id.name
val start: LocalVar = Timepoint(this.id("start"))
val end: LocalVar = Timepoint(this.id("end"))
......@@ -114,37 +117,37 @@ package object full {
LocalVarDeclaration(start) +
LocalVarDeclaration(end)
}
case class TimedEqualAssertion(left: TimedExpr, // TODO: should not restrict this to be symbolic
right: StaticExpr,
parent: Option[Ctx],
name: String)(implicit predef: Predef)
extends TimedAssertion(parent, name) {
if(name == "__296")
println(name)
override def toString: String =
if(name.startsWith(reservedPrefix)) s"$left == $right"
else s"$name: $left == $right"
case class TimedEqualAssertion(fluent: TimedExpr, right: StaticExpr, parent: Option[Ctx], id: Id)(
implicit predef: Predef)
extends TimedAssertion(parent, id) {
override def toString: String = id match {
case Id(_, name) => s"$name: $fluent == $right"
case _ => s"$fluent == $right"
}
}
case class TimedTransitionAssertion(fluent: TimedExpr,
from: StaticExpr,
to: StaticExpr,
parent: Option[Ctx],
name: String)(implicit predef: Predef)
extends TimedAssertion(parent, name) {
override def toString: String =
if(name.startsWith(reservedPrefix)) s"$fluent == $from :-> $to"
else s"$name: $fluent == $from :-> $to"
id: Id)(implicit predef: Predef)
extends TimedAssertion(parent, id) {
override def toString: String = id match {
case Id(_, name) => s"$name: $fluent == $from :-> $to"
case _ => s"$fluent == $from :-> $to"
}
}
case class TimedAssignmentAssertion(fluent: TimedExpr,
to: StaticExpr,
parent: Option[Ctx],
name: String)(implicit predef: Predef)
extends TimedAssertion(parent, name) {
override def toString: String =
if(name.startsWith(reservedPrefix)) s"$fluent := $to"
else s"$name: $fluent := $to"
id: Id)(implicit predef: Predef)
extends TimedAssertion(parent, id) {
override def toString: String = id match {
case Id(_, name) => s"$name: $fluent := $to"
case _ => s"$fluent := $to"
}
}
trait TemporalQualifier
......@@ -201,7 +204,7 @@ package object full {
override def toString: String = s"$template(${params.mkString(", ")})"
}
final case class ActionTemplate(override val name: String,
final case class ActionTemplate(id: Id,
containingModel: Model,
override val store: BlockStore[InActionBlock] = new BlockStore())
extends Ctx
......@@ -210,12 +213,15 @@ package object full {
override val scope: InnerScope = parent.get.scope + name
def +(block: InActionBlock): ActionTemplate =
new ActionTemplate(name, containingModel, store + block)
ActionTemplate(id, containingModel, store + block)
def ++(newBlocks: Seq[InActionBlock]): ActionTemplate = newBlocks.headOption match {
case Some(first) => (this + first) ++ newBlocks.tail
case None => this
}
def map(f: InActionBlock => InActionBlock): ActionTemplate =
ActionTemplate(id, containingModel, store.map(f))
override def toString: String =
s"action $name(${store.blocks
.collect { case x: ArgDeclaration => s"${x.arg.typ} ${x.arg.id.name}" }
......@@ -224,10 +230,10 @@ package object full {
" };"
}
case class Model(store: BlockStore[InModuleBlock] = new BlockStore()) extends Ctx {
final case class Model(store: BlockStore[InModuleBlock] = new BlockStore()) extends Ctx {
override def parent = None
override def name = "_module_"
override val scope: Scope = RootScope
override val id: Id = scope.makeNewId()
def +(block: InModuleBlock): Option[Model] = {
Some(Model(store + block))
......@@ -237,6 +243,10 @@ package object full {
blocks.foldLeft(Option(this))((m, block) => m.flatMap(_ + block))
}
def map(f: InModuleBlock => InModuleBlock): Model = {
Model(store.map(f))
}
override def toString: String =
"module:\n" +
store.blocks
......@@ -244,8 +254,8 @@ package object full {
.mkString("\n")
}
class BlockStore[+T <: Block] private (val blocks: Vector[T],
val declarations: Map[Id, Declaration[_]]) {
final class BlockStore[+T <: Block] private (val blocks: Vector[T],
val declarations: Map[Id, Declaration[_]]) {
def this() = this(Vector(), Map())
......@@ -265,15 +275,20 @@ package object full {
new BlockStore[B](newBlocks, newDeclarations)
}
def map[B <: Block](f: T => B): BlockStore[B] = {
blocks.foldLeft(new BlockStore[B]())((store, b) => store + f(b))
}
}
trait Ctx {
def scope: Scope
def id: Id
def id(name: String): Id = Id(scope, name)
def parent: Option[Ctx]
def name: String
final def name: String = id.name
def root: Ctx = parent match {
case Some(p) => p.root
case None => this
......
package dahu.planning
package object model {
val reservedPrefix = "__"
private[this] var nextID = 0
def defaultId(): String = reservedPrefix + { nextID += 1; nextID - 1 }
}
......@@ -95,7 +95,10 @@ object ActionInstantiation {
case RootScope => RootScope
case InnerScope(s, name) => transScope(s) + name
}
x match { case Id(s, name) => Id(transScope(s), name) }
x match {
case Id(s, name) => Id(transScope(s), name)
case x: Anonymous => new Anonymous(transScope(x.scope))
}
}
val instanceContent = template.content.map(s => implicits.ofBlock.map(s, trans))
......
......@@ -119,7 +119,7 @@ object FullToCore {
val startEnd: CoreM[Interval[Expr]] =
qualifier match {
case full.Equals(interval)
if ctx.config.mergeTimepoints && assertion.name.startsWith(reservedPrefix) =>
if ctx.config.mergeTimepoints && assertion.id.isAnonymous =>
// we are asked to merge timepoints and assertion was not given a name
// use the timepoints from the interval instead of the one of the assertion
for {
......
......@@ -3,14 +3,16 @@ package dahu.planning.pddl.parser
import dahu.planning.model.common._
import dahu.planning.model.full._
import Utils._
import dahu.planning.pddl.parser.TQual.Start
import dahu.utils.errors._
import fr.uga.pddl4j.parser.{Exp, Op}
import scala.collection.JavaConverters._
import scala.collection.mutable
class ActionFactory(actionName: String, parent: Resolver, model: Model) extends Factory {
implicit def predef = parent.predef
private var template = ActionTemplate(actionName, model)
private var template = ActionTemplate(model.scope / actionName, model)
private val start = LocalVar(resolver.id(predef.StartSym), resolver.predef.Time)
private val end = LocalVar(resolver.id(predef.EndSym), resolver.predef.Time)
......@@ -28,7 +30,7 @@ class ActionFactory(actionName: String, parent: Resolver, model: Model) extends
template = template + block
}
def loadOp(op: Op): Unit = {
def preprocess(op: Op): IntermediateAction = {
require(op.getName.getImage == actionName)
op.getParameters.asScala.foreach {
......@@ -45,81 +47,111 @@ class ActionFactory(actionName: String, parent: Resolver, model: Model) extends
rec(
TemporallyQualifiedAssertion(
Equals(ClosedInterval(start, start)),
TimedEqualAssertion(resolver.getTranslator(f).fluent(f, args, resolver), duration, Some(context), resolver.nextId())
TimedEqualAssertion(resolver.getTranslator(f).fluent(f, args, resolver), duration, Some(context), model.scope.makeNewId())
)
)
case x =>
unexpected(x.toString)
}
val ass = assertions(op.getPreconditions, op.getEffects)
IntermediateAction(context, start, end, ass)
}
recPrecondition(op.getPreconditions)
recEffect(op.getEffects)
def asEffectAss(e: Exp): TimedAssignmentAssertion = e match {
case ast.AssertionOnFunction(funcName) =>
resolver.getTranslator(funcName).effect(e, resolver)
}
def asCondAss(e: Exp): TimedEqualAssertion = e match {
case ast.AssertionOnFunction(funcName) =>
resolver.getTranslator(funcName).condition(e, resolver)
}
def assertions(conds: Exp, effs: Exp): Seq[Ass] = {
def getPre(pre: Exp): Seq[Ass] = pre match {
case ast.And(subs) => subs.flatMap(getPre)
case ast.AtStart(e) => Ass(TQual.Start, asCondAss(e)) :: Nil
case ast.AtEnd(e) => Ass(TQual.End, asCondAss(e)) :: Nil
case ast.OverAll(e) => Ass(TQual.All, asCondAss(e)) :: Nil
}
def getEff(pre: Exp): Seq[Ass] = pre match {
case ast.And(subs) => subs.flatMap(getEff)
case ast.AtStart(e) => Ass(TQual.Start, asEffectAss(e)) :: Nil
case ast.AtEnd(e) => Ass(TQual.End, asEffectAss(e)) :: Nil
case ast.OverAll(e) => Ass(TQual.All, asEffectAss(e)) :: Nil
}
getPre(conds) ++ getEff(effs)
}
}
private def recPrecondition(pre: Exp): Unit = pre match {
case ast.And(subs) =>
subs.foreach(recPrecondition)
case ast.AtStart(e) =>
e match {
case ast.AssertionOnFunction(funcName) =>
val assertion = resolver.getTranslator(funcName).condition(e, resolver)
rec(
TemporallyQualifiedAssertion(
Equals(ClosedInterval(start, start)),
assertion
)
)
}
case ast.AtEnd(e) =>
e match {
case ast.AssertionOnFunction(funcName) =>
val assertion = resolver.getTranslator(funcName).condition(e, resolver)
rec(
TemporallyQualifiedAssertion(
Equals(ClosedInterval(end, end)),
assertion
)
)
}
case ast.OverAll(e) =>
e match {
case ast.AssertionOnFunction(funcName) =>
val assertion = resolver.getTranslator(funcName).condition(e, resolver)
rec(
TemporallyQualifiedAssertion(
Equals(ClosedInterval(start, end)),
assertion
)
)
}
object ActionFactory {
def build(op: Op, resolver: Resolver, model: Model): ActionTemplate = {
implicit val predef: PddlPredef = resolver.predef
val pre = preProcess(op, resolver, model)
val optimizedAssertions = linter(pre.assertions)
postProcess(pre.copy(assertions = optimizedAssertions))
}
private def recEffect(pre: Exp): Unit = pre match {
case ast.And(subs) =>
subs.foreach(recEffect)
case ast.AtStart(e) =>
e match {
case ast.AssertionOnFunction(funcName) =>
val assertion = resolver.getTranslator(funcName).effect(e, resolver)
rec(
TemporallyQualifiedAssertion(
Equals(LeftOpenInterval(start, BinaryExprTree(operators.Add, start, predef.Epsilon))),
assertion
)
)
}
case ast.AtEnd(e) =>
e match {
case ast.AssertionOnFunction(funcName) =>
val assertion = resolver.getTranslator(funcName).effect(e, resolver)
rec(
TemporallyQualifiedAssertion(
Equals(LeftOpenInterval(end, BinaryExprTree(operators.Add, end, predef.Epsilon))),
assertion
)
)
}
def preProcess(op: Op, resolver: Resolver, model: Model): IntermediateAction = {
val fact = new ActionFactory(op.getName.getImage, resolver, model)
fact.preprocess(op)
}
def result: ActionTemplate = template
def postProcess(act: IntermediateAction)(implicit predef: PddlPredef): ActionTemplate = {
var mod = act.base
val start = act.start
val end = act.end
def add(t: TemporalQualifier, e: TimedAssertion): Unit = {
mod = mod + TemporallyQualifiedAssertion(t, e)
}
import TQual._
for(ass <- act.assertions) ass match {
case Ass(Start, e: TimedEqualAssertion) =>
add(Equals(ClosedInterval(start, start)), e)
case Ass(End, e: TimedEqualAssertion) =>
add(Equals(ClosedInterval(end, end)), e)
case Ass(All, e: TimedEqualAssertion) =>
add(Equals(ClosedInterval(start, end)), e)
case Ass(Start, e: TimedAssignmentAssertion) =>
add(Equals(LeftOpenInterval(start, BinaryExprTree(operators.Add, start, predef.Epsilon))), e)
case Ass(End, e: TimedAssignmentAssertion) =>
add(Equals(LeftOpenInterval(end, BinaryExprTree(operators.Add, end, predef.Epsilon))), e)
case _ => unexpected
}
mod
}
type PASS = Seq[Ass] => Seq[Ass]
case class Val(t: TQual, v: StaticExpr)
case class Reqs(fluent: TimedExpr, cond: Seq[Val], effs: Seq[Val]) {
override def toString: String = s"$fluent\n ${cond.mkString(" ")}\n ${effs.mkString(" ")}"
}
val linter: PASS = asss => {
val reqs = asss.groupBy(_.ass.fluent).map {
case (fluent, s) =>
val (conds, effs) = s.foldLeft((List[Val](), List[Val]())) {
case ((cs, es), Ass(t, TimedEqualAssertion(_, v, _, _))) => (Val(t, v) :: cs, es)
case ((cs, es), Ass(t, TimedAssignmentAssertion(_, v, _, _))) => (cs, Val(t, v) :: es)
case _ => unexpected
}
Reqs(fluent, conds, effs)
}
println( )
reqs.foreach(println)
asss
}
}
sealed trait TQual
object TQual {
case object Start extends TQual
case object End extends TQual
case object All extends TQual
}
case class Ass(qual: TQual, ass: TimedAssertion)
case class IntermediateAction(base: ActionTemplate, start: LocalVar, end: LocalVar, assertions: Seq[Ass])
......@@ -31,8 +31,6 @@ abstract class Factory { self =>
unexpected(s"unknown variable: $name")
}
override def nextId(): String = dahu.planning.model.reservedPrefix + next().toString
def getTranslator(name: String): FunctionCompat = self.getTranslator(name)
override def predef: PddlPredef = self.predef
......@@ -136,9 +134,7 @@ class ModelFactory(val predef: PddlPredef) extends Factory {
dom.getDerivesPredicates.asScala.foreach(_ => ???)
dom.getOperators.asScala.foreach { op =>
val fact = new ActionFactory(op.getName.getImage, resolver, model)
fact.loadOp(op)
rec(fact.result)
rec(ActionFactory.build(op, resolver, model))
}
}
......
......@@ -50,7 +50,7 @@ class DefaultPredicate(pddl: NamedTypedList, top: Resolver) extends FunctionComp
Fluent(model, args.map(local.variable)),
local.predef.True,
Some(local.ctx),
local.nextId()
local.scope.makeNewId()
)
case _ => unexpected
}
......@@ -61,14 +61,14 @@ class DefaultPredicate(pddl: NamedTypedList, top: Resolver) extends FunctionComp
fluent(fun, args, local),
local.predef.True,
Some(local.ctx),
local.nextId()
local.scope.makeNewId()
)
case ast.Not(ast.Fluent(fun, args)) =>
TimedAssignmentAssertion(
fluent(fun, args, local),
predef.False,
Some(local.ctx),
local.nextId()
local.scope.makeNewId()
)
case _ => unexpected
}
......@@ -96,7 +96,7 @@ class DefaultFunction(pddl: NamedTypedList, top: Resolver) extends FunctionCompa
fluent(funName, args, local),
rhs,
Some(local.ctx),
local.nextId()
local.scope.makeNewId()
)
case _ => unexpected
}
......@@ -107,7 +107,7 @@ class DefaultFunction(pddl: NamedTypedList, top: Resolver) extends FunctionCompa
fluent(funName, args, local),
rhs,
Some(local.ctx),
local.nextId()
local.scope.makeNewId()
)
case _ => unexpected
}
......
......@@ -5,6 +5,7 @@ import java.io.File
import dahu.planning.model.core.CoreModel
import dahu.planning.model.full.Model
import dahu.planning.model.transforms.FullToCore
import dahu.planning.pddl.parser.optim.ActionRewrite
import scala.util.Try
......@@ -24,7 +25,10 @@ class Parser(opt: Options) {
def parse(domain: File, problem: File): Try[CoreModel] = {
parseToFull(domain, problem).flatMap { m =>
Try(FullToCore.trans(m))
Try {
val opt = ActionRewrite.optimize(m)
FullToCore.trans(opt)
}
}
}
......
......@@ -14,8 +14,6 @@ trait Resolver {
def variable(name: String): StaticExpr
def getTranslator(name: String): FunctionCompat
def nextId(): String
}
object Resolver {
......
package dahu.planning.pddl.parser.optim
import dahu.planning.model.common._
import dahu.planning.model.full._
import dahu.planning.pddl.parser.PddlPredef