| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- # lonline_nb.py
- import math
- def lonline_network_benchmarking(network, path_list, bounces, C_budget, return_details=False):
- """
- L-Online 風の逐次削除型 NB。
- 返り値(常に一貫):
- return_details=False:
- (correctness: bool, cost: int, best_path_fidelity: float|None)
- return_details=True:
- (correctness: bool, cost: int, best_path_fidelity: float|None,
- alloc_by_path: dict[int,int], est_fid_by_path: dict[int,float])
- 想定 I/F:
- network.benchmark_path(path, bounces, sample_times) -> (p, used_cost)
- 忠実度変換: fidelity = p + (1 - p)/2
- """
- candidate_set = list(path_list)
- # 既存コード由来のパラメータ(必要に応じて合わせてください)
- s = 0
- C = 0.01
- delta = 0.1
- # 集計器
- cost = 0
- estimated_fidelities = {}
- # 詳細返却用の器(return_details に関わらず初期化:どの分岐でも形を揃える)
- alloc_by_path = {int(p): 0 for p in path_list}
- est_fid_by_path = {}
- if not candidate_set or C_budget <= 0:
- # 何も測れないケースでも形は揃えて返す
- if return_details:
- return False, int(cost), None, alloc_by_path, est_fid_by_path
- return False, int(cost), None
- # 1 経路を 1 サンプル測るコストの近似(ここでは hop 重みの和)
- cost_per_sample_unit = sum(bounces) if sum(bounces) > 0 else 1
- # ---- メインループ ----
- while cost < C_budget and len(candidate_set) > 1:
- s += 1
- # ラウンド s のサンプル回数(既存式)
- Ns = math.ceil(C * (2 ** (2 * s)) * math.log2(max((2 ** s) * len(candidate_set) / delta, 2)))
- if Ns < 4:
- Ns = 4
- # このラウンドで 1 経路に必要なコスト目安
- cost_needed_for_one_path = Ns * cost_per_sample_unit
- # 2 ラウンド目以降で 1 経路すら回せないなら終了
- if cost + cost_needed_for_one_path > C_budget and s > 1:
- break
- # hop ごとに同じ Ns を配る(network 側の想定 I/F に合わせる)
- sample_times = {h: int(Ns) for h in bounces}
- # ラウンド内の観測
- p_s = {}
- measured_paths = []
- for path in list(candidate_set):
- if cost + cost_needed_for_one_path > C_budget:
- continue # 予算が入らない経路はこのラウンドでは測らない
- # 実測
- p, used = network.benchmark_path(path, bounces, sample_times)
- cost += int(used)
- # 忠実度推定を更新(既存式)
- fidelity = p + (1 - p) / 2.0
- estimated_fidelities[path] = fidelity
- p_s[path] = p
- measured_paths.append(path)
- # 詳細集計
- alloc_by_path[int(path)] = alloc_by_path.get(int(path), 0) + int(used)
- est_fid_by_path[int(path)] = float(fidelity)
- # このラウンドで 1 本も測れなかったら終了
- if not p_s:
- break
- # 連続削除(幅 2^{-s})
- p_max = max(p_s.values())
- new_candidate_set = []
- for path in measured_paths:
- if p_s[path] + 2 ** (-s) > p_max - 2 ** (-s):
- new_candidate_set.append(path)
- # 全消し回避:空になったら据え置き
- candidate_set = new_candidate_set or candidate_set
- # 1 本も推定できなかった場合
- if not estimated_fidelities:
- if return_details:
- return False, int(cost), None, alloc_by_path, est_fid_by_path
- return False, int(cost), None
- # 最良推定パスと正解判定
- best_path = max(estimated_fidelities, key=estimated_fidelities.get)
- best_path_fidelity = estimated_fidelities[best_path]
- correctness = (best_path == getattr(network, "best_path", None))
- if return_details:
- return bool(correctness), int(cost), best_path_fidelity, alloc_by_path, est_fid_by_path
- return bool(correctness), int(cost), best_path_fidelity
- # 互換用エイリアス(古い呼び名を使っているコード向け)
- lonline_network_benchmarking_with_budget = lonline_network_benchmarking
|