[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.datastruct.{Behaviour, BehaviourVector, Cell, Configuration, FunctionArgs, Group, SensorCount, SharedMemory, SimulationBehaviours, SimulationConfig, Time, Truth}
|
||||||
import scim.lib.ConcurrentBroadcast.BroadcastObject
|
import scim.lib.ConcurrentBroadcast.BroadcastObject
|
||||||
import scim.lib.debugUtil.{DebugType, debugf}
|
import scim.lib.debugUtil.{DebugType, debugf}
|
||||||
import scim.lib.mapUtil.traversableToMap
|
import scim.lib.mapUtil._
|
||||||
import scim.lib.simulation.SimulationBehaviour
|
import scim.lib.simulation.SimulationBehaviour
|
||||||
import scim.lib.yml
|
import scim.lib.yml
|
||||||
|
|
||||||
@@ -47,6 +47,9 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
behaviours.foreach(behaviour => parseBehaviour(behaviour))
|
behaviours.foreach(behaviour => parseBehaviour(behaviour))
|
||||||
groundTruths.foreach(groundTruth => parseGroundTruth(groundTruth))
|
groundTruths.foreach(groundTruth => parseGroundTruth(groundTruth))
|
||||||
// TODO: insert dummy sensors if none exists at cells of the parent group where this group exists
|
// 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
|
Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,16 +81,19 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def parseCells(cells: Vector[Any]): Map[Cell, SensorCount] = {
|
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]]
|
case cell: Vector[_] => val cellVector = cell.asInstanceOf[Vector[Int]]
|
||||||
(Cell(cellVector(0), cellVector(1)), SensorCount(cellVector(2)))
|
(Cell(cellVector(0), cellVector(1)), SensorCount(cellVector(2)))
|
||||||
case cell: Map[_, _] => val cellMap = cell.asInstanceOf[Map[String, Int]]
|
case cell: Map[_, _] => val cellMap = cell.asInstanceOf[Map[String, Int]]
|
||||||
(Cell(cellMap("x"), cellMap("y")), SensorCount(cellMap("count")))
|
(Cell(cellMap("x"), cellMap("y")), SensorCount(cellMap("count")))
|
||||||
case _ => throw new Exception("Invalid Cell: " + cell.toString)
|
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)
|
traversableToMap[Cell, SensorCount](cells, parseCell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: multiply time by tickfactor
|
||||||
private def parseTimeLineElement(timeLineElement: Any): (Int, Option[Int]) = {
|
private def parseTimeLineElement(timeLineElement: Any): (Int, Option[Int]) = {
|
||||||
timeLineElement match {
|
timeLineElement match {
|
||||||
case timeLineElement: Vector[_] => val elementVector = timeLineElement.asInstanceOf[Vector[Int]]
|
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
|
// 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
|
// 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
|
// 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 ={
|
def simCell(input: (Cell, Map[Group, List[(Group, Sensor, Option[GroundTruth])]])): Unit ={
|
||||||
val cell = input._1
|
val cell = input._1
|
||||||
val groups = input._2
|
val groups = input._2
|
||||||
|
|||||||
@@ -59,9 +59,9 @@ package object evalUtil {
|
|||||||
|
|
||||||
// FIXME: obviously need to assign something and not null
|
// FIXME: obviously need to assign something and not null
|
||||||
def memSize(obj: Any): Long = {
|
def memSize(obj: Any): Long = {
|
||||||
// import java.lang.instrument.Instrumentation
|
// import java.lang.instrument.Instrumentation
|
||||||
// val instrumentation: Instrumentation = null
|
// val instrumentation: Instrumentation = null
|
||||||
// instrumentation.getObjectSize(obj)
|
// instrumentation.getObjectSize(obj)
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,21 +70,32 @@ package object debugUtil {
|
|||||||
sealed case class DebugType(name: String, level: Int)
|
sealed case class DebugType(name: String, level: Int)
|
||||||
|
|
||||||
object DebugType {
|
object DebugType {
|
||||||
object ERROR extends DebugType("ERROR", 0)
|
// Comments based on linux kernel debugging (https://elinux.org/Debugging_by_printing)
|
||||||
object WARNING extends DebugType("WARNING", 1)
|
object ERROR extends DebugType("ERROR", 0) // Error occurred, simulation crashed
|
||||||
object MESSAGE extends DebugType("MESSAGE", 2)
|
object WARNING extends DebugType("WARNING", 1) // A warning, meaning nothing serious by itself but might indicate problems
|
||||||
val values = Seq(ERROR, WARNING, MESSAGE)
|
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 currentMethodName: String = Thread.currentThread.getStackTrace()(2).getMethodName
|
||||||
def currentMethodCallerName: String = Thread.currentThread.getStackTrace()(3).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 {
|
package object convertUtil {
|
||||||
@@ -164,51 +175,141 @@ package object mapUtil {
|
|||||||
ListMap(map.toSeq.sortBy(_._1):_*).toVector(map.size - 1)
|
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
|
// 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 mid = start + (end - start) / 2
|
||||||
val foundKey = vector(mid)._1
|
val foundKey = vector(mid)._1
|
||||||
// dont check for equality but order
|
// dont check for equality but order, if key found, check if its the beginning
|
||||||
if(ordering.compare(key, foundKey) == 0) return (vector(mid), Some(vector(mid + 1)))
|
if(ordering.compare(key, foundKey) == 0) if(mid == 0) return (KeyPosition.START, 0) else return (KeyPosition.AT, mid)
|
||||||
// special case for 2 elements left
|
// special case for 2 elements left
|
||||||
if((end - start) == 1) {
|
if(mid == start) {
|
||||||
if(ordering.compare(key, vector(end)._1) >= 0) {
|
if(ordering.compare(key, vector(end)._1) == 0) {
|
||||||
(vector(end), None)
|
// 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 {
|
} else {
|
||||||
// use getFirstElement to check if this is the beginning
|
// key is before start
|
||||||
(vector(start), Some(vector(end)))
|
// 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 {
|
} else {
|
||||||
if (ordering.compare(key, foundKey) > 0) {
|
if (ordering.compare(key, foundKey) > 0) {
|
||||||
getClosestKeysHelper(key, mid, end, vector)
|
getIndexOrPrev(key, mid, end, vector)
|
||||||
} else {
|
} 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
|
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)))
|
||||||
def getKeyOrNext[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): (A, B) = {
|
case (KeyPosition.START, _) =>(Some(vectorMap(0)), Some(vectorMap(1)))
|
||||||
if(map.contains(key)) return (key, map(key))
|
case (KeyPosition.AT, i) =>(Some(vectorMap(i)), Some(vectorMap(i + 1)))
|
||||||
val vectorMap = ListMap(map.toSeq.sortBy(_._1):_*).toVector
|
case (KeyPosition.BETWEEN, i) =>(Some(vectorMap(i)), Some(vectorMap(i + 1)))
|
||||||
val result = getClosestKeysHelper(key, 0, vectorMap.size - 1, vectorMap)
|
case (KeyPosition.END, i) => (Some(vectorMap(i)), None)
|
||||||
if(ordering.compare(key, result._1._1)<= 0) {
|
case (KeyPosition.RIGHT, i) =>(Some(vectorMap(i)), None)
|
||||||
result._1
|
|
||||||
} else {
|
|
||||||
result._2.getOrElse(result._1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getKeyOrPrev[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): (A, B) = {
|
def getPrevKeyAndClosest[A, B](key: A, map: Map[A, B])(implicit ordering:Ordering[A]): (Option[(A, B)], Option[(A, B)]) = {
|
||||||
if(map.contains(key)) return (key, map(key))
|
|
||||||
val vectorMap = ListMap(map.toSeq.sortBy(_._1):_*).toVector
|
val vectorMap = ListMap(map.toSeq.sortBy(_._1):_*).toVector
|
||||||
val result = getClosestKeysHelper(key, 0, vectorMap.size - 1, vectorMap)
|
if(vectorMap.size <= 1) if(vectorMap.size == 0) return (None, None) else return (None, Some(vectorMap(0)._1, vectorMap(0)._2))
|
||||||
result._1
|
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 {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package object yml {
|
package object yml {
|
||||||
|
|||||||
Reference in New Issue
Block a user