
Research code implementing Dynamical Low-Rank Nash Equilibrium computation for stochastic ICS security games between advanced persistent threats and moving target defenses.
Dynamical Low-Rank Equilibrium Computation for Stochastic Games between Advanced Persistent Threats and Moving Target Defense
Regular Paper, under review at Automatica (resubmitted October 2026)
This repository contains the implementation and experimental validation of the Dynamical Low-Rank Nash Equilibrium (DLR-NE) algorithm for computing Nash equilibria in high-dimensional Industrial Control System (ICS) security games.
The strategic interaction between Advanced Persistent Threats (APTs) and Moving Target Defenses (MTDs) is modeled as a two-player zero-sum stochastic game over a continuous state space of dimension $n \sim 10^3$–$10^4$. The key insight is that the physical low-rank coupling of attack and defense channels (a structural property inherent to ICS network topology) enables tractable low-rank neural-network approximation of the equilibrium value function, reducing per-iteration complexity from $\mathcal{O}(n^2)$ to $\mathcal{O}(nr^2)$, a $\Theta(n/r^2)$ speedup.
| Theorem | Statement | Experiment |
|---|---|---|
| Theorem 1 | Global low-rank approximation error bound for the optimal value function $V^*$ | Exp. 1 |
| Theorem 2 | Geometric convergence of DLR-NE with explicit steady-state error decomposition | Exp. 2 |
| Theorem 3 | Per-iteration complexity $\mathcal{O}(nr^2)$ and speedup $\Theta(n/r^2)$ over full-rank baselines | Exp. 3 |
| Corollary 1 | Game-theoretic robustness: equilibrium sensitivity is controlled by the condition number $\kappa(S)$ | Exp. 4 |
dlr-ne-ics-security/
├── src/ # Core algorithmic modules
│ ├── environment.py # Synthetic nonlinear power-system dynamics
│ ├── networks.py # Low-rank and full-rank neural networks
│ ├── bellman.py # Bellman operator and greedy Nash policy extractor
│ ├── dlra_vi.py # Algorithm 1: DLR-NE
│ └── utils.py # FLOPs accounting, EYM error, metrics
│
├── experiments/ # Experimental scripts (one per theorem)
│ ├── exp01_truncation_error.py # Theorem 1: Low-rank truncation error
│ ├── exp02_convergence.py # Theorem 2: Convergence of DLR-NE
│ ├── exp03_complexity.py # Theorem 3: Computational complexity
│ ├── exp04_robustness.py # Corollary 1: Game-theoretic robustness
│ ├── exp05_tradeoff.py # Compression-accuracy Pareto frontier
│ └── exp06_ablation.py # Ablation: necessity of basis augmentation
│
├── data/ # Generated datasets (excluded from git)
├── results/ # Figures and logs (excluded from git)
├── notebooks/ # Prototyping and visualization
├── requirements.txt # Python dependencies
└── README.md # This file
# Clone the repository
git clone https://github.com/tz98lab/dlr-ne-ics-security.git
cd dlr-ne-ics-security
# Create a virtual environment (recommended)
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
numpy>=1.24.0
scipy>=1.10.0
matplotlib>=3.7.0
torch>=2.0.0
All experiments are self-contained and can be run independently. Each script generates figures in the results/figures/ directory.
Note on Scale: The default configurations use $n=200$ for rapid demonstration. To reproduce the full-scale results reported in the paper ($n \sim 10^3$), modify the
PowerSystemConfigat the top of each script (see inline comments).
Validates that the truncation error $|V_{\mathrm{full}} - \widehat{V}r|\infty$ decays with rank $r$ and aligns with the Eckart-Young-Mirsky theoretical prediction.
python experiments/exp01_truncation_error.py
Output: results/figures/exp1_truncation_error.png
Expected: Measured error (blue circles) tracks the theoretical bound $L_\phi |w_{\mathrm{out}}^*|2 R{\mathcal{X}} \epsilon_{\mathrm{EYM}}(r)$ (purple dashed line); singular-value spectrum shows rapid decay.
Validates geometric convergence with rate $\gamma$ and the explicit steady-state error neighborhood $\varepsilon_{\mathrm{total}}/(1-\gamma)$.
python experiments/exp02_convergence.py
Output: results/figures/exp2_convergence.png
Expected: Error curves first decay along the $\gamma^k$ envelope, then bend into a plateau dominated by the inner-loop budget $s^$ (halving $s^$ costs a factor of $\approx 20$ in accuracy); the batch size $N_b$ has a comparatively negligible effect.
Validates the $\mathcal{O}(nr^2)$ per-iteration complexity and the $\Theta(n/r^2)$ speedup over full-rank FC-NN baselines.
python experiments/exp03_complexity.py
Output: results/figures/exp3_complexity.png
Expected: DLR-NE scales linearly in $n$ (slope 1 in log-log), FC-NN scales quadratically (slope 2); measured FLOPs speedup $\approx 12\times$ (wall-clock $\approx 4\times$) at $n=2000, r=10$.
Validates the linear relation $|V(\cdot;\pi_D,\pi_A) - V(\cdot;\tilde{\pi}D,\pi_A)|\infty \le L_\kappa |\Delta S|F$ with a finite, $\beta$-tunable sensitivity constant $L\kappa$ certified by the condition number $\kappa(S)$ (an a priori worst-case certificate).
python experiments/exp04_robustness.py
Output: results/figures/exp4_robustness.png
Expected: Value deviation is linear in $|\Delta S|_F$ with slope decreasing in spectral regularization weight $\beta$; $\kappa(S)$ is compressed by increasing $\beta$.
Explores the Pareto frontier between compression ratio and equilibrium policy utility.
python experiments/exp05_tradeoff.py
Output: results/figures/exp5_tradeoff.png
Expected: Sweet spot at $r = 5$: $\approx 94%$ parameter compression at $\approx 2.3%$ utility loss.
Compares three variants: (i) full Algorithm 1, (ii) fixed basis, (iii) no retraction.
python experiments/exp06_ablation.py
Output: results/figures/exp6_ablation.png
Expected: Fixed-basis variant stagnates; no-retraction variant is unstable; full Algorithm 1 achieves stable decay at controlled cost.
All random seeds are fixed and reported. Each experiment script is self-contained and can be executed on a standard laptop (CPU-only) for the default $n=200$ configuration. Full-scale experiments ($n \sim 10^3$) were conducted on an Intel Xeon Gold 6248R with an NVIDIA A100 40GB GPU (used only for accelerating full-rank baselines).
Key hyperparameters:
See src/utils.py and individual experiment scripts for complete parameter lists.
If you use this code, please cite: