ayaderaghul

asynchorous backtracking algorithm - graph coloring

Jul 8th, 2026 (edited)
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 5.62 KB | Science | 0 0
  1. export {Agent, Environment}
  2. type AgentID = number
  3. type Value = number
  4.  
  5. type Assignment = {
  6.     agentID: AgentID,
  7.     value: Value
  8. }
  9. type Nogood = Assignment[]
  10. type QueuedMessage = {
  11.     type: 'OK' | 'NOGOOD' | 'ADD_NEIGHBOR',
  12.     from: AgentID,
  13.     to: AgentID,
  14.     payload: any
  15. }
  16.  
  17. class Agent {
  18.     id: AgentID;
  19.     domain: number[];
  20.     neighbors: number[];
  21.     terminated: boolean;
  22.     currentValue: number;
  23.     agentView: Map<number,number>;
  24.     nogoodList: Nogood[];
  25.     environment: Environment;
  26.  
  27.     constructor(id: AgentID, domain: number[], neighbors: number[], environment: Environment) {
  28.         this.id = id;
  29.         this.domain = domain;
  30.         this.neighbors = neighbors;
  31.         this.terminated = domain.length === 0;
  32.         this.currentValue = this.terminated ? -1 : domain[0];    
  33.         this.agentView = new Map<number,number>;
  34.         this.nogoodList = [];
  35.         this.environment = environment;
  36.     }
  37.  
  38.     initialize(){
  39.         for (const neighbor of this.neighbors) {
  40.             if (neighbor > this.id) {
  41.                 this.environment.sendMessage(
  42.                     'OK',
  43.                     this.id,
  44.                     neighbor,
  45.                     [this.id, this.currentValue]
  46.                 )
  47.             }
  48.         }
  49.     }
  50.  
  51.     isConsistent(agentView: Map<number,number>, currentValue: number): boolean {
  52.         for (const [agentId, otherAgentValue] of agentView) {
  53.             if (otherAgentValue === currentValue)
  54.                 return false
  55.         }
  56.  
  57.         // no good
  58.  
  59.         for (const nogood of this.nogoodList) {
  60.             const matchesView = nogood.every(assign => {
  61.                 if (assign.agentID === this.id) return assign.value === currentValue
  62.                 return agentView.get(assign.agentID) === assign.value
  63.             })
  64.             if (matchesView) return false
  65.         }
  66.  
  67.  
  68.         return true
  69.     }
  70.  
  71.     handleMessage(msg: QueuedMessage){
  72.         if (msg.type === 'OK') {
  73.             this.agentView.set(msg.payload[0], msg.payload[1])
  74.             this.checkAgentView()
  75.         } else if (msg.type === 'NOGOOD') {
  76.             this.nogoodList.push(msg.payload)
  77.             for (const [Ak, dk] of msg.payload) {
  78.                 if (!this.neighbors.includes(Ak)) {
  79.                     this.agentView.set(Ak, dk)
  80.                     this.environment.sendMessage('ADD_NEIGHBOR', this.id, Ak, null)
  81.                 }
  82.             }
  83.             this.checkAgentView()
  84.         } else {
  85.             this.neighbors.push(msg.from)
  86.             this.environment.sendMessage('OK', this.id, msg.from, [this.id, this.currentValue])
  87.         }
  88.     }
  89.  
  90.     checkAgentView() {
  91.         console.log("in agent view", this.agentView)
  92.         if (!this.isConsistent(this.agentView, this.currentValue)) {
  93.             console.log("inconsistent")
  94.             let candidate = this.domain.find((element) => this.isConsistent(this.agentView, element))
  95.             if (candidate === undefined) {
  96.                 this.backtrack()
  97.             } else {
  98.                 this.currentValue = candidate
  99.                 let lowerPriorityNeighbors = this.neighbors.filter((n) => n > this.id)
  100.                 lowerPriorityNeighbors.forEach((n) => this.environment.sendMessage('OK', this.id, n, [this.id, this.currentValue]))
  101.             }
  102.         }
  103.     }
  104.  
  105.    
  106.  
  107.     backtrack() {
  108.         let nogood = this.hyperresolve(this.nogoodList)
  109.         if (!nogood || nogood.length === 0) {
  110.             this.environment.broadcastTermination()
  111.             this.terminated = true
  112.         } else {
  113.             let lowestPriorityNeighborNogood = nogood.reduce((prev:Assignment, curr:Assignment) => prev.agentID < curr.agentID ? curr : prev)
  114.             this.environment.sendMessage('NOGOOD', this.id, lowestPriorityNeighborNogood.agentID, nogood)
  115.             this.agentView.delete(lowestPriorityNeighborNogood.agentID)
  116.             this.checkAgentView()
  117.         }
  118.     }
  119.  
  120.     hyperresolve(nogoodList: Nogood[]): Nogood {
  121.         let res = new Map<AgentID, Assignment>()
  122.  
  123.         for (const nogood of nogoodList) {
  124.             for (const assignment of nogood) {
  125.                 if (assignment.agentID === this.id)
  126.                     continue
  127.                 else
  128.                     res.set(assignment.agentID, assignment)
  129.             }
  130.         }
  131.         return [...res.values()]
  132.     }
  133. }
  134.  
  135. class Environment {
  136.     agents: Map<AgentID, Agent> = new Map()
  137.     messageQueue: QueuedMessage[] = []
  138.     running: boolean = true
  139.  
  140.     addAgent(agent: Agent){
  141.         this.agents.set(agent.id, agent)
  142.     }
  143.  
  144.     sendMessage(type: 'OK' | 'NOGOOD' | 'ADD_NEIGHBOR', from: AgentID, to: AgentID, payload: any):void {
  145.         this.messageQueue.push({type, from, to, payload})
  146.     }
  147.     broadcastTermination() {
  148.         console.log("TERMINATED")
  149.         this.running = false
  150.     }
  151.  
  152.     async process() {
  153.         this.agents.forEach(a => a.initialize())
  154.         console.log("after agent view")
  155.         while (this.running && this.messageQueue.length > 0) {
  156.             let msg = this.messageQueue.shift()!
  157.             const receiver = this.agents.get(msg.to)
  158.             if (!receiver) continue
  159.             receiver.handleMessage(msg)
  160.  
  161.         }
  162.  
  163.         if (this.running) {
  164.             console.log("--- Solution Found ---");
  165.             this.agents.forEach(a => console.log(`Agent ${a.id}: ${a.currentValue}`));
  166.         }
  167.     }
  168. }
  169.  
  170. // RUNNING
  171. let env = new Environment()
  172. let agent0 = new Agent(0, [0,1,2], [1,2], env)
  173. let agent1 = new Agent(1, [0,1,2], [2], env)
  174. let agent2 = new Agent(2, [0,1,2], [], env)
  175.  
  176. env.addAgent(agent0)
  177. env.addAgent(agent1)
  178. env.addAgent(agent2)
  179.  
  180. env.process()
Advertisement
Add Comment
Please, Sign In to add comment