Contention¶
This is the result the suite exists to produce.
Running one application at a time, an arbiter almost never has to choose — there is
one pending demand and every policy agrees. Scheduling only matters under
contention: several tenants competing for a link whose entanglement supply is
below aggregate demand. qnetbench contention sets that up and shows that the best
policy depends on what the workload is made of.
$ qnetbench contention
policy deadline_heavy fidelity_heavy
----------------------------------------------------
fifo 0.800 0.867
fidelity_first 0.800 0.933*
edf 0.817* 0.900
winner edf fidelity_first
RANKING INVERSION — the best policy flips across workloads (deadline_heavy: edf → fidelity_heavy: fidelity_first).
Single-workload evaluation would have crowned one policy and been wrong on the other.
EDF wins on deadline-heavy traffic and is beaten on fidelity-heavy traffic;
fidelity_first does exactly the opposite. A paper that evaluated on either
workload alone would have crowned one policy and been wrong about the other. That
inversion is the argument that single-workload evaluation produces unreliable
rankings — and it is why a characterized benchmark suite is worth building.
How the experiment works¶
Tenants are parameterized by real applications, not by invented parameters.
app_profile runs the application, reads its actual demand contract out of the
trace, and measures its actual inter-arrival pattern:
from qnetbench.contention import app_profile
tenant = app_profile("distributed_gate", n_requests=12, interval=0.03)
tenant.min_fidelity, tenant.budget, tenant.pattern[:3]
The arrival pattern is normalised to mean 1.0 before being replayed at interval.
That removes the application's absolute rate — a property of the backend's link
model, not of the workload — while preserving the shape of its demand process.
Two mixes built this way can differ in burstiness at identical aggregate load,
which is what makes burstiness separately testable.
The simulation itself is a shared link producing one pair per service tick. At each tick the policy ranks the pending queue and one request is served. A served request's delivered fidelity decays with how long it waited, because the memory holding its pair decoheres. So:
- a high-
min_fidelitydemand must be served promptly or it fails its contract; - a deadline demand must be served before its deadline or it expires unserved.
Those two pressures pull in different directions. Which one a policy is good at decides which mix it wins — hence the inversion.
The two mixes¶
| Mix | Tenants |
|---|---|
deadline_heavy |
4 × distributed_gate + 1 × qkd |
fidelity_heavy |
2 × bqc + 2 × chsh + 1 × qkd |
At the default operating point: capacity 160 pairs/s against five tenants at one request per 30 ms (167 req/s of aggregate demand) — moderate overload — with link fidelity 0.99 and coherence time 0.25 s.
Give tenants their own arrivals
default_mixes(seed=None) keeps the synchronised model: every tenant of an
application replays the same measured arrival sequence starting at t=0, so
four distributed_gate tenants are really one stream counted four times. Pass
an integer seed to give each tenant its own arrival realisation and start
phase — which is what "five tenants" ought to mean, and which turns a single
deterministic realisation into a distribution over arrival patterns.
Driving it yourself¶
from qnetbench.contention import (
app_profile, default_mixes, ranking_experiment, render_experiment,
has_inversion, winning_policies,
)
mixes = default_mixes(seed=7) # independent arrivals per tenant
exp = ranking_experiment(mixes, capacity=160.0, link_fidelity=0.99, coherence_time=0.25)
print(render_experiment(exp))
print(has_inversion(exp)) # True if the winner differs across mixes
print(winning_policies(exp["deadline_heavy"])) # every policy tied at the top
Use winning_policies rather than best_policy wherever a tie would be reported
as a result: several operating points have two or three policies on exactly the
same score, and an argmax silently turns that into a winner.
Your own mix¶
mix = {
"my_workload": (
[app_profile("bqc", 100, 0.03, trace_seed=s) for s in range(3)]
+ [app_profile("distillation", 100, 0.03, trace_seed=s) for s in range(2)]
),
}
print(render_experiment(ranking_experiment(
mix, capacity=160.0, link_fidelity=0.99, coherence_time=0.25)))
Isolating burstiness¶
burstiness_mixes builds two mixes that differ in nothing but arrival shape —
same application, same contracts, same tenant count, same mean rate; one replays
each tenant's measured inter-arrival pattern, the other issues on a perfectly
regular cadence:
from qnetbench.contention import burstiness_mixes, ranking_experiment, render_experiment
exp = ranking_experiment(
burstiness_mixes("heralded_teleport", count=5, seed=1),
capacity=160.0, link_fidelity=0.99, coherence_time=0.25,
)
print(render_experiment(exp))
This isolates the burstiness axis of the characterization, which a fixed-cadence arrival model simply cannot express — under it every tenant is smooth, regardless of how the application actually behaves.
Honest scope¶
The inversion requires the two workloads to place genuinely opposed demands on the link; it is not a claim that any two mixes will disagree. What the experiment supports is the narrower and more useful claim: workload composition determines which policy wins, so a study that evaluates on one workload has not established a ranking.