# 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
