[mod][mod] modified function to return closest keys in map into object
- two variants, one with binary search, the other with iterate - [mod] modified debug to closer mirror linux kernel debug - [mod] just added a few comments in parser and simulation
This commit is contained in:
@@ -3,7 +3,7 @@ package scim.components
|
||||
import scim.datastruct.{Behaviour, BehaviourVector, Cell, Configuration, FunctionArgs, Group, SensorCount, SharedMemory, SimulationBehaviours, SimulationConfig, Time, Truth}
|
||||
import scim.lib.ConcurrentBroadcast.BroadcastObject
|
||||
import scim.lib.debugUtil.{DebugType, debugf}
|
||||
import scim.lib.mapUtil.traversableToMap
|
||||
import scim.lib.mapUtil._
|
||||
import scim.lib.simulation.SimulationBehaviour
|
||||
import scim.lib.yml
|
||||
|
||||
@@ -47,6 +47,9 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
||||
behaviours.foreach(behaviour => parseBehaviour(behaviour))
|
||||
groundTruths.foreach(groundTruth => parseGroundTruth(groundTruth))
|
||||
// TODO: insert dummy sensors if none exists at cells of the parent group where this group exists
|
||||
// TODO: when inserting time based values, only overwrite None, dont overwrite Some() with None!
|
||||
// e.g. truth was declared at tick 9 with value 11, but later again with None at tick 9, DONT OVERWRITE
|
||||
// e.g. truth was declared at tick 9 with value none, but later again with value 9, OVERWRITE
|
||||
Unit
|
||||
}
|
||||
|
||||
@@ -78,16 +81,19 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
||||
}
|
||||
|
||||
private def parseCells(cells: Vector[Any]): Map[Cell, SensorCount] = {
|
||||
val parseCell = (cell: Any) => cell match {
|
||||
val parseCell: (Any) => (Cell, SensorCount) = (cell: Any) => cell match {
|
||||
case cell: Vector[_] => val cellVector = cell.asInstanceOf[Vector[Int]]
|
||||
(Cell(cellVector(0), cellVector(1)), SensorCount(cellVector(2)))
|
||||
case cell: Map[_, _] => val cellMap = cell.asInstanceOf[Map[String, Int]]
|
||||
(Cell(cellMap("x"), cellMap("y")), SensorCount(cellMap("count")))
|
||||
case _ => throw new Exception("Invalid Cell: " + cell.toString)
|
||||
}
|
||||
// traversableToMap iterates over the vector while parseCell returns a key value
|
||||
// pair for each element which than gets added to the returned map
|
||||
traversableToMap[Cell, SensorCount](cells, parseCell)
|
||||
}
|
||||
|
||||
// TODO: multiply time by tickfactor
|
||||
private def parseTimeLineElement(timeLineElement: Any): (Int, Option[Int]) = {
|
||||
timeLineElement match {
|
||||
case timeLineElement: Vector[_] => val elementVector = timeLineElement.asInstanceOf[Vector[Int]]
|
||||
|
||||
@@ -26,7 +26,8 @@ class Simulation(config: Configuration) {
|
||||
// Instead of using foreach, we could map and have the result returned here and write it into the shared memory
|
||||
// This would possibly result in less writes into the shared memory and allows just to return their result
|
||||
// the previous ground truth result would have to be passed into the map function, this approach
|
||||
// would fit better with functional programming than the current
|
||||
// would fit better with functional programming than the current a tuple (Sensor Result, Truth Result)
|
||||
// TODO: return (Map[Cell, Map[Group, Vector[SensorResult]]], Map[Cell, Map[Group, Truth]]) from subsequent functions
|
||||
def simCell(input: (Cell, Map[Group, List[(Group, Sensor, Option[GroundTruth])]])): Unit ={
|
||||
val cell = input._1
|
||||
val groups = input._2
|
||||
|
||||
@@ -70,21 +70,32 @@ package object debugUtil {
|
||||
sealed case class DebugType(name: String, level: Int)
|
||||
|
||||
object DebugType {
|
||||
object ERROR extends DebugType("ERROR", 0)
|
||||
object WARNING extends DebugType("WARNING", 1)
|
||||
object MESSAGE extends DebugType("MESSAGE", 2)
|
||||
val values = Seq(ERROR, WARNING, MESSAGE)
|
||||
// Comments based on linux kernel debugging (https://elinux.org/Debugging_by_printing)
|
||||
object ERROR extends DebugType("ERROR", 0) // Error occurred, simulation crashed
|
||||
object WARNING extends DebugType("WARNING", 1) // A warning, meaning nothing serious by itself but might indicate problems
|
||||
object NOTICE extends DebugType("NOTICE", 2) // Nothing serious, but notably nevertheless.
|
||||
object INFO extends DebugType("INFO", 3) // Informational message e.g. startup information at simulation start
|
||||
object DEBUG extends DebugType("DEBUG", 4) // Debug messages
|
||||
val values = Seq(ERROR, WARNING, NOTICE, INFO, DEBUG)
|
||||
}
|
||||
val level: Int = DebugType.ERROR.level
|
||||
val level: Int = DebugType.DEBUG.level
|
||||
|
||||
def currentMethodName: String = Thread.currentThread.getStackTrace()(2).getMethodName
|
||||
def currentMethodCallerName: String = Thread.currentThread.getStackTrace()(3).getMethodName
|
||||
private def debugPrefix(dbg: DebugType): String = {
|
||||
dbg.name + " [" + Thread.currentThread.getStackTrace()(4).getMethodName + "]: "
|
||||
}
|
||||
def debug(dbg: DebugType, x: Any): Unit = if(level <= dbg.level) print(debugPrefix(dbg) + x)
|
||||
|
||||
def debugf(dbg: DebugType, text: String, xs: Any*): Unit = if(level <= dbg.level) printf(debugPrefix(dbg) + text, xs: _*)
|
||||
private def debugPrefix(dbg: DebugType): String = {
|
||||
dbg.name + " [" + Thread.currentThread.getStackTrace()(5).getMethodName + "]: "
|
||||
}
|
||||
|
||||
// TODO: implement logging to file
|
||||
|
||||
def debug(dbg: DebugType, x: Any): Unit = if(dbg.level <= level) println(debugPrefix(dbg) + x)
|
||||
|
||||
// FIXME: should try catch this, printf is finicky
|
||||
// "When you are a responsible developer and create an error logging system for production"
|
||||
// *facepalm*
|
||||
// "and it crashes production..."
|
||||
def debugf(dbg: DebugType, text: String, xs: Any*): Unit = if(dbg.level <= level) printf(debugPrefix(dbg) + text + "\n", xs: _*)
|
||||
}
|
||||
|
||||
package object convertUtil {
|
||||
@@ -164,51 +175,141 @@ package object mapUtil {
|
||||
ListMap(map.toSeq.sortBy(_._1):_*).toVector(map.size - 1)
|
||||
}
|
||||
|
||||
sealed trait KeyPosition
|
||||
|
||||
object KeyPosition {
|
||||
object LEFT extends KeyPosition()
|
||||
object START extends KeyPosition()
|
||||
object AT extends KeyPosition()
|
||||
object BETWEEN extends KeyPosition()
|
||||
object END extends KeyPosition()
|
||||
object RIGHT extends KeyPosition()
|
||||
val values = Seq(LEFT, START, AT, BETWEEN, END, RIGHT)
|
||||
}
|
||||
|
||||
// this is just a binary search that returns the closest match
|
||||
private def getClosestKeysHelper[A, B](key: A, start: Int, end: Int, vector: Vector[(A, B)])(implicit ordering:Ordering[A]): ((A, B), Option[(A, B)]) = {
|
||||
// as this is private we dont check for sane input (start < end, vector.size >= 2)
|
||||
private def getIndexOrPrev[A, B](key: A, start: Int, end: Int, vector: Vector[(A, B)])(implicit ordering:Ordering[A]): (KeyPosition, Int) = {
|
||||
val mid = start + (end - start) / 2
|
||||
val foundKey = vector(mid)._1
|
||||
// dont check for equality but order
|
||||
if(ordering.compare(key, foundKey) == 0) return (vector(mid), Some(vector(mid + 1)))
|
||||
// dont check for equality but order, if key found, check if its the beginning
|
||||
if(ordering.compare(key, foundKey) == 0) if(mid == 0) return (KeyPosition.START, 0) else return (KeyPosition.AT, mid)
|
||||
// special case for 2 elements left
|
||||
if((end - start) == 1) {
|
||||
if(ordering.compare(key, vector(end)._1) >= 0) {
|
||||
(vector(end), None)
|
||||
if(mid == start) {
|
||||
if(ordering.compare(key, vector(end)._1) == 0) {
|
||||
// key is at the end
|
||||
// end == vector.size - 1 should not be required as we wouldnt be here otherwise
|
||||
(KeyPosition.END, end)
|
||||
} else if(ordering.compare(key, vector(end)._1) > 0) {
|
||||
// key beyond the end
|
||||
(KeyPosition.RIGHT, end)
|
||||
} else if(ordering.compare(key, vector(start)._1) > 0) {
|
||||
// key is between start and end
|
||||
(KeyPosition.BETWEEN, start)
|
||||
} else {
|
||||
// use getFirstElement to check if this is the beginning
|
||||
(vector(start), Some(vector(end)))
|
||||
}
|
||||
// key is before start
|
||||
// start == 0 should not be required as we wouldnt be here otherwise
|
||||
(KeyPosition.LEFT, start)
|
||||
} // at (start) is caught earlier due to mid == start
|
||||
} else {
|
||||
if (ordering.compare(key, foundKey) > 0) {
|
||||
getClosestKeysHelper(key, mid, end, vector)
|
||||
getIndexOrPrev(key, mid, end, vector)
|
||||
} else {
|
||||
getClosestKeysHelper(key, start, mid, vector)
|
||||
getIndexOrPrev(key, start, mid, vector)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getClosestKeys[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): ((A, B), Option[(A, B)]) = {
|
||||
def getClosestKeyAndNext[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): (Option[(A, B)], Option[(A, B)]) = {
|
||||
val vectorMap = ListMap(map.toSeq.sortBy(_._1):_*).toVector
|
||||
getClosestKeysHelper(key, 0, vectorMap.size - 1, vectorMap)
|
||||
if(vectorMap.size <= 1) if(vectorMap.size == 0) return (None, None) else return (Some(vectorMap(0)._1, vectorMap(0)._2), None)
|
||||
getIndexOrPrev(key, 0, vectorMap.size - 1, vectorMap) match {
|
||||
case (KeyPosition.LEFT, _) => (None, Some(vectorMap(0)))
|
||||
case (KeyPosition.START, _) =>(Some(vectorMap(0)), Some(vectorMap(1)))
|
||||
case (KeyPosition.AT, i) =>(Some(vectorMap(i)), Some(vectorMap(i + 1)))
|
||||
case (KeyPosition.BETWEEN, i) =>(Some(vectorMap(i)), Some(vectorMap(i + 1)))
|
||||
case (KeyPosition.END, i) => (Some(vectorMap(i)), None)
|
||||
case (KeyPosition.RIGHT, i) =>(Some(vectorMap(i)), None)
|
||||
}
|
||||
}
|
||||
|
||||
def getKeyOrNext[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): (A, B) = {
|
||||
if(map.contains(key)) return (key, map(key))
|
||||
def getPrevKeyAndClosest[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): (Option[(A, B)], Option[(A, B)]) = {
|
||||
val vectorMap = ListMap(map.toSeq.sortBy(_._1):_*).toVector
|
||||
val result = getClosestKeysHelper(key, 0, vectorMap.size - 1, vectorMap)
|
||||
if(ordering.compare(key, result._1._1)<= 0) {
|
||||
result._1
|
||||
if(vectorMap.size <= 1) if(vectorMap.size == 0) return (None, None) else return (None, Some(vectorMap(0)._1, vectorMap(0)._2))
|
||||
getIndexOrPrev(key, 0, vectorMap.size - 1, vectorMap) match {
|
||||
case (KeyPosition.LEFT, _) => (None, Some(vectorMap(0)))
|
||||
case (KeyPosition.START, _) =>(None, Some(vectorMap(0)))
|
||||
case (KeyPosition.AT, i) =>(Some(vectorMap(i - 1)), Some(vectorMap(i)))
|
||||
case (KeyPosition.BETWEEN, i) =>(Some(vectorMap(i)), Some(vectorMap(i + 1)))
|
||||
case (KeyPosition.END, i) => (Some(vectorMap(i - 1)), Some(vectorMap(i)))
|
||||
case (KeyPosition.RIGHT, i) =>(Some(vectorMap(i)), None)
|
||||
}
|
||||
}
|
||||
|
||||
class KeyFinder[A, B](map: Map[A, B])(implicit ordering:Ordering[A]) {
|
||||
if(map.size == 0) throw new Exception("Empty Map")
|
||||
val vectorMap = ListMap(map.toSeq.sortBy(_._1):_*).toVector // FIXME: seems to hang for size > 10k (tested only in IDE)
|
||||
var iterator = vectorMap.iterator
|
||||
var currentKV = if(iterator.hasNext) Some(iterator.next()) else None
|
||||
var nextKV = if(iterator.hasNext) Some(iterator.next()) else None
|
||||
val firstElement = currentKV
|
||||
|
||||
private def reset(): Unit = {
|
||||
iterator = vectorMap.iterator
|
||||
currentKV = if(iterator.hasNext) Some(iterator.next()) else None
|
||||
nextKV = if(iterator.hasNext) Some(iterator.next()) else None
|
||||
}
|
||||
|
||||
private def shift(): Unit = {
|
||||
currentKV = nextKV
|
||||
nextKV = if(iterator.hasNext) Some(iterator.next()) else None
|
||||
}
|
||||
|
||||
// returns value of key in map if it exists or the previous one. If key is smaller then the key of the first
|
||||
// element, the first value is returned. This functions is optimized for random key calls.
|
||||
// For sequentially incrementing key calls use getValueOrPrevIterate.
|
||||
def getValueOrPrev(key: A): B = {
|
||||
// quick simple checks first
|
||||
if(map.contains(key)) return map(key)
|
||||
if(currentKV.isEmpty) throw new Exception("Empty Map")// only happens when size == 0
|
||||
if(vectorMap.size == 1) return vectorMap(0)._2
|
||||
|
||||
val result = getIndexOrPrev(key, 0, vectorMap.size - 1, vectorMap)
|
||||
vectorMap(result._2)._2
|
||||
}
|
||||
|
||||
// returns value of key in map if it exists or the previous one. If key is smaller then the key of the first
|
||||
// element, the first value is returned. This functions is optimized for sequentially incrementing key calls.
|
||||
// For random access calls use getValueOrPrev.
|
||||
// Note: probably need rather large map to make this valuable vs getValueOrPrev
|
||||
def getValueOrPrevIterate(key: A): B = {
|
||||
// quick simple checks first
|
||||
if(map.contains(key)) return map(key)
|
||||
if(currentKV.isEmpty) throw new Exception("Empty Map")// only happens when size == 0
|
||||
if(vectorMap.size == 1) return vectorMap(0)._2
|
||||
|
||||
val currentKey = currentKV.get._1
|
||||
if(ordering.compare(key, currentKey) >= 0) {
|
||||
// TODO: find out if scala actually does not check the second condition if first one fails and always in that order
|
||||
while(!nextKV.isEmpty && ordering.compare(key, nextKV.get._1) >= 0) {
|
||||
// when trying to do this with recursion (shift than call this function again) resulted in a non reproducible stack overflow
|
||||
shift()
|
||||
}
|
||||
currentKV.get._2
|
||||
} else {
|
||||
result._2.getOrElse(result._1)
|
||||
// currentKV.isEmpty catches size == 0, vectormap has at least 1 element here
|
||||
if(ordering.compare(key, vectorMap(0)._1) < 0) {
|
||||
vectorMap(0)._2
|
||||
} else {
|
||||
// out of order call to function, have to reset
|
||||
scim.lib.debugUtil.debugf(debugUtil.DebugType.DEBUG, "Function called out of order, Key: %s is greater then current Key: %s", key.toString, currentKey.toString)
|
||||
reset()
|
||||
getValueOrPrevIterate(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getKeyOrPrev[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): (A, B) = {
|
||||
if(map.contains(key)) return (key, map(key))
|
||||
val vectorMap = ListMap(map.toSeq.sortBy(_._1):_*).toVector
|
||||
val result = getClosestKeysHelper(key, 0, vectorMap.size - 1, vectorMap)
|
||||
result._1
|
||||
}
|
||||
}
|
||||
|
||||
package object yml {
|
||||
|
||||
Reference in New Issue
Block a user