From 95bc1363f9b1842ef463737e4f8d872d899e7a44 Mon Sep 17 00:00:00 2001 From: Pascal Date: Wed, 29 Jan 2020 01:19:11 -0600 Subject: [PATCH] [add] added missing tickfactor parsing - override Time compares to only use Time.time - added Option[Int] tickfactor to Time class - behaviours need to be selfaware and use Time.tickfactor - addded tickfactor to simulation time vector - added parser tickfactor parsing - modified output to use tickfactor aware duration --- .../scala/scim/components/Behaviour.scala | 10 +++---- src/main/scala/scim/components/Output.scala | 4 +-- src/main/scala/scim/components/Parser.scala | 22 +++++++------- .../scala/scim/components/Simulation.scala | 2 +- .../scala/scim/datastruct/Configuration.scala | 29 ++++++++++++++++--- src/main/scala/scim/lib/Interfaces.scala | 4 +++ 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/main/scala/scim/components/Behaviour.scala b/src/main/scala/scim/components/Behaviour.scala index e770ad5..cf22210 100644 --- a/src/main/scala/scim/components/Behaviour.scala +++ b/src/main/scala/scim/components/Behaviour.scala @@ -83,9 +83,9 @@ package object behaviour { val y = modifier.get.getKey[Int]("y") val timeMod = modifier.get.getKey[Int]("time") (timeMod, x, y) match { - case (timeFault, None, None) if (!timeFault.isEmpty && timeFault.get == time.time) => behaviour(cell, time, inTruth, modifyArgs(args)) + case (timeFault, None, None) if (!timeFault.isEmpty && (timeFault.get * time.tickfactor.getOrElse(1)) == time.time) => behaviour(cell, time, inTruth, modifyArgs(args)) case (None, xVal, yVal) if (!xVal.isEmpty && !yVal.isEmpty && Cell(xVal.get, yVal.get) == cell) => behaviour(cell, time, inTruth, modifyArgs(args)) - case (timeFault, xVal, yVal) if (!timeFault.isEmpty && timeFault.get == time.time && !xVal.isEmpty && !yVal.isEmpty && Cell(xVal.get, yVal.get) == cell) => + case (timeFault, xVal, yVal) if (!timeFault.isEmpty && (timeFault.get * time.tickfactor.getOrElse(1)) == time.time && !xVal.isEmpty && !yVal.isEmpty && Cell(xVal.get, yVal.get) == cell) => behaviour(cell, time, inTruth, modifyArgs(args)) case _ => behaviour(cell, time, inTruth, args) } @@ -163,7 +163,7 @@ package object behaviour { val center: Vector[Int] = args.getParam("center", Vector(0, 0)) val distOffset: Option[Double] = args.getParam[Double]("distOffset") val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time") - val offset = if(!distOffset.isEmpty && offsetTimes.get.contains(time.time)) { + val offset = if(!distOffset.isEmpty && offsetTimes.get.contains(time.getNormalized().getOrElse(-1))) { val centerCell = Cell(center(0), center(1)) centerCell.distance(cell) * distOffset.get } else { 0 } @@ -179,7 +179,7 @@ package object behaviour { if(inTruth.isEmpty) return None val range: Double = args.getParam("range", 1.0) val target: Double = args.getParam("target", inTruth.get + 5) - val targetTime: Int = args.getParam("targetTime", time.time + 1) + val targetTime: Int = args.getParam("targetTime", time.getNormalized + 1) * time.tickfactor.getOrElse(1) val center: Vector[Int] = args.getParam("center", Vector(0, 0)) val distOffset: Option[Double] = args.getParam[Double]("distOffset") val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time") @@ -190,7 +190,7 @@ package object behaviour { } else { 0 } // only change truth on offsetTimes - val truth = if(!offsetTimes.isEmpty && offsetTimes.get.contains(time.time)) inTruth.get + offset else inTruth.get + val truth = if(!offsetTimes.isEmpty && offsetTimes.get.contains(time.getNormalized().getOrElse(-1))) inTruth.get + offset else inTruth.get val rng = new Random() val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1) diff --git a/src/main/scala/scim/components/Output.scala b/src/main/scala/scim/components/Output.scala index c62c03e..f2caf47 100644 --- a/src/main/scala/scim/components/Output.scala +++ b/src/main/scala/scim/components/Output.scala @@ -5,13 +5,13 @@ import scim.lib.consoleUtil.Color._ class Output(sharedMemory: SharedMemory, printOutput: Boolean = true) extends Runnable { def run(): Unit = { - val duration = sharedMemory.config.duration + val duration = sharedMemory.config.getDuration def outputHelper(time: Time): Unit = { val simOutput = sharedMemory.subscribe(time) outputData(time, simOutput._1, simOutput._2) } - Vector.tabulate(sharedMemory.config.duration)(t => Time(t, duration)).foreach(outputHelper) + Vector.tabulate(duration)(t => Time(t, duration)).foreach(outputHelper) } def outputData(time: Time, sensors: Map[Cell, Map[Group, Vector[SensorResult]]], groundTruths: Map[Cell, Map[Group, Truth]]): Unit = { diff --git a/src/main/scala/scim/components/Parser.scala b/src/main/scala/scim/components/Parser.scala index bc87405..66642d6 100644 --- a/src/main/scala/scim/components/Parser.scala +++ b/src/main/scala/scim/components/Parser.scala @@ -53,7 +53,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu val configurationMapping = sensorList.map(cell => cell._1 -> cellMapper(cell._1, cell._2.toMap)).toMap // construct shared memory sensor - val sharedMemorySensor = Vector.tabulate(config.duration)(t => (Time(t, config.duration), sensorList.map(kv => kv._1 -> kv._2.map(kv2 => kv2._1 -> Vector.tabulate(kv2._2.count)(_ => SensorResult())).toMap).toMap)) + val sharedMemorySensor = Vector.tabulate(config.getDuration)(t => (Time(t, config.getDuration), sensorList.map(kv => kv._1 -> kv._2.map(kv2 => kv2._1 -> Vector.tabulate(kv2._2.count)(_ => SensorResult())).toMap).toMap)) def truthHelper(cell: Cell, list: Map[Group, List[(Group, Sensor, Option[GroundTruth])]]): (Cell, Map[Group, Truth]) = { val groupList: mutable.Map[Group, Truth] = mutable.Map() @@ -61,7 +61,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu cell -> groupList.toMap } - val sharedMemoryTruth = Vector.tabulate(config.duration)(t => (Time(t, config.duration), configurationMapping.map(a => truthHelper(a._1, a._2)))) + val sharedMemoryTruth = Vector.tabulate(config.getDuration)(t => (Time(t, config.getDuration), configurationMapping.map(a => truthHelper(a._1, a._2)))) val sharedMemorySensorMap = traversableToMap[Time, Map[Cell, Map[Group, Vector[SensorResult]]]](sharedMemorySensor) @@ -129,9 +129,9 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu private def parseTimeLineElement(timeLineElement: Any): (Int, Option[Int]) = { timeLineElement match { case timeLineElement: Vector[_] => val elementVector = timeLineElement.asInstanceOf[Vector[Int]] - (elementVector(0), if(elementVector.size > 1) Some(elementVector(1)) else None) + (elementVector(0) * config.tickfactor, if(elementVector.size > 1) Some(elementVector(1) * config.tickfactor) else None) case timeLineElement: Map[_, _] => val elementMap = timeLineElement.asInstanceOf[Map[String, Int]] - (elementMap("start"), if(elementMap.contains("end")) Some(elementMap("end")) else None) + (elementMap("start") * config.tickfactor, if(elementMap.contains("end")) Some(elementMap("end") * config.tickfactor) else None) } } @@ -140,7 +140,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu var funcPointer: Option[(SimulationBehaviour, Option[FunctionArgs])] = None function match { case function: Vector[_] => { - val start: Int = function(0).asInstanceOf[Int] + val start: Int = function(0).asInstanceOf[Int] * config.tickfactor // val end: Option[Int] = None // Not used val name: String = function(1).asInstanceOf[String] val args: Option[FunctionArgs] = if (function.size > 2) Some(FunctionArgs(function(2).asInstanceOf[Map[String, Any]])) else None @@ -171,12 +171,12 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu if(!kv._2.isEmpty || !parsedFunction.contains(kv._1)) // only set None when it doesnt exist yet parsedFunction(kv._1) = kv._2 } )) - val timeVector: mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]] = new mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]](config.duration).map(_ => None) + val timeVector: mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]] = new mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]](config.getDuration).map(_ => None) val it = parsedFunction.toList.sortBy(_._1).iterator var current = if(it.hasNext) Some(it.next) else None while(!current.isEmpty) { val next = if(it.hasNext) Some(it.next) else None - for(time <- current.get._1 to (if(!next.isEmpty && next.get._1 < config.duration) next.get._1 else config.duration - 1)) { + for(time <- current.get._1 to (if(!next.isEmpty && next.get._1 < config.getDuration) next.get._1 else config.getDuration - 1)) { timeVector(time) = current.get._2 } current = next @@ -250,7 +250,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu val start: Int = truthObject(0).asInstanceOf[Int] // val end: Option[Int] = None // Not used truth.set(parseDouble(truthObject(1))) - Map[Time, Truth](Time(start, config.duration) -> truth) + Map[Time, Truth](config.time(start) -> truth) } case truthObject: Map[_, _] => { val truth: Truth = Truth() @@ -262,10 +262,10 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu val output: mutable.Map[Time, Truth] = mutable.Map() timeLineVector.map(tuple => { - output(Time(tuple._1, config.duration)) = truth + output(config.time(tuple._1)) = truth if (!tuple._2.isEmpty) { endTruth.set(None) - output(Time(tuple._2.get, config.duration)) = endTruth + output(config.time(tuple._2.get)) = endTruth } }) output.toMap @@ -293,7 +293,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu } else { if(!behaviourListSensor.contains(group)) { debugf(DebugType.WARNING, "Group [%s] does not have a behaviour, using default", group.toString()) - behaviourListSensor(group) = Behaviour(BehaviourVector(Vector.tabulate(config.duration)(_ => None))) + behaviourListSensor(group) = Behaviour(BehaviourVector(Vector.tabulate(config.getDuration)(_ => None))) } if(group.parent.isEmpty) { groupDone.add(group) diff --git a/src/main/scala/scim/components/Simulation.scala b/src/main/scala/scim/components/Simulation.scala index d5830ff..b9289e7 100644 --- a/src/main/scala/scim/components/Simulation.scala +++ b/src/main/scala/scim/components/Simulation.scala @@ -15,7 +15,7 @@ class Simulation(config: Configuration) { def start(): Unit = { val duration = config.getDuration - val timeVector = Vector.tabulate(duration)(t => Time(t, duration)) + val timeVector = Vector.tabulate(duration)(t => Time(t, duration, Some(config.getTickFactor))) def simTime(time: Time): Unit = { // FIXME: really shouldn't have to .map(kv => kv._1 -> kv._2) to get an immutable map, in fact it shouldn't even be mutable in the first place... val prevTruths: scala.collection.immutable.Map[Cell, Map[Group, Truth]] = if(time.start) sharedMemory.groundTruth(time).map(kv => kv._1 -> kv._2) else sharedMemory.groundTruth(time.prev).map(kv => kv._1 -> kv._2) diff --git a/src/main/scala/scim/datastruct/Configuration.scala b/src/main/scala/scim/datastruct/Configuration.scala index 3b02056..dd4a279 100644 --- a/src/main/scala/scim/datastruct/Configuration.scala +++ b/src/main/scala/scim/datastruct/Configuration.scala @@ -12,15 +12,31 @@ import scala.util.control.NonFatal // Note: case classes implement equality and compare their values and not the memory address // It only compares vals from the constructor -case class Time(time: Int, duration: Int) extends Ordered[Time]{ - def prev: Time = { Time(time - 1, duration) } +case class Time(time: Int, duration: Int, tickfactor: Option[Int] = None) extends Ordered[Time]{ + def prev: Time = Time(time - 1, duration, tickfactor) + def prevTickfactor = Time(time - 1 * tickfactor.getOrElse(1), duration, tickfactor) + def next: Time = Time(time + 1, duration, tickfactor) + def nextTickfactor = Time(time + 1 * tickfactor.getOrElse(1), duration, tickfactor) def start: Boolean = { time == 0 } def get: Int = { time } - + def getNormalized: Int = time / tickfactor.getOrElse(1) + def getNormalized(collision: Option[Int] = None): Option[Int] = { + if((time % tickfactor.getOrElse(1) )== 0) Some(time / tickfactor.getOrElse(1)) else collision + } + def newTime(t: Int, normalized: Boolean = true): Time = if(normalized) Time(t * tickfactor.getOrElse(1), duration, tickfactor) else Time(t, duration, tickfactor) + override def equals(obj: Any): Boolean = { + obj match { + case that: Time => time.equals(that.time) + case that: Int => time.equals(that * tickfactor.getOrElse(1)) + case _ => false + } + } override def compare(that: Time): Int = { time.compare(that.time) } + override def hashCode: Int = time.hashCode() + } // while it would seem convinient to just throw the sensors/behaviours as value into these @@ -81,6 +97,11 @@ case class SimulationConfig(duration: Int, runs: Int, tickfactor: Int = 1, val cellThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parCellThreads)) val groupThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parGroupThreads)) val sensorThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parSensorThreads)) + def time(t: Int, useTickfactor: Boolean = true): Time = { + if (useTickfactor) Time(t * tickfactor, getDuration * tickfactor, Some(tickfactor)) + else Time(t, getDuration * tickfactor, Some(tickfactor)) + } + def getDuration: Int = duration * tickfactor } case class SensorResult() extends Result[(Group, Cell, Option[Double])] /* rough size estimation @@ -195,7 +216,7 @@ case class SensorCount(count: Int) { // NOTE: The Group is actually obsolete in the Tuple3 as Sensor and GroundTruth hold theirs (unless removed from their case class) case class Configuration(cells: Map[Cell, Map[Group, List[(Group, Sensor, Option[GroundTruth])]]], sharedMemory: SharedMemory) extends SimConfig { - def getDuration: Int = sharedMemory.config.duration + def getDuration: Int = sharedMemory.config.duration * sharedMemory.config.tickfactor def getRuns: Int = sharedMemory.config.runs def getTickFactor: Int = sharedMemory.config.tickfactor def getCellThreadConfig: (Int, ForkJoinTaskSupport) = (sharedMemory.config.parCell, sharedMemory.config.cellThreadPool) diff --git a/src/main/scala/scim/lib/Interfaces.scala b/src/main/scala/scim/lib/Interfaces.scala index fec04a6..67af42d 100644 --- a/src/main/scala/scim/lib/Interfaces.scala +++ b/src/main/scala/scim/lib/Interfaces.scala @@ -34,6 +34,10 @@ package object configInterfaces { def getDuration: Int def getRuns: Int def getTickFactor: Int + def newTime(t: Int, tickfactor: Boolean = true): Time = { + if (tickfactor) Time(t * getTickFactor, getDuration * getTickFactor, Some(getTickFactor)) + else Time(t, getDuration * getTickFactor, Some(getTickFactor)) + } def getCellThreadConfig: (Int, ForkJoinTaskSupport) def getGroupThreadConfig: (Int, ForkJoinTaskSupport) def getSensorThreadConfig: (Int, ForkJoinTaskSupport)