Skip to content
Snippets Groups Projects
Commit 1c85bd4c authored by Maxime MORGE's avatar Maxime MORGE
Browse files

First centralize version of CBAA

parent f666539d
No related branches found
No related tags found
No related merge requests found
// Copyright (C) Maxime MORGE 2024
import org.scata.core.SingleAssignmentProblem
import org.scata.algorithm.CBAA
object Main {
def main(args: Array[String]): Unit = {
println("Hello world!")
val nbWorkers = 4
val nbTasks = 3
val pb = SingleAssignmentProblem.randomProblem(nbWorkers, nbTasks)
val cbaa = new CBAA(pb)
println(pb)
val solution = cbaa.solve()
println(solution)
}
}
\ No newline at end of file
// Copyright (C) Maxime MORGE, 2024
package org.scata.algorithm
import org.scata.core._
/**
......@@ -8,11 +7,12 @@ import org.scata.core._
* a single-assignment strategy
*/
class CBAA(pb : SingleAssignmentProblem) {
private val debug = true
var neighbours : Map[Worker, List[Worker]] = Map[Worker, List[Worker]]()
/* pb.workers.foreach{ worker =>
neighbours = neighbours.updated(worker, pb.workers.toList)
}*/
// Generate a fully connected communication network
private val neighbours: Map[Worker, List[Worker]] = pb.workers.toList.map { worker =>
worker -> pb.workers.toList // Each worker is connected to all workers, including themselves
}.toMap
private var workerTaskList: Map[Worker, Map[Task, Boolean]] = pb.workers.toList.map { worker =>
worker -> pb.tasks.toList.map { task =>
......@@ -22,42 +22,31 @@ class CBAA(pb : SingleAssignmentProblem) {
private var winningBid: Map[Worker, Map[Task, (Worker, Int)]] = pb.workers.toList.map { worker =>
worker -> pb.tasks.toList.map { task =>
task -> (NoWorker, 0)
task -> (NoWorker, Int.MaxValue)
}.toMap
}.toMap
/**
* Generate a fully connect communication network
*/
//private def fullyConnected : Map[Worker, List[Worker]] =
/**
* Returns true if the worker has no task assigned
*/
private def isFree(worker : Worker) : Boolean = !pb.tasks.exists(task => workerTaskList(worker)(task))
/**
* Checks if two workers are neighbors
* @param worker1 the first worker
* @param worker2 the second worker
* @return true if they are neighbors, false otherwise
*/
private def isNeighbor(worker1: Worker, worker2: Worker): Boolean = neighbours(worker1).contains(worker2)
/**
* CBAA Phase 1 for worker i
* Selects the best task for the worker based on the cost
* @param i index of the worker in the workers set
* */
def selectTask(i : Int) : Unit = {
private def selectTask(i : Int) : Unit = {
val worker = pb.workers.toIndexedSeq(i)
// Check if the worker has no tasks assigned
if (isFree(worker)) {
if (debug) println(s"Worker ${worker.name} is free")
// Determine valid tasks based on cost comparison
var validTasks: Map[Task, Boolean] = Map[Task, Boolean]()
pb.tasks.foreach { task =>
if (pb.cost(worker, task) < winningBid(worker)(task)._2) {
if (debug) println(s"Worker ${worker.name} can perform task ${task.name}")
validTasks = validTasks.updated(task, true)
} else {
validTasks = validTasks.updated(task, false)
......@@ -66,26 +55,71 @@ class CBAA(pb : SingleAssignmentProblem) {
// If there are valid tasks, select the one with the minimum cost
if (pb.tasks.exists(task => validTasks(task))) {
val bestTask = validTasks.filter(_._2).keys.minBy(task => pb.cost(worker, task))
workerTaskList(worker) = workerTaskList(worker).updated(bestTask, true)
winningBid(worker) = winningBid(worker).updated(bestTask, (worker, pb.cost(worker, bestTask)))
if (debug) println(s"Worker ${worker.name} selects task ${bestTask.name} with cost ${pb.cost(worker, bestTask)}")
workerTaskList = workerTaskList.updated(worker, workerTaskList(worker).updated(bestTask, true))
winningBid = winningBid.updated(worker, winningBid(worker).updated(bestTask, (worker, pb.cost(worker, bestTask))))
}
}
}
/**
* TODO CBAA Phase 1 for worker i
* CBAA Phase 2 for worker i
* Updates the winning bids of the worker based on the minimum winning bid
* among its neighbors.
* Returns true if the worker's winning bid is updated
* @param i index of the worker in the workers set
*/
def consensus(i: Int): Unit = {
private def consensus(i: Int): Boolean = {
var isWinningBidUpdated = false
val worker = pb.workers.toIndexedSeq(i)
// Iterate over all tasks
pb.tasks.foreach { task =>
// Find the minimum winning bid among the neighbors of the worker
val minWinningBid = pb.workers
.filter(neighbor => isNeighbor(worker, neighbor))
.map(neighbor => winningBid((neighbor, task)))
.min
// Update the winning bid for the worker and task
winningBid = winningBid.updated((worker, task), minWinningBid)
pb.tasks.foreach { task : Task =>
// Find the minimum winning bid among the worker's neighbors (including itself)
val minWinningBid = neighbours(worker)
.map(neighbor => winningBid(neighbor)(task))
.minBy(_._2)
println(s"Worker ${worker.name} minimum winning bid for task ${task.name} is ${minWinningBid}")
// Update the worker's winning bid for the task if a lower bid is found
if (minWinningBid._2 < winningBid(worker)(task)._2) {
println(s"Worker ${worker.name} updates winning bid for task ${task.name} to ${minWinningBid}")
isWinningBidUpdated = true
winningBid = winningBid.updated(worker, winningBid(worker).updated(task, minWinningBid))
// Update the worker's task list to reflect the new winning bid
workerTaskList = workerTaskList.updated(worker, workerTaskList(worker).updated(task, minWinningBid._1 == worker))
}
}
isWinningBidUpdated
}
/**
* Solves the single-assignment problem using the CBAA algorithm
* @return the final assignment
*/
def solve() : SingleAssignment = {
var hasConverged = false
while (!hasConverged) {
if (debug) println("CBAA Phase 1")
for (i <- 0 until pb.m) {
selectTask(i)
}
hasConverged = true
if (debug) println("CBAA Phase 2")
for (i <- 0 until pb.m) {
if (consensus(i)) {
hasConverged = false
}
if (debug) println(s"CBAA Phase 2 has converged: ${hasConverged}")
}
}
// Generate the final assignment
val assignment = new SingleAssignment(pb)
pb.workers.foreach { worker =>
pb.tasks.foreach { task =>
if (workerTaskList(worker)(task)) {
assignment.bundle = assignment.bundle.updated(worker, task)
}
}
}
return assignment
}
}
......@@ -17,7 +17,7 @@ class SingleAssignment(val pb: SingleAssignmentProblem) {
}
override def toString: String =
pb.workers.toList.map(worker => s"$worker: $bundle").mkString("\n")
pb.workers.toList.map(worker => s"$worker: ${bundle(worker)}").mkString("\n")
override def equals(that: Any): Boolean =
that match {
......
......@@ -18,10 +18,19 @@ class SingleAssignmentProblem(val workers: SortedSet[Worker],
* Returns a string describing the MASTAPlus problem
*/
override def toString: String = {
s"m: ${workers.size}\n" +
s"n: ${tasks.size}\n" +
"workers: " + workers.mkString(", ") + "\n" +
"tasks: " + tasks.mkString(", ") + "\n"
val workersStr = workers.map(_.name).mkString(", ")
val tasksStr = tasks.map(_.name).mkString(", ")
val costStr = workers.map { worker =>
val taskCosts = tasks.map { task =>
s"${task.name}: ${cost((worker, task))}"
}.mkString(", ")
s"${worker.name} -> [$taskCosts]"
}.mkString("\n")
s"Workers (${workers.size}): $workersStr\n" +
s"Tasks (${tasks.size}): $tasksStr\n" +
s"Costs:\n$costStr"
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment