From 7d045e79b6c47aa6508e64de84fbb766c4ff8186 Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Tue, 21 Apr 2020 21:42:19 +0530 Subject: [PATCH] temp commit --- src/main/scala/HHCSim.scala | 24 +- src/main/scala/Main.scala | 118 +++++++-- src/main/scala/model/HHCEdge.scala | 2 + src/main/scala/model/Types.scala | 29 +++ src/main/scala/sim/HHCSim2.scala | 395 +++++++++++++++++++++++++++++ src/main/scala/util/Util.scala | 116 ++++++++- 6 files changed, 649 insertions(+), 35 deletions(-) create mode 100644 src/main/scala/model/Types.scala create mode 100644 src/main/scala/sim/HHCSim2.scala diff --git a/src/main/scala/HHCSim.scala b/src/main/scala/HHCSim.scala index 62fba0b..3f9d2e0 100644 --- a/src/main/scala/HHCSim.scala +++ b/src/main/scala/HHCSim.scala @@ -24,23 +24,15 @@ class HHCSim( ) .tap(logCentroids) .tap(logRemovedEdges) - .map( - e => - e match { - case (mst, centroids, removed) => { - printGraph(mst) - Util.findClusters(mst, centroids, removed) - } - } - ) + .map { + case (mst, centroids, removed) => + Util.findClusters(mst, centroids, removed) + } .tap(logClusters) - .map( - e => - e match { - case (centroids, clusters, removed) => - Util.groupClusters(centroids, clusters, removed) - } - ) + .map { + case (centroids, clusters, removed) => + Util.groupClusters(centroids, clusters, removed) + } def checkEmpty( customers: IndexedSeq[Customer] diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index 7b5835f..9267bbf 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -6,6 +6,7 @@ import util.Util import config.Conf import java.io.File import scala.io.Source +import sim.HHCSim2 object Main { def main(args: Array[String]): Unit = { @@ -13,25 +14,32 @@ object Main { val bufferedSource = Source.fromFile(conf.infile()) - val customers = Util.getCustomers(bufferedSource) + val customers = HHCSim2.getCustomers(bufferedSource) bufferedSource.close() - val hhc = new HHCSim(epsilonMax = 40, iterations = 10, WLDMax = 10) - val adjList2 = - customers - .map(Util.formAdjMatrix) - .map(e => Util.mstUsingPrims2(e)) - .map(_._1) - // .map(e => Util.makeAdjacencyList2(e._1)) - adjList2.map(adjl => { - for (i <- 0 until adjl.size) { - print(s"$i -> ") - adjl(i).foreach(e => { - print(f"(${e._1}, ${e._2}%.2f), ") - }) - println - } - }) + // import Ordering.Double.IeeeOrdering + // val hhc = new HHCSim(epsilonMax = 40, iterations = 10, WLDMax = 10) + // val adjList2 = + // customers + // .map(Util.formAdjMatrix) + // .map(e => Util.mstUsingPrims2(e)) + // .map { case (mst, eps) => Util.removeEdges(mst)(eps) } + // // .map(_._1) + + // // .map(e => Util.makeAdjacencyList2(e._1)) + // adjList2.map(e => { + // val (mst, rm) = e + // for (i <- 0 until mst.size) { + // print(s"$i -> ") + // mst(i).foreach(e => { + // print(f"(${e._1}, ${e._2}%.2f), ") + // }) + // println + // } + + // println(rm) + // }) + // val fnl2 = hhc.go(customers) // customers.map(println(_)) @@ -61,6 +69,82 @@ object Main { // println(s"Oops, an error occured. The error message was: $e") // } + val hhc = new HHCSim2( + epsilonMax = 40, + iterations = 10, + WLDMax = 10, + customers = customers + ) + + val x = hhc.go() + + x match { + case Left(value) => println(value) + case Right(value) => + } + + x.foreach(d => { + val (a, b, c, g) = d + println(c) + + println + + for (i <- 0 until b.length) { + if (!b(i).isEmpty) { + print(s"$i ") + print(b(i)) + // edgeMappings + // b(i).foreach(e => { + // print(f"(${e.toNode}%d, ${e.weight}%.2f), ") + // }) + println + } + } + println + + for (i <- 0 until g.length) { + if (!g(i)._2.isEmpty) { + print(s"$i ") + print(g(i)) + // edgeMappings + // b(i).foreach(e => { + // print(f"(${e.toNode}%d, ${e.weight}%.2f), ") + // }) + println + } + } + println + + var i = 0 + a.foreach(e => { + if (!e.isEmpty) { + print(s"Cluster-$i: ") + i += 1 + e.foreach(f => { print(s"$f ") }) + println + } + }) + + }) + + val x2 = hhc.go2() + x2.map(_.map(println)) + + // x.map(e => { + // val (mst, rm) = e + // val y = HHCSim2.DFS(0)(mst) + // println(y) + // for (i <- 0 until mst.size) { + // print(s"$i -> ") + // mst(i).foreach(e => { + // print(f"(${e.toNode}, ${e.weight}%.2f), ") + // }) + // println + // } + + // println(rm) + // }) + val coord1 = Coord(3, 4) println(s"Distance from origin = ${coord1.distance}") diff --git a/src/main/scala/model/HHCEdge.scala b/src/main/scala/model/HHCEdge.scala index dfc8141..3e131d3 100644 --- a/src/main/scala/model/HHCEdge.scala +++ b/src/main/scala/model/HHCEdge.scala @@ -6,3 +6,5 @@ import util.Util case class HHCEdge(v1: Customer, v2: Customer) { val weight = Util.getHaversineDistance(v1.location, v2.location) } + +case class HHCEdge2[T](fromNode: Int, toNode: Int, weight: T) diff --git a/src/main/scala/model/Types.scala b/src/main/scala/model/Types.scala new file mode 100644 index 0000000..5b74166 --- /dev/null +++ b/src/main/scala/model/Types.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]] +} diff --git a/src/main/scala/sim/HHCSim2.scala b/src/main/scala/sim/HHCSim2.scala new file mode 100644 index 0000000..327136c --- /dev/null +++ b/src/main/scala/sim/HHCSim2.scala @@ -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) + } +} diff --git a/src/main/scala/util/Util.scala b/src/main/scala/util/Util.scala index 486689f..c790109 100644 --- a/src/main/scala/util/Util.scala +++ b/src/main/scala/util/Util.scala @@ -12,6 +12,8 @@ import com.typesafe.scalalogging.LazyLogging import scala.collection.immutable.ArraySeq object Util extends LazyLogging { + type Graph[T] = IndexedSeq[Seq[(Int, T)]] + import Ordering.Implicits._ private val r = 6471.00 // km def toRad(num: Double): Double = { @@ -253,6 +255,117 @@ object Util extends LazyLogging { (mst, centroids.toIndexedSeq, removed.toIndexedSeq) } + // def removeEdges[T](mst: IndexedSeq[Seq[(Int, T)]], epsilon: T)( + // implicit num: Numeric[T] + // ): (IndexedSeq[Seq[(Int, T)]], IndexedSeq[(Int, Int, T)]) = { + // // val mutMST = mst.to(Array) + // // val n = mst.length + // // val removed: mutable.ListBuffer[(Int, Int, T)] = mutable.ListBuffer.empty + // val it = mst.iterator + + // @annotation.tailrec + // def loop( + // mst: IndexedSeq[Seq[(Int, T)]], + // removed: Seq[(Int, Int, T)], + // epsilon: T, + // iter: Iterator[Seq[(Int, T)]], + // index: Int + // )( + // implicit num: Numeric[T] + // ): (IndexedSeq[Seq[(Int, T)]], Seq[(Int, Int, T)]) = { + // if (!iter.hasNext) { + // return (mst, removed) + // } + // val lst = iter.next + // // val filtered = lst.filter(e => { + // // val (node, weight) = e + // // num.lt(weight, epsilon) + // // }) + // // val rm = lst + // // .filter(e => { + // // val (node, weight) = e + // // num.gt(weight, epsilon) + // // }) + // // .map(e => { + // // val (node, weight) = e + // // (index, node, weight) + // // }) + + // val (filtered, rm) = lst.partition(e => { + // val (node, weight) = e + // num.lt(weight, epsilon) + // }) + // val rm2 = rm.map(e => { + // val (node, weight) = e + // (index, node, weight) + // }) + // loop( + // mst.updated(index, filtered), + // removed ++ rm2, + // epsilon, + // iter, + // index + 1 + // ) + // } + // // for (i <- 0 until n) { + // // // for (j <- 0 until n) { + // // // val weight = mutMST(i)(j)._2 + // // // if (num.gt(weight, epsilon) && weight != 0) { + // // // removed += ((i, j, weight)) + // // // mutMST(i).remove(j) + // // // } + // // // } + + // // // var j = 0; + // // // for ((node, weight) <- mutMST(i)) { + // // // if (num.gt(weight, epsilon) && weight != 0) { + // // // removed += ((i, j, weight)) + // // // mutMST(i).remove(j) + // // // } + // // // j += 1 + // // // } + + // // mutMST(i) = mutMST(i).filter(e => { + // // val (node, weight) = e + // // removed += ((i, node, weight)) + // // num.lt(weight, epsilon) + // // }) + // // } + + // // val updated = ArraySeq.unsafeWrapArray(mutMST.map(e => e.toList)) + // // val updated = ArraySeq.unsafeWrapArray(mutMST) + + // val (updated, removed) = + // loop(mst, Seq[(Int, Int, T)](), epsilon, it, 0) + + // (updated, removed.toIndexedSeq) + // } + + def removeEdges[N: Ordering]( + mst: Graph[N] + )(epsilon: N): (Graph[N], List[(Int, Int, N)]) = { + + import scala.collection.mutable.ListBuffer + + val removed = ListBuffer.empty[(Int, Int, N)] + + val result = ArraySeq.tabulate(mst.length) { i => + val (filtered, rm) = mst(i).partitionMap { + case (node, weight) => + if (weight <= epsilon) + Left((node, weight)) + else + Right((i, node, weight)) + } + + removed ++= rm + + filtered + } + + (result, removed.toList) + } + def DFS[T]( start: Int, graph: Array[Array[T]] @@ -341,8 +454,7 @@ object Util extends LazyLogging { lst: IndexedSeq[Customer], iter: Iterator[String] ): String Either IndexedSeq[Customer] = { - if (!iter.hasNext) - return Right(lst) + if (!iter.hasNext) return Right(lst) val line = iter.next val arr = line.split(",").map(_.trim) arr match {