From 1e61253b3b3731731856b8d009659c64a17d42c7 Mon Sep 17 00:00:00 2001 From: Pascal Date: Wed, 29 Jan 2020 01:19:10 -0600 Subject: [PATCH] [add][fix] added output that can be run parallel to the simulation - the output sleep waits for a full tick result - [fix] fixed the simulation to pass the correct gt parent if exists --- src/main/scala/scim/components/Output.scala | 10 ++++++---- src/main/scala/scim/components/Simulation.scala | 15 ++++++++++----- .../scala/scim/datastruct/Configuration.scala | 14 ++++++++++---- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/main/scala/scim/components/Output.scala b/src/main/scala/scim/components/Output.scala index 39abb0c..c2664dc 100644 --- a/src/main/scala/scim/components/Output.scala +++ b/src/main/scala/scim/components/Output.scala @@ -1,13 +1,15 @@ package scim.components -import scim.datastruct.SharedMemory +import scim.datastruct.{Cell, Group, SensorResult, SharedMemory} class Output(sharedMemory: SharedMemory) extends Runnable { def run(): Unit = { - outputData() + val output = sharedMemory.output + (0 until output.size, output).zipped.foreach((time, result) => outputData(time, result.watch())) } - def outputData(): Unit = { - + def outputData(time: Int, output: Map[Cell, Map[Group, Vector[SensorResult]]]): Unit = { + println("TICK: " + time) + println(output) } } diff --git a/src/main/scala/scim/components/Simulation.scala b/src/main/scala/scim/components/Simulation.scala index b13dbb1..f36b290 100644 --- a/src/main/scala/scim/components/Simulation.scala +++ b/src/main/scala/scim/components/Simulation.scala @@ -7,6 +7,7 @@ class Simulation(config: Configuration) { private val sharedMemory = config.sharedMemory private val sharedConfig = sharedMemory.config private val cells = config.cells + val output = sharedMemory.output def start(): Unit = { @@ -38,6 +39,8 @@ class Simulation(config: Configuration) { } else { cells.foreach(simCell) } + // broadcast the results + output(time.time).broadcast(sensorResults) } private def simGroups(time: Time, cell: Cell, groups: Map[Group, List[(Group, Sensor, Option[GroundTruth])]], prevTruths: Map[Group, Truth], resultTruths: Map[Group, Truth], resultSensors: Map[Group, Vector[SensorResult]]): Unit = { @@ -46,12 +49,14 @@ class Simulation(config: Configuration) { // at the same time as the parent in a different thread. This would allow the child to start running as soon // as the ground truth is calculated in the parent and doesn't have have to wait for the sensors to finish. // Any subsequent children can start in the same manner, when their parent is done with the ground truth. + // this would require sharedConfig.cellThreadPool * 2 additional threads // : - // only run ground truth in list, have sensors in their respective groups - // requires groups to be split into two elements: sensor and ground truth - // Tuple2(Map[Group, list[(Group, Option[GroundTruth])]], Map[Group, Sensor]) - def simGroup(group: (Group, List[(Group, Sensor, Option[GroundTruth])])): Unit = { - group._2.foreach(element => simulate(time, cell, prevTruths(group._1), resultTruths(group._1), resultSensors(group._1), element)) + // another option would be to calculate all ground truths within a list first and than parellize the sensor list + def simGroup(input: (Group, List[(Group, Sensor, Option[GroundTruth])])): Unit = { + val group = input._1 + val truthGroup = group.getParentOrGroup // make sure the correct ground truth gets passed + val groupList = input._2 + groupList.foreach(element => simulate(time, cell, prevTruths(truthGroup), resultTruths(group), resultSensors(group), element)) } if(sharedConfig.parGroup != 0 && cells.size >= sharedConfig.parGroup ) { val groupList = groups.par diff --git a/src/main/scala/scim/datastruct/Configuration.scala b/src/main/scala/scim/datastruct/Configuration.scala index b2627f9..e299adf 100644 --- a/src/main/scala/scim/datastruct/Configuration.scala +++ b/src/main/scala/scim/datastruct/Configuration.scala @@ -15,9 +15,13 @@ case class Time(time: Int, duration: Int){ def get: Int = { time } } -case class SharedMemory( sensor: Map[Time, Map[Cell, Map[Group, Vector[SensorResult]]]], - groundTruth: Map[Time, Map[Cell, Map[Group, Truth]]], - config: SimulationConfig, cache: Boolean = true) { +// while it would seem convinient to just throw the sensors/behaviours as value into these +// and have them just hold their value, this would get messy with dependencies: +// Map[Time, Map[Cell, Map[Group, List(Group, Truth)]]] and getting the result for the sensor +case class SharedMemory(sensor: Map[Time, Map[Cell, Map[Group, Vector[SensorResult]]]], + groundTruth: Map[Time, Map[Cell, Map[Group, Truth]]], + config: SimulationConfig, + output: Vector[BroadcastObject[Map[Cell, Map[Group, Vector[SensorResult]]]]]) { // This could be spatially optimized to have a function to invoke a new simulation tick // a template of Map[SensorID, SensorResult] would have to be kept and deep copied when // another tick is simulated. this won't work for ground truth, as it may already be defined @@ -99,6 +103,8 @@ case class Behaviour(behaviour: BehaviourVector){ } } +// while the gt depends on the cell, the object itself does not +// unlike the sensor which is unique due to its count for each cell case class GroundTruth(group: Group, behaviour: Option[Behaviour]) { def simulate(cell: Cell, time: Time, prevTruth: Truth, result: Truth): Unit = { // don't need a truth, if the sensor uses another, but this should not happen @@ -136,6 +142,6 @@ 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)