Skip to content
Snippets Groups Projects
Commit 811b868a authored by Maxime Morge's avatar Maxime Morge :construction_worker:
Browse files

First CBBA version

parent 4404221f
No related branches found
No related tags found
No related merge requests found
// Copyright (C) Maxime MORGE 2024 // Copyright (C) Maxime MORGE 2024
import org.scata.core.AssignmentProblem import org.scata.core.AssignmentProblem
import org.scata.algorithm.CBAA import org.scata.algorithm.CBBA
import org.scata.patrol.Environment import org.scata.patrol.Environment
object Main { object Main {
def main(args: Array[String]): Unit = { def main(args: Array[String]): Unit = {
val nbRobots= 4 val nbRobots= 4
val nbTargets = 4 val nbTargets = 8
val env = Environment.randomEnvironment(nbRobots, nbTargets) val env = Environment.randomEnvironment(nbRobots, nbTargets)
println(env) println(env)
val pb = env.toSAP() val pb = env.toAP()
println(pb) println(pb)
val cbaa = new CBAA(pb) val cbba = new CBBA(pb)
println(pb) println(pb)
val solution = cbaa.solve() val solution = cbba.solve()
println(solution) println(solution)
} }
} }
\ No newline at end of file
// Copyright (C) Maxime MORGE, 2024
package org.scata.algorithm
import org.scata.core._
import scala.math.Ordering.Implicits.seqOrdering
class CBBA(pb: AssignmentProblem) {
private val debug = true
private val maxTasksPerAgent = pb.tasks.size // You may define a limit Lt per agent
// Initialize structures
private var bundle: Map[Worker, List[Task]] = pb.workers.map(_ -> List.empty[Task]).toMap
private var path: Map[Worker, List[Task]] = pb.workers.map(_ -> List.empty[Task]).toMap
private var winningBid: Map[Task, (Worker, Int)] = pb.tasks.map(_ -> (NoWorker, Int.MaxValue)).toMap
private var winningAgent: Map[Task, Worker] = pb.tasks.map(_ -> NoWorker).toMap
private def marginalCost(worker: Worker, task: Task, currentPath: List[Task]): Int = {
// Here a simple cost (you can add a more complex scoring function)
pb.cost(worker, task)
}
/**
* Phase 1: Each agent builds a bundle of tasks greedily
*/
private def buildBundle(worker: Worker): Unit = {
while (bundle(worker).size < maxTasksPerAgent) {
val candidateTasks = pb.tasks.diff(bundle(worker).toSet)
val costs = candidateTasks.map(task => task -> marginalCost(worker, task, path(worker))).toMap
val validTasks = costs.filter { case (task, cost) => cost < winningBid(task)._2 }
if (validTasks.nonEmpty) {
val bestTask = validTasks.minBy { case (task, gain) => (gain, task.name) }._1
bundle = bundle.updated(worker, bundle(worker) :+ bestTask)
path = path.updated(worker, path(worker) :+ bestTask)
winningBid = winningBid.updated(bestTask, (worker, costs(bestTask)))
winningAgent = winningAgent.updated(bestTask, worker)
if (debug) println(s"${worker.name} adds ${bestTask.name} to bundle with bid ${costs(bestTask)}")
} else return
}
}
/**
* Phase 2: Conflict resolution using consensus across all workers
*/
private def resolveConflicts(): Boolean = {
var changed = false
for (task <- pb.tasks) {
val bids = pb.workers.map(worker =>
(worker, bundle(worker).indexOf(task)) match {
case (_, -1) => (worker, Int.MaxValue)
case (_, idx) => (worker, marginalCost(worker, task, path(worker)))
}
)
val (bestWorker, bestBid) = bids.minBy { case (w, b) => (b, w.name) }
if (winningAgent(task) != bestWorker) {
// Task is reallocated, remove from previous owner's bundle
val oldOwner = winningAgent(task)
bundle = bundle.updated(oldOwner, bundle(oldOwner).filterNot(_ == task))
path = path.updated(oldOwner, path(oldOwner).filterNot(_ == task))
bundle = bundle.updated(bestWorker, bundle(bestWorker) :+ task)
path = path.updated(bestWorker, path(bestWorker) :+ task)
winningAgent = winningAgent.updated(task, bestWorker)
winningBid = winningBid.updated(task, (bestWorker, bestBid))
changed = true
if (debug) println(s"Task ${task.name} reassigned to ${bestWorker.name} with bid $bestBid")
}
}
changed
}
/**
* Solve the assignment using CBBA
*/
def solve(): MultipleAssignment = {
var converged = false
while (!converged) {
for (worker <- pb.workers) buildBundle(worker)
converged = !resolveConflicts()
}
val result = new MultipleAssignment(pb)
for ((worker, tasks) <- bundle; task <- tasks) {
result.bundle = result.bundle.updated(worker, result.bundle(worker) :+ task)
}
result
}
}
\ No newline at end of file
...@@ -39,9 +39,7 @@ class Environment(val robots: SortedSet[Robot], ...@@ -39,9 +39,7 @@ class Environment(val robots: SortedSet[Robot],
/** /**
* Generate a single assignment problem * Generate a single assignment problem
*/ */
def toSAP(): AssignmentProblem = { def toAP()(): AssignmentProblem = {
if (n > m)
throw new RuntimeException("Cannot generate a single assignment problem since there are more targets than robots")
// Convert robots and targets into Workers and Tasks respectively // Convert robots and targets into Workers and Tasks respectively
val workers: SortedSet[Worker] = robots.map(r => new Worker(r.name)) val workers: SortedSet[Worker] = robots.map(r => new Worker(r.name))
......
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