You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

506 lines
14 KiB

package sim
import model.HHCTypes
import scala.reflect.ClassTag
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import scala.collection.immutable.ArraySeq
import model.HHCEdge2
import util.Util
import model.Customer
import scala.io.BufferedSource
import model.Coord
import scala.util.Random
import com.typesafe.scalalogging.LazyLogging
import cats.implicits
import scala.util.chaining._
class HHCSim2(
epsilonMax: Int = 0,
iterations: Int = 0,
WLDMax: Float = 0,
customers: String Either ArraySeq[Customer]
) extends LazyLogging {
import HHCSim2._
import Ordering.Double.IeeeOrdering
private var adjMatrix: Option[GraphMatrix[Double]] = None
def go() = {
customers
.flatMap(checkEmpty)
.map(formAdjMatrix)
.tap(logGraph)
.tap(_.foreach(e => adjMatrix = Some(e)))
.map(e => mstUsingPrims(e))
.tap(logMST)
.map { case (mst, epsilon) => removeEdges(mst)(epsilon) }
.tap(logMST2)
.map { case (mstUpdated, removed) => findClusters(mstUpdated)(removed) }
.map {
case (mstUpdated, clusters, edgeMappings, removedEdges) =>
printClusters(clusters)
val e =
groupClusters(mstUpdated, clusters, edgeMappings, removedEdges)
printGroups(e._5)
e
}
.flatMap(value => {
val (mst, clusters, edgeMappings, removed, clusterGroups) = value
balanceWorkload(clusters, clusterGroups)
})
}
// def go2() = {
// val res = go().flatMap(value => {
// val (mst, clusters, edgeMappings, removed, clusterGroups) = value
// balanceWorkload(mst, clusters, clusterGroups)
// })
// res
// }
def printGraph(graph: GraphMatrix[Double]) = graph.foreach { e =>
e.foreach { d =>
print(f"$d%.2f, ")
}
println
}
def printGraph(graph: Graph[Double]) = {
for (i <- 0 until graph.size) {
print(s"$i: ")
for (edge <- graph(i)) {
print(f"(${edge.toNode}, ${edge.weight}%.2f), ")
}
println
}
}
def logGraph(maybeGraph: Either[String, GraphMatrix[Double]]): Unit =
maybeGraph.foreach(
graph => {
logger.whenDebugEnabled {
logger.debug("Graph: ")
printGraph(graph)
}
}
)
def logMST(maybeMST: Either[String, (Graph[Double], Double)]): Unit =
maybeMST.foreach(
t => {
logger.whenDebugEnabled {
logger.debug(s"Epsilon = ${t._2}")
logger.debug("MST: ")
printGraph(graph = t._1)
}
}
)
def logMST2(it: Either[String, (Graph[Double], RemovedEdges[Double])]): Unit =
it.foreach(
t => {
logger.whenDebugEnabled {
logger.debug(s"Removed Edges = ${t._2}")
logger.debug("MST: ")
printGraph(graph = t._1)
}
}
)
def printClusters(clusters: Clusters) = {
logger.whenDebugEnabled {
logger.debug("Clusters:")
var i = 0
clusters.foreach(e => {
print(s"Cluster-$i: ")
i += 1
e.foreach(f => print(s"$f "))
println
})
}
}
def printClusters(clusters: Array[List[Int]]) = {
logger.whenDebugEnabled {
logger.debug("Clusters:")
var i = 0
clusters.foreach(e => {
print(s"Cluster-$i: ")
i += 1
e.foreach(f => print(s"$f "))
println
})
}
}
def printGroups(groups: ArraySeq[List[(Int, Double)]]) = {
logger.whenDebugEnabled {
logger.debug("Neighbours: ")
var i = 0
groups.foreach(e => {
print(s"Cluster-$i: ")
i += 1
e.foreach(f => { print(f"(${f._1}%d, ${f._2}%.2f), ") })
println
})
}
}
def balanceWorkload[N: Numeric](
clusters: Clusters,
groups: ArraySeq[List[(Int, N)]]
) = {
customers.map(c => {
val mutClusters =
clusters.toArray
val clusterOrder = (0 until clusters.length).toArray
logger.debug(s"Initial order: $clusterOrder")
val k = mutClusters.size
for (_ <- 0 until iterations) {
val WL = mutClusters.view
.map(nodes => {
val customers = nodes.map(node => c(node))
val wl = customers.foldLeft(0f)(_ + _.workload)
wl
})
.to(ArraySeq)
logger.debug(s"$WL")
clusterOrder.sortInPlaceWith((x, y) => WL(x) < WL(y))
logger.debug(s"Current order $clusterOrder")
val (minWL, maxWL) = WL.foldLeft((99999f, 0f))((acc, wl) => {
val (currMin, currMax) = acc
if (wl < currMin) (wl, currMax)
else if (wl > currMax) (currMin, wl)
else acc
})
val WLD = maxWL - minWL
logger.debug(s"maxWL = $maxWL, minWL = $minWL")
logger.debug(s"WLD = $WLD")
if (WLD > WLDMax) {
val randNum = Random.between(((k / 2) + 1), k)
logger.debug(s"Rand Num=$randNum")
val effectiveNum = clusterOrder(randNum)
logger.debug(s"Chosen cluster = $effectiveNum")
val (s, _) = groups(effectiveNum)(0)
logger.debug(s"Nearest neighbour = $s")
// val avg = averagePairwiseDistance(mst(s))
val avg = adjMatrix
.map(m => averagePairwiseDistance(m)(mutClusters(s)))
.getOrElse(9999d)
// if (avg < epsilonMax) true else false
logger.debug(s"Pairwise average = $avg")
if (avg < epsilonMax) {
logger.debug("Average less than epsilonMax. Updating Clusters.")
if (!mutClusters(s).contains(effectiveNum)) {
mutClusters(s) = effectiveNum :: mutClusters(s)
}
// mutClusters.foreach(println)
printClusters(mutClusters)
}
}
}
ArraySeq.unsafeWrapArray(mutClusters)
})
}
def averagePairwiseDistance(graph: GraphMatrix[Double])(
cluster: List[Int]
) = {
val (sum, size) = cluster
.combinations(2)
.foldLeft((0d, 0))((acc, pair) => {
val (sum, size) = acc
val (i, j) = pair(0) -> pair(1)
logger.trace(s"$i $j - ")
logger.trace(s"weight = ${graph(i)(j)}")
(sum + graph(i)(j), size + 1)
})
val n = if (size == 0) 1 else size
sum / n
}
}
object HHCSim2 extends HHCTypes {
import Ordering.Implicits._
val composed = (formAdjMatrix _)
.andThen(e => mstUsingPrims(e))
.andThen { case (mst, epsilon) => removeEdges(mst)(epsilon) }
.andThen { case (mstUpdated, removed) => findClusters(mstUpdated)(removed) }
def getCustomers(
infile: BufferedSource
): String Either ArraySeq[Customer] = {
var customers: String Either ArraySeq[Customer] = Right(ArraySeq.empty)
val it = infile.getLines
@annotation.tailrec
def loop(
lst: ListBuffer[Customer],
iter: Iterator[String]
): String Either ListBuffer[Customer] = {
if (!iter.hasNext) Right(lst)
else {
val line = iter.next
val arr = line.split(",").map(_.trim)
arr match {
case Array(latitude, longitude, workLoad) => {
val cust =
for {
lat <- latitude.toDoubleOption
lon <- longitude.toDoubleOption
coord = Coord(lat, lon)
wl <- workLoad.toFloatOption
} yield (Customer(coord, wl))
cust match {
case Some(c) => loop(lst += c, iter)
case None => Left(s"Error reading customers at line - $line")
}
}
case _ => {
if (line.equals(" ") || line.contains("\n"))
Left("Error newline")
else {
Left(
"Error reading customers from" +
s" file at line - $line}"
)
}
}
}
}
}
try {
customers = loop(ListBuffer.empty, it).map(_.to(ArraySeq))
} catch {
case e: NumberFormatException =>
customers = Left(
s"Expected number but received string ${e.getMessage()}"
)
case e: NullPointerException =>
customers = Left("Input was null")
}
customers
}
def checkEmpty(
customers: IndexedSeq[Customer]
): Either[String, IndexedSeq[Customer]] =
customers.length match {
case 0 => Left("Error input was empty")
case _ => Right(customers)
}
def formAdjMatrix(customers: IndexedSeq[Customer]): GraphMatrix[Double] = {
val n = customers.length
val edges: Array[Array[Double]] = Array.ofDim(n, n)
for (i <- 0 until n) {
for (j <- i until n) {
val weight =
Util.getHaversineDistance(
customers(i).location,
customers(j).location
)
edges(i)(j) = weight
edges(j)(i) = weight
}
}
edges
}
def mstUsingPrims[T: Numeric](
edges: GraphMatrix[T]
): (Graph[T], T) = {
val num = implicitly[Numeric[T]]
val n = edges.length
val selected: Array[Boolean] = Array.fill(n)(false)
selected(0) = true
val adjList =
Array.fill(n)(ListBuffer.empty[HHCEdge2[T]])
var sum = 0
var count = 0
for (_ <- 0 until n - 1) {
var min = 999999
var x = 0
var y = 0
for (i <- 0 until n) {
if (selected(i) == true) {
for (j <- 0 until n) {
if (selected(j) == false && edges(i)(j) != 0) {
if (min > num.toInt(edges(i)(j))) {
min = num.toInt(edges(i)(j))
x = i
y = j
}
}
}
}
}
sum += num.toInt(edges(x)(y))
adjList(x) += HHCEdge2(x, y, edges(x)(y))
adjList(y) += HHCEdge2(y, x, edges(x)(y))
selected(y) = true
}
val adjList2 = adjList.map(l => {
count += l.size
l.toList
})
// adjList2.foreach(println)
(ArraySeq.unsafeWrapArray(adjList2), num.fromInt(sum / (count / 2)))
}
def removeEdges[N: Ordering](
mst: Graph[N]
)(epsilon: N): (Graph[N], RemovedEdges[N]) = {
val removed = ListBuffer.empty[HHCEdge2[N]]
val result = ArraySeq.tabulate(mst.length) { i =>
val (filtered, rm) = mst(i)
// .view
// .filter(e => (e.fromNode <= e.toNode))
.partition(_.weight <= epsilon)
// val rm2 = rm.filter(e => (e.fromNode <= e.toNode))
removed ++= rm
filtered
}
println
// result.foreach(println)
// println
(result, removed.toList)
}
def DFS[T: Numeric](start: Int)(graph: Graph[T]) = {
val visited = Array.fill(graph.size)(false)
val buf = ListBuffer[Int]()
def loop(
start: Int,
graph: Graph[T],
visited: Array[Boolean]
): Unit = {
visited(start) = true
buf += start
// val iter = graph(start).iterator
// while (iter.hasNext) {
// val edge = iter.next
// if (!visited(edge.toNode))
// loop(edge.toNode, graph, visited)
// }
for (edge <- graph(start)) {
if (!visited(edge.toNode))
loop(edge.toNode, graph, visited)
}
}
loop(start, graph, visited)
buf.toList
}
def findClusters[T: Numeric](mstUpdated: Graph[T])(
removedEdges: RemovedEdges[T]
): (Graph[T], Clusters, ArraySeq[List[HHCEdge2[T]]], RemovedEdges[T]) = {
val visited = Array.fill[Boolean](mstUpdated.length)(false)
var removedEdges2 = removedEdges
val egdeMappings =
Array.fill(mstUpdated.length)(List.empty[HHCEdge2[T]])
val nodeToClusterMappings = Array.fill(mstUpdated.length)(0)
val result = ArraySeq.tabulate(mstUpdated.length) { i =>
{
val buf = ListBuffer[Int]()
visited(i) match {
case true =>
case false if (!mstUpdated(i).isEmpty) => {
val nodes = DFS(i)(mstUpdated)
buf ++= nodes
val (nds, rms) = assignEdges(nodes, removedEdges2)
removedEdges2 = rms
egdeMappings(i) = nds
for (j <- nodes) visited(j) = true
}
case false => {
buf += i
val (nds, rms) = assignEdges(List(i), removedEdges2)
egdeMappings(i) = nds
removedEdges2 = rms
}
}
buf.toList
}
}
// println(s"Removed edges size: ${removedEdges2.size}")
(
mstUpdated,
result.filterNot(_.isEmpty),
ArraySeq.unsafeWrapArray(egdeMappings.filterNot(_.isEmpty)),
removedEdges
)
}
def assignEdges[T: Ordering](
nodes: List[Int],
removedEdges: RemovedEdges[T]
) = {
val it = nodes.iterator
@annotation.tailrec
def loop(
nodes: List[Int],
removedEdges: RemovedEdges[T],
iter: Iterator[Int],
edges: List[HHCEdge2[T]]
): (List[HHCEdge2[T]], RemovedEdges[T]) = {
if (!iter.hasNext) (edges, removedEdges)
else if (!edges.isEmpty) (edges, removedEdges)
else {
val node = iter.next
val (filt, rm) =
removedEdges.partition(e => e.fromNode == node)
loop(nodes, rm, iter, filt)
}
}
val (edges, removedEdgesUpdated) =
loop(nodes, removedEdges, it, List.empty[HHCEdge2[T]])
(edges, removedEdgesUpdated)
}
def mapNodestoClusters[N: Ordering](mst: Graph[N], clusters: Clusters) = {
val arr = Array.fill(mst.size)(0)
for (i <- 0 until clusters.size) {
val nodes = clusters(i)
for (node <- nodes) {
arr(node) = i
}
}
ArraySeq.unsafeWrapArray(arr)
}
def groupClusters[N: Ordering](
mst: Graph[N],
clusters: Clusters,
edgeMappings: ArraySeq[List[HHCEdge2[N]]],
removed: RemovedEdges[N]
) = {
val nodeMap = mapNodestoClusters(mst, clusters)
val groups = ArraySeq.tabulate(edgeMappings.size)(i => {
val buf = ListBuffer.empty[(Int, N)]
val lst = edgeMappings(i)
lst.foreach(e => {
buf += nodeMap(e.toNode) -> e.weight
})
buf.sortWith {
case ((_, weight1), (_, weight2)) =>
weight1 < weight2
}.toList
})
(mst, clusters, edgeMappings, removed, groups)
}
}