All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unibo.scafi.space.SpatialAbstraction.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016-2019, Roberto Casadei, Mirko Viroli, and contributors.
 * See the LICENSE file distributed with this work for additional information regarding copyright ownership.
*/

package it.unibo.scafi.space

import it.unibo.scafi.space.SpatialAbstraction.Bound
import it.unibo.scafi.space.optimization.nn.NNIndex
import it.unibo.utils.BiMap

import scala.collection.concurrent.TrieMap
import scala.language.higherKinds


/**
  * Component which represents a spatial abstraction
  */

trait SpatialAbstraction {
  type P // Type for "position"

  type SPACE[E] <: Space[E] // Type for "spatial container" of elements E

  trait NeighbouringRelation {
    def neighbouring(p1: P, p2: P): Boolean
  }

  def buildNewSpace[E](elems: Iterable[(E,P)]): SPACE[E]

  trait Space[E] extends NeighbouringRelation {
    def contains(e: E): Boolean
    def getLocation(e: E): P
    def getAt(p: P): Option[E]
    def getAll(): Iterable[E]
    def getNeighbors(e: E): Iterable[E] = {
      val p1 = getLocation(e)
      getAll().filter(e2 => neighbouring(p1, getLocation(e2)))
    }
  }

  trait MutableSpace[E] extends Space[E] {
    def add(e: E, p: P): Unit
    def remove(e: E): Unit
    def setLocation(e: E, p: P): Unit
  }
}

object SpatialAbstraction {
  case class Bound(inclusive: Shape, exclusive: List[(Shape,Point3D)] = List.empty) {
    import optimization._
    def accept(p: Point3D) : Boolean = {
      inclusive.contains(p) && exclusive.forall(x => !x._1.contains(p - x._2))
    }
  }
}
trait AdHocSpatialAbstraction extends SpatialAbstraction {
  type P

  override type SPACE[E] <: AdHocSpace[E]

  abstract class AdHocSpace[E](nbrs: Map[E,Set[E]]) extends MutableSpace[E] {
    def elementToPosition(e: E): P

    private val elems = scala.collection.mutable.Map[E,Set[E]](nbrs.toSeq :_ *)
    private val positions = BiMap[E,P](nbrs.keys.map(e => (e -> elementToPosition(e))).toSeq :_ *)

    override def add(e: E, p: P): Unit = positions += (e->p)

    override def setLocation(e: E, p: P): Unit = positions += (e->p)

    override def remove(e: E): Unit = positions -= e

    override def getLocation(e: E): P = positions(e)

    override def getAll(): Iterable[E] = positions.keys

    override def getAt(p: P): Option[E] = positions.getByValue(p)

    override def neighbouring(p1: P, p2: P): Boolean = {
      (getAt(p1), getAt(p2)) match {
        case (Some(e1), Some(e2)) => elems(e1).contains(e2)
        case _ => false
      }
    }

    override def contains(e: E): Boolean = positions.contains(e)

    // Override for performance reasons (the base impl depends on neighbouring())
    override def getNeighbors(e: E): Iterable[E] = elems.getOrElse(e,Set())
  }
}

trait BasicAdHocSpatialAbstraction extends AdHocSpatialAbstraction {
  type P = Int

  override type SPACE[E] = BasicAdHocSpace[E]

  class BasicAdHocSpace[E](nbrs: Map[E,Set[E]]) extends AdHocSpace[E](nbrs) {
    override def elementToPosition(e: E): Int = e.hashCode()
  }
}

trait MetricSpatialAbstraction extends SpatialAbstraction {
  override type SPACE[E] <: Space[E] with DistanceStrategy

  type D // Type for "distance"

  implicit val positionOrdering: Ordering[P]

  trait DistanceStrategy {
    def getDistance(p1: P, p2: P): D
  }

  trait MetricSpace[E] extends Space[E] with DistanceStrategy {
    def getNeighborsWithDistance(e: E) : Iterable[(E,D)] =
      getNeighbors(e) map (elem =>
        (elem, getDistance(getLocation(elem), getLocation(e))))
  }

  trait MutableMetricSpace[E] extends MetricSpace[E] with MutableSpace[E]
}

trait BasicSpatialAbstraction extends MetricSpatialAbstraction {
  override type P <: Point3D
  override type D = Double

  override type SPACE[E] = Space3D[E]

  implicit val positionOrdering: Ordering[P] = new Ordering[P] {
    override def compare(a: P, b: P): Int = {
      if (a.z > b.z){ +1 }
      else if (a.z < b.z){ -1 }
      else if (a.y > b.y){ +1 }
      else if (a.y < b.y){ -1 }
      else if (a.x > b.x){ +1 }
      else if (a.x < b.x){ -1 }
      else { 0 }
    }
  }

  override def buildNewSpace[E](elems: Iterable[(E,P)]): SPACE[E] =
    new Basic3DSpace(elems.toMap)

  abstract class Space3D[E](var elemPositions: Map[E,P],
                    override val proximityThreshold: Double) extends MutableMetricSpace[E]
    with EuclideanStrategy
    with Serializable {
    def add(e: E, p: P): Unit = elemPositions += (e -> p)
    def getLocation(e: E): P = elemPositions(e)
    def getAll(): Iterable[E] = elemPositions.keys
    def remove(e: E): Unit = elemPositions -= e

    override def contains(e: E): Boolean = elemPositions.contains(e)
  }
  class Basic3DSpace[E](pos: Map[E,P],
                        proximityThreshold: Double = EuclideanStrategy.DefaultProximityThreshold)
    extends Space3D[E](pos,proximityThreshold){

    var neighbourhoodMap: Map[E, Set[(E,D)]] = initNeighbours()

    def initNeighbours(): Map[E, Set[(E,D)]] = {
      var result = Map[E, Set[(E,D)]]()
      elemPositions.foreach(elem => { result += (elem._1 -> this.calculateNeighbours(elem._1).toSet) })
      result
    }

    def setLocation(e: E, p: P): Unit = {
      add(e, p)
      var newNeighbours: Set[(E,D)] = this.calculateNeighbours(e).toSet
      var oldNeighbours: Set[(E,D)] = neighbourhoodMap(e)
      if(oldNeighbours != newNeighbours){
        var noMoreNeighbours: Set[(E,D)] = oldNeighbours.filter(el => !newNeighbours.exists(newNbr => newNbr._1==el._1))
        var brandNewNeighbours: Set[(E,D)] = newNeighbours.filter(el => !oldNeighbours.exists(old => old._1==el))
        for (elem <- noMoreNeighbours) {
          neighbourhoodMap += (elem._1 -> this.calculateNeighbours(elem._1).toSet)
        }
        for (elem <- brandNewNeighbours) {
          neighbourhoodMap += (elem._1 -> this.calculateNeighbours(elem._1).toSet)
        }
        neighbourhoodMap += (e -> newNeighbours)
      }
    }

    private def calculateNeighbours(e: E): Iterable[(E,D)] = {

      val p1 = getLocation(e)
      getAll()
        .filter(nbr => nbr != e && neighbouring(p1,getLocation(nbr)))
       .map(e2 => (e2, getDistance(getLocation(e), getLocation(e2))))
    }

    override def add(e: E, p: P): Unit = {
      elemPositions += (e -> p)
      if (!neighbourhoodMap.contains(e)) neighbourhoodMap += (e -> Set())
    }
    override def getAt(p: P): Option[E] = elemPositions.find(_._2 == p).map(_._1)

    override def getNeighbors(e: E): Iterable[E] = getNeighborsWithDistance(e) map (_._1)
    override def getNeighborsWithDistance(e: E): Iterable[(E, D)] = neighbourhoodMap(e)
  }

  trait EuclideanStrategy extends DistanceStrategy
    with NeighbouringRelation with Serializable {
    val proximityThreshold: Double
    def neighbouring(p1: P, p2: P): Boolean =
      getDistance(p1,p2) <= proximityThreshold

    override def getDistance(p1: P, p2: P): Double = {
      p1.distance(p2)
    }
  }

  object EuclideanStrategy {
    val DefaultProximityThreshold: Double = 1
  }

  /**
    * a space that used quad tree index to compute neighbour
    * @param pos the position of noe
    * @param radius radius of neighbour range
    * @tparam E the type of node
    */
  class QuadTreeSpace[E](pos : Map[E,P], radius : Double, bound : Option[Bound] = None) extends Space3D[E](pos,radius) {
    private val nMap: TrieMap[E, Set[E]] = TrieMap.empty
    //TODO CREATE AN INDEX THAT INCREASE HIS SIZE WITH NODE POSITIONING
    private val neighbourIndex: NNIndex[E] = NNIndex(pos)
    override def setLocation(e: E, p: P): Unit = if(bound.isEmpty || bound.exists(_.accept(p))) {
      resetNeighbours(e)
      neighbourIndex -= (elemPositions(e))
      neighbourIndex += (p -> e)
      elemPositions += e -> p
      calculateNeighbours(e)
      addNeighbours(e)
    }

    private def resetNeighbours(e: E): Unit = {nMap.get(e).last.foreach {x => { nMap += x -> (nMap(x) - e) }}}

    private def addNeighbours(e: E): Unit = {nMap.get(e).last.foreach {x => { nMap += x -> (nMap(x) + e) }}}
    private def calculateNeighbours(e: E): Unit = {
      val neigh: (E, Set[E]) = e -> (neighbourIndex.neighbours(elemPositions(e), radius).map {
        _._2
      }.toSet)
      this.nMap += neigh
    }


    override def add(e: E, p: P): Unit = {
      neighbourIndex += (p -> e)
      super.add(e,p)
    }
    override def remove(e: E): Unit = {
      neighbourIndex -= (elemPositions(e))
      super.remove(e)
    }
    //TODO CHECK IF TWO ELEMENTS ARE IN THE SAME POSITION
    override def getNeighbors(e: E): Iterable[E] = {
      if(!nMap.contains(e)) {
        calculateNeighbours(e)
      }
      if(nMap.contains(e)) {
        nMap(e)
      } else {
        List()
      }
    }
    override def getNeighborsWithDistance(e: E): Iterable[(E, D)] = {
      val p = elemPositions(e)
      getNeighbors(e) map {x => x -> elemPositions(x).distance(p)}
    }

    override def getAt(p: P): Option[E] = neighbourIndex.get(p)

    override def contains(e: E): Boolean = elemPositions.contains(e)

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy