Skip to content
Snippets Groups Projects
Commit ac0d87b8 authored by Anne-Cecile Caron's avatar Anne-Cecile Caron
Browse files

algo VDS for reallocation

parent ab3c4235
Branches
No related tags found
No related merge requests found
package org.smastaplus.balancer.local
import org.smastaplus.core.{Allocation, GlobalFlowtime, LocalFlowtime, Makespan, STAP, SocialRule}
import org.smastaplus.process.{Deal, SingleDelegation, SingleSwap}
/**
*
* @param stap instance to tackle
* @param rule is the social rule to be applied
* @param name of the balancer
* @param withSwap is true if swap are allowed
*
* Inspired by Algo VDS in MIC97 "A variable depth search algorithm for the generalized assignment problem"
* Authors : Mutsunori Yagiura, Takashi Yamaguchi and Toshihide Ibaraki
*
* best feasible solution is estimated with Global Flowtime
* cost(alloc) = local flowtime, so needs to know the 2 contractors
* pcost(alloc) = cost(alloc) + alpha * global flowtime
*
*/
class VDSBalancer (stap: STAP, rule: SocialRule = GlobalFlowtime, name: String = "VDSBalancer",
withSwap: Boolean = false, alpha:Double = 0.5)
extends LocalBalancer(stap,rule, name, withSwap) {
var newBestAlloc: Boolean = false
var bestAllocForCost : Allocation = null
var bestCost : Double = Double.MaxValue
/**
*
* compute the cost of an allocation, i.e. the local flowtime between two contractors of a deal
*/
def cost(allocation: Allocation, deal: Deal): Double = {
val node1 = deal.contractors(0)
val node2 = deal.contractors(1)
// local flowtime between node1 and node2
allocation.jobs.foldLeft(0.0) { (sum, job) => sum + Math.max(allocation.completionTime(job, node1), allocation.completionTime(job, node2)) }
}
/**
*
* compute the "penalized" cost of an allocation, i.e. the local flowtime between two contractors of a deal, penalized by global flowtime
*/
def pcost(allocation: Allocation, deal: Deal): Double =
cost(allocation, deal) + alpha * allocation.globalFlowtime
/**
*
* best feasible allocation between a1 and a2, estimated with Global Flowtime
*/
def bestBetween(a1 : Allocation, a2 : Allocation) : Allocation =
if (a1.globalFlowtime <= a2.globalFlowtime) a1 else a2
/**
* returns a neighbor better than the current allocation (the first encountered)
*/
def firstBetterNeighbor(current: Allocation, mycost: (Allocation, Deal) => Double, withSwap: Boolean, alreadyVisited : Set[Allocation]): Option[(Allocation, Deal)] = {
newBestAlloc = false
stap.ds.computingNodes.foreach { initiator => // Foreach initiator
current.bundle(initiator).foreach { task => // Foreach task in its bundle
(stap.ds.computingNodes diff Set(initiator)).foreach { responder => // Foreach responder
if (withSwap) { // Let us consider the swaps from this delegation
current.bundle(responder).foreach { counterpart => // Foreach counterpart
val swap = new SingleSwap(stap, initiator, responder, task, counterpart)
val nextAllocation = swap.execute(current)
val currentValue = mycost(current, swap)
val nextValue = mycost(nextAllocation, swap)
if (nextValue < currentValue && !alreadyVisited.contains(nextAllocation)) {
bestAllocForCost = bestBetween(current, nextAllocation)
bestCost = bestAllocForCost.globalFlowtime
if (bestCost > current.globalFlowtime) newBestAlloc = true else newBestAlloc = false
return Some((nextAllocation, swap))
}
}
} else { // gift, not swap
val gift = new SingleDelegation(stap, initiator, responder, task)
val nextAllocation = gift.execute(current)
val nextValue = mycost(nextAllocation, gift)
val currentValue = mycost(current, gift)
if (nextValue < currentValue && !alreadyVisited.contains(nextAllocation)) {
bestAllocForCost = bestBetween(current, nextAllocation)
bestCost = bestAllocForCost.globalFlowtime
if (bestCost > current.globalFlowtime) newBestAlloc = true else newBestAlloc = false
return Some((nextAllocation, gift))
}
}
}
}
}
None
}
/**
* returns the best neighbor of the current allocation w.r.t. mycost
*/
def bestNeighbor(current: Allocation, mycost: (Allocation, Deal) => Double, withSwap: Boolean, alreadyVisited : Set[Allocation]): Option[(Allocation, Deal)] = {
newBestAlloc = false
var bestAllocation : Allocation = null
var bestValue : Double = Double.MaxValue
var bestDeal : Deal = null
stap.ds.computingNodes.foreach { initiator => // Foreach initiator
current.bundle(initiator).foreach { task => // Foreach task in its bundle
(stap.ds.computingNodes diff Set(initiator)).foreach { responder => // Foreach responder
if (withSwap) { // Let us consider the swaps from this delegation
current.bundle(responder).foreach { counterpart => // Foreach counterpart
val swap = new SingleSwap(stap, initiator, responder, task, counterpart)
val nextAllocation = swap.execute(current)
val currentValue = mycost(current, swap)
val nextValue = mycost(nextAllocation, swap)
if (nextValue < currentValue && !alreadyVisited.contains(nextAllocation)) {
bestAllocation = nextAllocation
bestValue = nextValue
bestDeal = swap
}
}
} else { // gift, not swap
val gift = new SingleDelegation(stap, initiator, responder, task)
val nextAllocation = gift.execute(current)
val nextValue = mycost(nextAllocation, gift)
val currentValue = mycost(current, gift)
if (nextValue < currentValue && !alreadyVisited.contains(nextAllocation)) {
bestAllocation = nextAllocation
bestValue = nextValue
bestDeal = gift
}
}
}
}
}
if (bestAllocation == null) None
else {
bestAllocForCost = bestBetween(current, bestAllocation)
bestCost = bestAllocForCost.globalFlowtime
if (bestCost > current.globalFlowtime) newBestAlloc = true else newBestAlloc = false
Some((bestAllocation, bestDeal))
}
}
/**
* returns a better allocation, found iterating firstBetterNeighbor
* Use reflexive transitive closure of neighborhood, i.e. If no better allocation is found in neighborhood, returns the allocation itself.
*/
def LS(allocation: Allocation, mycost: (Allocation, Deal) => Double, withSwap: Boolean, alreadyVisited : Set[Allocation]): (Allocation, Set[Allocation]) = {
//super.init(allocation)
val candidate = firstBetterNeighbor(allocation, mycost, withSwap, alreadyVisited)
candidate match {
case Some((alloc, _)) =>
if (alloc.equals(allocation)) {
(allocation, alreadyVisited)
} else {
LS(alloc, mycost, withSwap, alreadyVisited+alloc)
}
case None => (allocation, alreadyVisited)
}
}
def step2b(allocation: Allocation, alreadyVisited : Set[Allocation]): (Allocation, Set[Allocation]) = {
val result1 = bestNeighbor(allocation, pcost, true, alreadyVisited)
result1 match{
case Some((allocbis, _)) => (allocbis, alreadyVisited+allocbis)
case None => (allocation, alreadyVisited)
}
}
def variable_depth_search_step2(allocation: Allocation, alreadyVisited : Set[Allocation]): (Allocation, Set[Allocation]) ={
// step 2a = a shift move
val step2a = bestNeighbor(allocation, pcost, false, alreadyVisited)
step2a match {
case Some((alloc, _)) => // step2b = sequence of swap moves
if (newBestAlloc) //exit to step3
variable_depth_search_step2(bestAllocForCost, alreadyVisited+bestAllocForCost+alloc)
else {
var result1 = step2b(allocation, alreadyVisited+alloc)
//val nextstep = step2b(alloc, alreadyVisited+alloc)
result1 match {
case (allocbis, visited) =>
if (newBestAlloc) //step3
variable_depth_search_step2(bestAllocForCost, visited + bestAllocForCost)
else if (allocbis.equals(alloc) ) // return to step2a (i.e. try shif)
variable_depth_search_step2(allocbis, visited)
else step2b(allocbis, visited+allocbis)
}
}
case None => (bestAllocForCost, alreadyVisited) // exit to step 3 ?
}
}
/**
* Modify the current allocation
* iterate step 2/step 3 in VDS (no iteration with new initial allocation)
*/
override def reallocate(allocation: Allocation): Allocation = {
val current = scheduler.schedule(allocation)
val result_vds = variable_depth_search_step2(current, Set[Allocation]()+current)
result_vds._1
}
/**
* step 1 : Generation of an initial solution
*
* @param allocation
* @return
*/
override def init(allocation: Allocation): Allocation = {
val initialAllocation = super.init(allocation)
bestCost = initialAllocation.globalFlowtime
bestAllocForCost = initialAllocation
// LS modifies bestGF and bestAlloc :
val allocbisWithPath = LS(initialAllocation, pcost, false, Set[Allocation]()+initialAllocation)
LS(allocbisWithPath._1, pcost, true,allocbisWithPath._2)
bestAllocForCost
}
}
/**
* companion object, for testing
*/
object VDSBalancer extends App {
val debug = false
import org.smastaplus.example.stap.ex3.stap
println(stap)
val balancer = new VDSBalancer(stap)
// run = build a first allocation with init(), and then reallocate a
val allocation = balancer.run()
allocation match {
case Some(a) => {
println("after VDS : " )
println(a)
println("global flowtime : "+a.globalFlowtime)
}
case None => println("no allocation")
}
}
...@@ -38,6 +38,8 @@ class Allocation(val stap: STAP, ...@@ -38,6 +38,8 @@ class Allocation(val stap: STAP,
case _ => false case _ => false
} }
override def hashCode(): Int = this.bundle.hashCode()
def canEqual(a: Any): Boolean = a.isInstanceOf[Allocation] def canEqual(a: Any): Boolean = a.isInstanceOf[Allocation]
/** /**
......
package org.smastaplus.core
import org.scalatest.flatspec.AnyFlatSpec
import org.smastaplus.example.allocation.stap1.ex1Allocation
class AllocationComparison extends AnyFlatSpec {
"Two identical maps" should "be equal" in {
val m1: Map[String,Int] = Map[String,Int]("n1"->5,"n2"->3)
val m2: Map[String,Int] = Map[String,Int]("n2"->3,"n1"->5)
println(m1)
println(m2)
assert(m1.equals(m2))
assert(m1==m2)
}
"Two identical allocations" should "be equal" in {
val a1 : Allocation = ex1Allocation.a
val a2 : Allocation = a1.copy()
println(a1)
println(a2)
assert(a1.equals(a2))
assert(a1==a2)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment