Rohan Sircar
4 years ago
6 changed files with 649 additions and 35 deletions
-
24src/main/scala/HHCSim.scala
-
118src/main/scala/Main.scala
-
2src/main/scala/model/HHCEdge.scala
-
29src/main/scala/model/Types.scala
-
395src/main/scala/sim/HHCSim2.scala
-
116src/main/scala/util/Util.scala
@ -0,0 +1,29 @@ |
|||||
|
package model |
||||
|
|
||||
|
import scala.collection.immutable.ArraySeq |
||||
|
import scala.collection.IndexedSeqView |
||||
|
|
||||
|
trait HHCTypes { |
||||
|
|
||||
|
/** |
||||
|
* Graph in mutable adjacency matrix form |
||||
|
*/ |
||||
|
type GraphMatrix[T] = Array[Array[T]] |
||||
|
|
||||
|
/** |
||||
|
* Graph in mutable adjacency list form |
||||
|
*/ |
||||
|
type MutGraph[T] = Array[Array[HHCEdge2[T]]] |
||||
|
|
||||
|
/** |
||||
|
* Graph in immutable adjacency list form |
||||
|
*/ |
||||
|
type Graph[T] = ArraySeq[List[HHCEdge2[T]]] |
||||
|
|
||||
|
/** |
||||
|
* List of removed edges |
||||
|
*/ |
||||
|
type RemovedEdges[T] = List[HHCEdge2[T]] |
||||
|
|
||||
|
type Clusters = ArraySeq[List[Int]] |
||||
|
} |
@ -0,0 +1,395 @@ |
|||||
|
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 |
||||
|
|
||||
|
class HHCSim2( |
||||
|
private val epsilonMax: Int = 0, |
||||
|
private val iterations: Int = 0, |
||||
|
private val WLDMax: Float = 0, |
||||
|
private val customers: String Either ArraySeq[Customer] |
||||
|
) { |
||||
|
import HHCSim2._ |
||||
|
import Ordering.Double.IeeeOrdering |
||||
|
def go() = { |
||||
|
customers |
||||
|
.flatMap(checkEmpty) |
||||
|
.map(formAdjMatrix) |
||||
|
.map(e => mstUsingPrims(e)) |
||||
|
.map { case (mst, epsilon) => removeEdges(mst)(epsilon) } |
||||
|
.map { case (mstUpdated, removed) => findClusters(mstUpdated)(removed) } |
||||
|
.map { |
||||
|
case (clusters, edgeMappings, removedEdges) => |
||||
|
groupClusters(clusters, edgeMappings, removedEdges) |
||||
|
} |
||||
|
} |
||||
|
def go2() = { |
||||
|
val res = go().flatMap(value => { |
||||
|
val (clusters, edgeMappings, removed, clusterGroups) = value |
||||
|
workloadBalance(clusters, edgeMappings, removed, clusterGroups) |
||||
|
}) |
||||
|
res |
||||
|
} |
||||
|
|
||||
|
def workloadBalance[N: Numeric]( |
||||
|
clusters: Clusters, |
||||
|
edgeMappings: ArraySeq[List[HHCEdge2[N]]], |
||||
|
removedEdges: RemovedEdges[N], |
||||
|
groups: ArraySeq[(Int, List[(Int, N)])] |
||||
|
) = { |
||||
|
customers.map { c => |
||||
|
val k = clusters.size |
||||
|
val mutClusters = clusters.toArray |
||||
|
val worlkLoads = |
||||
|
mutClusters.map(_.map(j => c(j)).foldLeft(0f)((x, y) => x + y.workload)) |
||||
|
val sortedClusters = |
||||
|
mutClusters.map(_.sortWith((x, y) => c(x).workload < c(y).workload)) |
||||
|
val minWL = |
||||
|
c.foldLeft(c(0).workload)(_ min _.workload) |
||||
|
val maxWL = |
||||
|
c.foldLeft(0f)(_ max _.workload) |
||||
|
val WLD = maxWL - minWL |
||||
|
if (WLD > WLDMax) { |
||||
|
var t = Random.between(((k / 2) + 1), k) |
||||
|
while (!mutClusters(t).isEmpty) t = Random.between(((k / 2) + 1), k) |
||||
|
val Ds = groups(t)._2(0) |
||||
|
mutClusters.updated(Ds._1, t) |
||||
|
} |
||||
|
for (_ <- 0 until iterations) { |
||||
|
// val workloads = clusters.flatMap(_.map(j => c(j))) |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
def checkAveragePairwiseDistance[T]( |
||||
|
lst: List[(Int, T)] |
||||
|
)(implicit num: Numeric[T]) = { |
||||
|
val sum = lst.foldLeft(0)((x, y) => x + num.toInt(y._2)) |
||||
|
val avg = sum / lst.length |
||||
|
if (avg < epsilonMax) true else false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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 = |
||||
|
Customer( |
||||
|
Coord(latitude.toDouble, longitude.toDouble), |
||||
|
workLoad.toFloat |
||||
|
) |
||||
|
loop(lst += cust, iter) |
||||
|
} |
||||
|
case _ => { |
||||
|
if (arr.mkString.equals(" ") || arr.mkString.contains("\n")) |
||||
|
Left("Error newline") |
||||
|
else { |
||||
|
Left( |
||||
|
"Error reading customers from" + |
||||
|
s" file - ${arr.mkString(", ")}" |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
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 += 1 |
||||
|
l.toList |
||||
|
}) |
||||
|
adjList2.foreach(println) |
||||
|
(ArraySeq.unsafeWrapArray(adjList2), num.fromInt(sum / count)) |
||||
|
} |
||||
|
|
||||
|
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] |
||||
|
): (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 result = ArraySeq.tabulate(mstUpdated.length) { i => |
||||
|
{ |
||||
|
val buf = ListBuffer[Int]() |
||||
|
// mstUpdated(i).isEmpty match { |
||||
|
// case true => { lst += i } |
||||
|
// case false => { |
||||
|
// if (!visited(i)) { |
||||
|
// lst ++= DFS(i)(mstUpdated) |
||||
|
// for (j <- lst) visited(j) = true |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
visited(i) match { |
||||
|
case true => { |
||||
|
// val (nds, rms) = assignEdges(List(i), removedEdges2) |
||||
|
// egdeMappings(i) = nds |
||||
|
// removedEdges2 = rms |
||||
|
} |
||||
|
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}") |
||||
|
|
||||
|
// result |
||||
|
(result, ArraySeq.unsafeWrapArray(egdeMappings), removedEdges) |
||||
|
// (result.filterNot(_.isEmpty), ArraySeq.unsafeWrapArray(egdeMappings), removedEdges) |
||||
|
} |
||||
|
|
||||
|
def assignEdges[T: Ordering]( |
||||
|
nodes: List[Int], |
||||
|
removedEdges: RemovedEdges[T] |
||||
|
) = { |
||||
|
val it = nodes.iterator |
||||
|
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) |
||||
|
// removedEdges.partition(e => e.toNode == node || e.fromNode == node) |
||||
|
loop(nodes, rm, iter, filt) |
||||
|
} |
||||
|
} |
||||
|
// val filtered = ListBuffer.empty[HHCEdge2[T]] |
||||
|
// val updated = ListBuffer.empty[HHCEdge2[T]] |
||||
|
// for (node <- nodes) { |
||||
|
// val (filt, rm) = |
||||
|
// removedEdges.partition(e => e.toNode == node || e.fromNode == node) |
||||
|
// if (!filtered.isEmpty) { |
||||
|
// // return (filt.toList, rm.toList) |
||||
|
// } else { |
||||
|
// filtered ++= filt |
||||
|
// updated ++= rm |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
// print(filtered) |
||||
|
// (filtered.toList, updated.toList) |
||||
|
val (edges, removedEdgesUpdated) = |
||||
|
loop(nodes, removedEdges, it, List.empty[HHCEdge2[T]]) |
||||
|
// val edges2 = edges.sortWith { |
||||
|
// case (HHCEdge2(_, _, weight1), HHCEdge2(_, _, weight2)) => |
||||
|
// weight1 < weight2 |
||||
|
// } |
||||
|
(edges, removedEdgesUpdated) |
||||
|
} |
||||
|
|
||||
|
def groupClusters[N: Ordering]( |
||||
|
clusters: Clusters, |
||||
|
edgeMappings: ArraySeq[List[HHCEdge2[N]]], |
||||
|
removed: RemovedEdges[N] |
||||
|
) = { |
||||
|
var k = -1 |
||||
|
val x = ArraySeq.tabulate(edgeMappings.size)(i => { |
||||
|
val buf = ListBuffer.empty[(Int, N)] |
||||
|
val lst = edgeMappings(i) |
||||
|
val y = lst.foreach(e => { |
||||
|
if (edgeMappings(e.toNode).isEmpty) { |
||||
|
// var buf = ListBuffer.empty[(Int, N)] |
||||
|
for (j <- 0 until clusters.size) { |
||||
|
for (edge <- clusters(j)) { |
||||
|
if (edge == e.toNode) |
||||
|
buf += ((j, e.weight)) |
||||
|
} |
||||
|
} |
||||
|
// Left(buf.toList) |
||||
|
} else { |
||||
|
buf += ((e.toNode, e.weight)) |
||||
|
} |
||||
|
// Right(lst.map { |
||||
|
// case HHCEdge2(fromNode, toNode, weight) => (toNode, weight) |
||||
|
// }) |
||||
|
}) |
||||
|
if (!clusters(i).isEmpty) k += 1 |
||||
|
// val lst2 = lst.map { |
||||
|
// case HHCEdge2(fromNode, toNode, weight) => (toNode, weight) |
||||
|
// } |
||||
|
// if (buf.isEmpty) lst2 else buf.toList ::: lst2 |
||||
|
// if (lt.isEmpty) rt else lt |
||||
|
(k, buf.toList.sortWith { |
||||
|
case ((_, weight1), (_, weight2)) => |
||||
|
weight1 < weight2 |
||||
|
}) |
||||
|
}) |
||||
|
(clusters, edgeMappings, removed, x) |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue