Main.scala 6.73 KB
Newer Older
1 2
package dahu.planner

3
import dahu.planning.model.core.{ActionTemplate, Statement}
4
import dahu.utils.errors._
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
5
import java.io.{File, FileWriter}
6
import dahu.planning.anml.parser._
7

8
import dahu.planning.model.core
Arthur Bit-Monnot's avatar
Arthur Bit-Monnot committed
9
import dahu.model.input.Tentative
10
import dahu.planning.model.common.Predef
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
11 12 13 14 15 16 17 18
import monix.eval.{MVar, Task}

import scala.concurrent.duration._
import monix.execution.Scheduler.Implicits.global

import scala.concurrent.{Await, Promise, TimeoutException}
import scala.util.{Failure, Success, Try}
import dahu.utils.debug._
19

Arthur Bit-Monnot's avatar
Arthur Bit-Monnot committed
20
case class Config(problemFile: File = null,
21 22
                  minInstances: Int = 0,
                  maxInstances: Int = 500,
Arthur Bit-Monnot's avatar
Arthur Bit-Monnot committed
23
                  symBreak: Boolean = true,
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
24 25
                  useXorForSupport: Boolean = true,
                  numThreads: Int = 1,
26 27
                  maxRuntime: Int = 1800,
                  warmupTimeSec: Int = 0)
28 29 30

object Main extends App {

31 32
  val parser = new scopt.OptionParser[Config]("dahu") {
    head("dahu", "0.x")
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
33

Arthur Bit-Monnot's avatar
Arthur Bit-Monnot committed
34 35
//    opt[Int]("num-threads")
//      .action((n, c) => c.copy(numThreads = n))
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
36 37 38 39

    opt[Int]("warmup")
      .action((t, c) => c.copy(warmupTimeSec = t))

40 41 42
    opt[Int]("min-depth")
      .action((d, c) => c.copy(minInstances = d))

Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
43 44 45 46 47 48
    opt[Int]("max-depth")
      .action((d, c) => c.copy(maxInstances = d))

    opt[Int]("timeout")
      .action((t, c) => c.copy(maxRuntime = t))

Arthur Bit-Monnot's avatar
Arthur Bit-Monnot committed
49 50 51 52 53
//    opt[Boolean]("use-xor")
//      .action((b, c) => c.copy(useXorForSupport = b))
//
//    opt[Boolean]("sym-break")
//      .action((b, c) => c.copy(symBreak = b))
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
54

55
    arg[File]("XXXX.pb.anml").action((f, c) => c.copy(problemFile = f))
56 57
  }

Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
58 59
  import dahu.utils.debug._

60
  parser.parse(args, Config()) match {
Arthur Bit-Monnot's avatar
Arthur Bit-Monnot committed
61 62
    case Some(cfg) =>
      implicit val cfgImpl = cfg
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
63 64 65 66 67

      if(cfg.warmupTimeSec > 0) {
        info("Warming up...")
        dahu.utils.debug.LOG_LEVEL = 0

68 69
        val warmUpTask =
          solveTask(cfg.problemFile, System.currentTimeMillis() + cfg.warmupTimeSec * 1000)
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
70 71 72 73
            .map(res => Success(res))
            .timeoutTo(cfg.warmupTimeSec.seconds, Task(Failure(new TimeoutException())))
            .runAsync

74
        Try(Await.result(warmUpTask, (cfg.warmupTimeSec + 10).seconds))
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
75 76 77 78 79 80 81 82

        dahu.utils.debug.LOG_LEVEL = 3
      }

      val startTime = System.currentTimeMillis()

      val future =
        solveTask(cfg.problemFile, System.currentTimeMillis() + cfg.maxRuntime * 1000)
83 84 85
          .map(res => Success(res))
          .timeoutTo(cfg.maxRuntime.seconds, Task(Failure(new TimeoutException())))
          .runAsync
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
86

87
      Await.result(future, (cfg.maxRuntime + 10).seconds) match {
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
88 89
        case Success(Some(result)) =>
          val runtime = System.currentTimeMillis() - startTime
90
          out(s"== Solution (in ${runtime / 1000}.${(runtime % 1000) / 10}s) ==")
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
91 92 93
          out(result.toString)
        case Success(None) =>
          out("Max depth or time reached")
94
        case Failure(_: TimeoutException) =>
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
95 96 97 98 99 100 101
          out("Timeout")
        case Failure(exception) =>
          out("Crash")
          exception.printStackTrace()
      }

      sys.exit(0)
102 103
    case None =>
  }
104

Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
105 106 107 108 109 110 111 112 113
  def solveTask(problemFile: File, deadline: Long)(implicit cfg: Config): Task[Option[Any]] = {
    Task {
      info(problemFile.getName)
      solve(problemFile, deadline)
    }
  }

