Source code for tabench.metrics.so
"""System-optimum metrics: certified SO gap, price of anarchy, first-best tolls.
All quantities are recomputed from ``(scenario, link_flows)`` (P1). The
marginal social cost used throughout is ``t(v) + v t'(v)``, computed directly
from the network's cost model — no transformed network needed on the
certification side.
"""
from __future__ import annotations
import dataclasses
import numpy as np
from ..core.scenario import Network, Scenario
from ..models.so import marginal_network
__all__ = ["marginal_costs", "price_of_anarchy", "marginal_cost_tolls", "tolled_network"]
[docs]
def marginal_costs(network: Network, link_flows: np.ndarray) -> np.ndarray:
"""Marginal social cost t(v) + v t'(v) per link at the given flows.
Computed via the transformed network's closed form (b -> b(1+p)), which
is exact everywhere — the naive ``t + v * t'`` product is unreliable for
0 < p < 1 at subnormal flows, where the derivative clamps to float max.
"""
return marginal_network(network).link_cost(link_flows)
[docs]
def price_of_anarchy(
scenario: Scenario, ue_flows: np.ndarray, so_flows: np.ndarray
) -> float:
"""PoA = TSTT(UE flows) / TSTT(SO flows), recomputed from both flow vectors.
Roughgarden & Tardos (2002): >= 1 always; <= 4/3 for affine latencies.
This is an experiment-level report comparing two certified solutions,
not a per-model metric.
"""
net = scenario.network
ue = np.maximum(np.asarray(ue_flows, dtype=np.float64), 0.0)
so = np.maximum(np.asarray(so_flows, dtype=np.float64), 0.0)
tstt_ue = float(ue @ net.link_cost(ue))
tstt_so = float(so @ net.link_cost(so))
if tstt_so <= 0:
raise ValueError("TSTT at the SO flows must be positive to define PoA")
return tstt_ue / tstt_so
[docs]
def marginal_cost_tolls(network: Network, so_flows: np.ndarray) -> np.ndarray:
"""First-best (Pigouvian) tolls v* t'(v*) at system-optimal flows.
Charging these tolls makes the system optimum a user equilibrium
(marginal-cost pricing; Yang & Huang 1998).
"""
v = np.maximum(np.asarray(so_flows, dtype=np.float64), 0.0)
return v * network.link_cost_derivative(v)
[docs]
def tolled_network(network: Network, tolls: np.ndarray) -> Network:
"""The network with the given tolls added to the generalized cost.
Any pre-existing toll contribution is folded into a weight-1 toll column
(``fixed_cost_new = fixed_cost_old + tolls`` exactly), so the transform
composes and works regardless of the base network's toll convention.
"""
tolls = np.asarray(tolls, dtype=np.float64)
if tolls.shape != (network.n_links,):
raise ValueError(f"tolls shape {tolls.shape} != ({network.n_links},)")
if np.any(tolls < 0) or not np.all(np.isfinite(tolls)):
raise ValueError("tolls must be finite and nonnegative")
return dataclasses.replace(
network,
name=f"{network.name}+tolled",
toll=network.toll_weight * network.toll + tolls,
toll_weight=1.0,
)