# matrix.yaml — Experiment matrix definition
#
# This file defines the ablation scenarios run by `make matrix`.
# It is separate from config.yaml (runtime config) so that:
# - config.yaml reflects exactly what a single direct run does
# - matrix.yaml captures experiment variations without polluting runtime config
#
# The matrix runner deep-merges each scenario's `overrides` onto config.yaml
# to produce a fully resolved per-run config. Processes never read this file
# directly — they only ever see the merged result via FSL_CONFIG_PATH.
experiment_matrix:
# 16 scenarios × 3 seeds = 48 runs
# (No-latency includes S0-S3; Low/high include S0-S5)
# Latency ranges calibrated against Mao et al. 2017 (MEC survey):
# None (~0ms): WiFi/LAN edge deployment (profiler off)
# Low (~8ms): 4G cellular MEC link
# High (~50ms): NB-IoT / wide-area IoT
# Scheduler thresholds (EMA latency): float16@4ms, int8@10ms (3-tier: float32 / float16 / int8)
# Strategy axes (clean ablation):
# S0: float32, rho=1, no scheduler (baseline)
# S1: float16, rho=1, no scheduler (compression only - tier 1)
# S2: int8, rho=1, no scheduler (compression only - tier 2)
# S3: float32, rho=3, no scheduler (sync interval only)
# S4: float32, rho=1, adaptive scheduler (compression only, rho fixed)
# S5: float32, rho=dynamic, adaptive scheduler (joint adaptation: compression + rho)
seeds: [42 ,52 ,62]
runner:
backend: native
server_device: cpu
client_device: cpu
eval_device: cpu
startup_timeout: 120
summary_csv: results/matrix_summary.csv
scenarios:
# ── No latency (profiler off — clean baseline, no floor interference) ─
- id: "N01"
description: "No latency | S0 baseline (float32, rho=1, no scheduler)"
overrides:
profiler:
enabled: false
scheduler:
enabled: false
compression:
mode: float32
federated:
rho: 1
- id: "N02"
description: "No latency | S1 float16 compression only"
overrides:
profiler:
enabled: false
scheduler:
enabled: false
compression:
mode: float16
federated:
rho: 1
- id: "N03"
description: "No latency | S2 int8 compression only"
overrides:
profiler:
enabled: false
scheduler:
enabled: false
compression:
mode: int8
federated:
rho: 1
- id: "N04"
description: "No latency | S3 sync interval only (float32, rho=3)"
overrides:
profiler:
enabled: false
scheduler:
enabled: false
compression:
mode: float32
federated:
rho: 3
# ── Low latency (~8ms base) ──────────────────────────────────────────
- id: "L05"
description: "Low latency | S0 baseline (float32, rho=1, no scheduler)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 8.0
# offsets span float16 zone (8-9ms) to int8 zone (10-14ms)
# clients 1-4: ~8-9ms → float16; clients 5-11: ~10-14ms → int8
client_offsets_ms: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0]
jitter_ms: 1.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: float32
federated:
rho: 1
- id: "L06"
description: "Low latency | S1 float16 compression only"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 8.0
client_offsets_ms: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0]
jitter_ms: 1.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: float16
federated:
rho: 1
- id: "L07"
description: "Low latency | S2 int8 compression only"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 8.0
client_offsets_ms: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0]
jitter_ms: 1.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: int8
federated:
rho: 1
- id: "L08"
description: "Low latency | S3 sync interval only (float32, rho=3)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 8.0
client_offsets_ms: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0]
jitter_ms: 1.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: float32
federated:
rho: 3
- id: "L09"
description: "Low latency | S4 adaptive scheduler (compression only, rho fixed)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 8.0
client_offsets_ms: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0]
jitter_ms: 1.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: true
rho_step: 0
compression:
mode: float32
federated:
rho: 1
- id: "L10"
description: "Low latency | S5 joint adaptive scheduler (compression + rho, rho_step=1, bounded-staleness FedAvg, max_staleness=3)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 8.0
client_offsets_ms: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0]
jitter_ms: 1.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: true
rho_step: 1
compression:
mode: float32
federated:
rho: 1
max_staleness: 3
# ── High latency (~50ms base) ────────────────────────────────────────
- id: "H11"
description: "High latency | S0 baseline (float32, rho=1, no scheduler)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 50.0
# all clients 50-80ms → far exceed int8 threshold (10ms), scheduler assigns int8
client_offsets_ms: [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 15.0, 18.0, 22.0, 26.0, 30.0]
jitter_ms: 5.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: float32
federated:
rho: 1
- id: "H12"
description: "High latency | S1 float16 compression only"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 50.0
client_offsets_ms: [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 15.0, 18.0, 22.0, 26.0, 30.0]
jitter_ms: 5.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: float16
federated:
rho: 1
- id: "H13"
description: "High latency | S2 int8 compression only"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 50.0
client_offsets_ms: [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 15.0, 18.0, 22.0, 26.0, 30.0]
jitter_ms: 5.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: int8
federated:
rho: 1
- id: "H14"
description: "High latency | S3 sync interval only (float32, rho=3)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 50.0
client_offsets_ms: [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 15.0, 18.0, 22.0, 26.0, 30.0]
jitter_ms: 5.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: false
compression:
mode: float32
federated:
rho: 3
- id: "H15"
description: "High latency | S4 adaptive scheduler (compression only, rho fixed)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 50.0
client_offsets_ms: [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 15.0, 18.0, 22.0, 26.0, 30.0]
jitter_ms: 5.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: true
rho_step: 0
compression:
mode: float32
federated:
rho: 1
- id: "H16"
description: "High latency | S5 joint adaptive scheduler (compression + rho, rho_step=1, bounded-staleness FedAvg, max_staleness=3)"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 50.0
client_offsets_ms: [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 15.0, 18.0, 22.0, 26.0, 30.0]
jitter_ms: 5.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: true
rho_step: 1
compression:
mode: float32
federated:
rho: 1
max_staleness: 3
# ── Mixed latency (heterogeneous clients) ───────────────────────────
- id: "M17"
description: "Mixed latency | 4 no-lat + 4 low-lat + 3 high-lat | joint adaptive + bounded-staleness FedAvg, max_staleness=3"
overrides:
profiler:
enabled: true
latency_generator:
base_latency_ms: 0.0
client_offsets_ms: [0.0, 0.0, 0.0, 0.0, 8.0, 8.0, 8.0, 8.0, 50.0, 50.0, 50.0]
jitter_ms: 2.0
burst_every_steps: 0
burst_latency_ms: 0.0
scheduler:
enabled: true
rho_step: 1
compression:
mode: float32
federated:
rho: 1
max_staleness: 3