  def solve(problemFile: File, deadline: Long)(implicit cfg: Config): Option[String] = {
    info("Parsing...")
114 115
    parse(problemFile) match {
      case ParseSuccess(model) =>
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
116
        solveIncremental(model, cfg.maxInstances, deadline)
117
      case fail: ParseFailure =>
118 119 120
        println("Parsing failed:")
        println(fail.format)
        sys.exit(1)
121 122 123
      case ParserCrash(err, _) =>
        println(s"Parser crashed.")
        err.printStackTrace()
124
        sys.exit(1)
125
    }
126
  }
127

128
  def solveIncremental(model: core.CoreModel, maxSteps: Int, deadline: Long)(
129 130
      implicit cfg: Config,
      predef: Predef): Option[String] = {
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
131 132 133 134 135 136
    val q = new java.util.concurrent.ConcurrentLinkedQueue[Integer]()
    for(i <- cfg.minInstances to cfg.maxInstances)
      q.add(i)

    val workers = (0 until cfg.numThreads) map { workerID =>
      Task {
137
        while(System.currentTimeMillis() < deadline) {
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
138
          val step: Integer = q.poll()
139
          if(step == null)
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
140 141 142 143 144 145 146 147 148 149 150 151
            return None

          info(s"Depth: $step (worker: $workerID)")
          solveIncrementalStep(model, step.toInt, deadline) match {
            case Some(sol) =>
              info(s"  Solution found at depth $step (worker: $workerID)")
              return Some(sol)
            case None =>
              info(s"  No solution at depth $step (worker: $workerID)")
          }
        }
        None
152
      }: Task[Option[String]]
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
153 154 155 156 157 158 159 160 161
    }
    val future = Task.raceMany(workers).runAsync

    Try {
      Await.result(future, (deadline - System.currentTimeMillis()).millis)
    } match {
      case Success(solution) =>
        solution
      case Failure(to: TimeoutException) => None
162
      case Failure(e)                    => throw e
163
    }
164 165
  }

166
  def solveIncrementalStep(model: core.CoreModel, step: Int, deadline: Long)(
167 168
      implicit cfg: Config,
      predef: Predef): Option[String] = {
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
169 170 171 172
    if(System.currentTimeMillis() >= deadline)
      return None

    info("  Processing ANML model...")
173 174 175 176
    val ctx = ProblemContext.extract(model)
    val result = model.foldLeft(Chronicle.empty(ctx)) {
      case (chronicle, statement: Statement) => chronicle.extended(statement)(_ => unexpected)
      case (chronicle, action: ActionTemplate) =>
Arthur Bit-Monnot's avatar
Arthur Bit-Monnot committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
        val actionInstances: Seq[Opt[Action[Tentative]]] =
          if(cfg.symBreak) {
            import dahu.model.input.dsl._
            (0 until step).foldLeft(List[Opt[Action[Tentative]]]()) {
              case (Nil, _) => // first action
                Opt.optional(Action.instance(action, ctx)) :: Nil
              case (last :: rest, _) =>
                // not first, enforce that this action is only present if the last one is and that its start no earliest that the last one
                val act = Opt.optional(Action.instance(action, ctx))
                val presence = act.present implies last.present
                val after = act.a.start >= last.a.start
                val withSymBreak: Opt[Action[Tentative]] = act.copy(
                  a = act.a.copy(
                    chronicle = act.a.chronicle.copy(
                      constraints = presence :: after :: act.a.chronicle.constraints
                    )))
                withSymBreak :: last :: rest
            }

          } else {
            (0 until step).map { _ =>
              Opt.optional(Action.instance(action, ctx))
            }
          }
201 202 203 204
        chronicle.copy(actions = chronicle.actions ++ actionInstances)
      case (chronicle, _) => chronicle
    }
//        println(result)
Arthur Bit-Monnot's avatar
WIP  
Arthur Bit-Monnot committed
205
    val solution = Planner.solve(result, deadline)
206 207
//        println(solution)
    solution
208 209 210
  }

}