"""Episode runner with persistent pheromone across churn.""" import numpy as np, sim, schedulers as sch SCHEDULERS = { "RoundRobin": lambda ctx, st, **k: sch.round_robin(ctx), "LeastConn": lambda ctx, st, **k: sch.least_connection(ctx), "ACO": lambda ctx, st, **k: sch.standalone_aco(ctx, iters=k["iters"], state=st), "PSO": lambda ctx, st, **k: sch.standalone_pso(ctx, iters=k["iters"]), "WOA": lambda ctx, st, **k: sch.woa(ctx, iters=k["iters"]), "AdaptiSwarm": lambda ctx, st, **k: sch.adaptiswarm_edge(ctx, iters=k["iters"], sigma_u=k.get("sigma_u",0.0), state=st), "Abl_ACOonly": lambda ctx, st, **k: sch.adaptiswarm_edge(ctx, iters=k["iters"], use_pso=False, sigma_u=k.get("sigma_u",0.0), state=st), "Abl_PSOrand": lambda ctx, st, **k: sch.adaptiswarm_edge(ctx, iters=k["iters"], use_aco_seed=False, use_adaptive=False, sigma_u=k.get("sigma_u",0.0), state=st), "Abl_PSOseed": lambda ctx, st, **k: sch.adaptiswarm_edge(ctx, iters=k["iters"], use_adaptive=False, sigma_u=k.get("sigma_u",0.0), state=st), } def run_episode(name, n_nodes, lam, seed, n_steps=15, iters=40, alpha=0.5, beta=0.3, gamma=0.2, churn=True, probe_step=8): rng = np.random.default_rng(seed) nodes = sim.make_nodes(n_nodes, rng) cap, mem, p_idle, p_peak = sim.node_arrays(nodes) link_lat = sim.make_link_latency(n_nodes, rng) active = np.ones(n_nodes, dtype=bool); backlog = np.zeros(n_nodes); state = {} agg = dict(latency=[], energy=[], util=[], jain=[], A_met=[], all_met=[], sigma=[]) conv_curve = None; prev_util = np.zeros(n_nodes) for step in range(n_steps): if churn and n_nodes >= 5: if rng.random() < 0.3: idx = rng.integers(n_nodes); active[idx] = not active[idx] if active.sum() < max(2, n_nodes // 3): active[:] = True act_idx = np.where(active)[0] a_cap = cap[act_idx]; a_mem = mem[act_idx]; a_link = link_lat[act_idx] a_pidle = p_idle[act_idx]; a_ppeak = p_peak[act_idx]; a_backlog = backlog[act_idx] n_arr = rng.poisson(lam * sim.DT) demand, tmem, deadline, cls = sim.make_tasks(n_arr, rng) sigma_u_live = float(np.std(prev_util[act_idx])) if act_idx.size else 0.0 ctx = sch.Ctx(demand, tmem, deadline, cls, a_backlog, a_cap, a_link, a_pidle, a_ppeak, alpha, beta, gamma, rng, node_mem=a_mem) local_state = {} for gkey in ("aco", "adapti"): gtau = state.get("global_" + gkey) if gtau is not None: local_state[gkey] = gtau[act_idx].copy() assign, curve = SCHEDULERS[name](ctx, local_state, iters=iters, sigma_u=sigma_u_live) for gkey in ("aco", "adapti"): if gkey in local_state: gtau = state.get("global_" + gkey) if gtau is None: gtau = np.ones(n_nodes) gtau[act_idx] = local_state[gkey]; state["global_" + gkey] = gtau if n_arr > 0: mtr = sim.evaluate(assign, demand, tmem, deadline, cls, a_backlog, a_cap, a_link, a_pidle, a_ppeak, alpha, beta, gamma, return_metrics=True, node_mem=a_mem) for k,mk in [("latency","avg_latency"),("energy","energy"),("util","utilization"), ("jain","jain"),("A_met","classA_deadline_met"),("all_met","all_deadline_met"),("sigma","sigma_u")]: agg[k].append(mtr[mk]) added = np.zeros(len(act_idx)) for j,n in enumerate(assign): added[n] += demand[j] backlog[act_idx] = np.maximum(a_backlog + added - a_cap * sim.DT, 0.0) prev_util = np.zeros(n_nodes); prev_util[act_idx] = mtr["served"] / (a_cap * sim.DT) else: backlog[act_idx] = np.maximum(a_backlog - a_cap * sim.DT, 0.0) if step == probe_step and len(curve) > 1: conv_curve = curve def stat(key): v = np.array(agg[key]) if agg[key] else np.array([0.0]); return v.mean() conv_iter = iters if conv_curve is not None and len(conv_curve) > 1: final = conv_curve[-1]; rs = abs(conv_curve[0] - final) + 1e-9 for i,f in enumerate(conv_curve): if abs(f - final) <= 0.02 * rs: conv_iter = i + 1; break return dict(latency_ms=stat("latency")*1000.0, utilization=stat("util")*100.0, energy=stat("energy"), jain=stat("jain"), A_deadline=stat("A_met")*100.0, all_deadline=stat("all_met")*100.0, conv_iter=conv_iter, conv_curve=conv_curve)