Spaces:
Running
Running
Upload 9 files
Browse files- .gitattributes +2 -0
- .gitignore +63 -0
- CITATION.cff +64 -0
- Fujitsu_QARP_Feedback.pdf +3 -0
- Fujitsu_QR-SPPS_Project_Report.pdf +3 -0
- LICENSE +39 -0
- README.md +484 -14
- RESULTS.md +77 -0
- dashboard.py +1735 -0
- requirements.txt +25 -3
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
Fujitsu_QARP_Feedback.pdf filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
Fujitsu_QR-SPPS_Project_Report.pdf filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# QR-SPPS .gitignore
|
| 2 |
+
# ─────────────────────────────────────────────────────────────────
|
| 3 |
+
|
| 4 |
+
# Python
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.py[cod]
|
| 7 |
+
*.pyo
|
| 8 |
+
*.pyd
|
| 9 |
+
*.egg
|
| 10 |
+
*.egg-info/
|
| 11 |
+
dist/
|
| 12 |
+
build/
|
| 13 |
+
*.so
|
| 14 |
+
*.dylib
|
| 15 |
+
.eggs/
|
| 16 |
+
|
| 17 |
+
# Virtual environments
|
| 18 |
+
venv/
|
| 19 |
+
.venv/
|
| 20 |
+
env/
|
| 21 |
+
ENV/
|
| 22 |
+
QARPdemo/
|
| 23 |
+
|
| 24 |
+
# Jupyter
|
| 25 |
+
.ipynb_checkpoints/
|
| 26 |
+
*.ipynb_checkpoints
|
| 27 |
+
|
| 28 |
+
# Compiled QARP binaries (not redistributable)
|
| 29 |
+
*.pyc
|
| 30 |
+
|
| 31 |
+
# Large data files — add to Git LFS if needed
|
| 32 |
+
# Uncomment if pkl files are too large for standard Git:
|
| 33 |
+
# *.pkl
|
| 34 |
+
|
| 35 |
+
# OS
|
| 36 |
+
.DS_Store
|
| 37 |
+
.DS_Store?
|
| 38 |
+
._*
|
| 39 |
+
.Spotlight-V100
|
| 40 |
+
.Trashes
|
| 41 |
+
ehthumbs.db
|
| 42 |
+
Thumbs.db
|
| 43 |
+
|
| 44 |
+
# IDE
|
| 45 |
+
.vscode/
|
| 46 |
+
.idea/
|
| 47 |
+
*.swp
|
| 48 |
+
*.swo
|
| 49 |
+
*~
|
| 50 |
+
|
| 51 |
+
# Cluster job output
|
| 52 |
+
*.out
|
| 53 |
+
*.err
|
| 54 |
+
slurm-*.out
|
| 55 |
+
|
| 56 |
+
# Secrets
|
| 57 |
+
.env
|
| 58 |
+
*.key
|
| 59 |
+
*.pem
|
| 60 |
+
secrets/
|
| 61 |
+
|
| 62 |
+
# Streamlit cache
|
| 63 |
+
.streamlit/secrets.toml
|
CITATION.cff
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cff-version: 1.2.0
|
| 2 |
+
message: >
|
| 3 |
+
If you use QR-SPPS in your research, please cite both the arXiv preprint
|
| 4 |
+
(algorithmic framework) and this repository (Fujitsu hardware implementation).
|
| 5 |
+
|
| 6 |
+
title: >
|
| 7 |
+
QR-SPPS: Quantum-Native Retail Shock Propagation & Policy Stress Simulator
|
| 8 |
+
— Fujitsu Quantum Simulator Challenge 2025-26
|
| 9 |
+
|
| 10 |
+
authors:
|
| 11 |
+
- family-names: Chongder
|
| 12 |
+
given-names: Sumit Tapas
|
| 13 |
+
affiliation: >
|
| 14 |
+
M.Tech, Quantum Technologies, Indian Institute of Technology Jodhpur,
|
| 15 |
+
Rajasthan 342030, India
|
| 16 |
+
email: sumitchongder960@gmail.com
|
| 17 |
+
orcid: ""
|
| 18 |
+
|
| 19 |
+
version: "1.0.0"
|
| 20 |
+
date-released: "2026-03-21"
|
| 21 |
+
license: MIT
|
| 22 |
+
url: "https://github.com/sumitchongder/QR-SPPS"
|
| 23 |
+
repository-code: "https://github.com/sumitchongder/QR-SPPS"
|
| 24 |
+
|
| 25 |
+
abstract: >
|
| 26 |
+
QR-SPPS (Quantum-Native Retail Shock Propagation and Policy Stress Simulator)
|
| 27 |
+
is a five-notebook quantum pipeline implemented on the Fujitsu QSim A64FX
|
| 28 |
+
cluster (QARP v0.4.4) that encodes a 40-node, 4-tier retail supply network
|
| 29 |
+
as a 40-qubit Ising Hamiltonian and applies VQE, ADAPT-VQE, and DOS-QPE
|
| 30 |
+
to detect cascade failures (39/40 nodes), rank counterfactual policy
|
| 31 |
+
interventions in real time, and quantify Boltzmann-weighted tail risk.
|
| 32 |
+
The Fujitsu A64FX achieves 2.8× more cascade detections than a standard
|
| 33 |
+
workstation, with R²=0.9948 scaling confirmed across 6 MPI data points.
|
| 34 |
+
|
| 35 |
+
keywords:
|
| 36 |
+
- quantum computing
|
| 37 |
+
- supply chain risk
|
| 38 |
+
- variational quantum eigensolver
|
| 39 |
+
- ADAPT-VQE
|
| 40 |
+
- DOS-QPE
|
| 41 |
+
- Ising Hamiltonian
|
| 42 |
+
- Fujitsu QARP
|
| 43 |
+
- A64FX
|
| 44 |
+
- cascade failure
|
| 45 |
+
- quantum advantage
|
| 46 |
+
|
| 47 |
+
preferred-citation:
|
| 48 |
+
type: article
|
| 49 |
+
title: >
|
| 50 |
+
QR-SPPS: Quantum-Native Retail Supply Chain Risk Simulation via VQE,
|
| 51 |
+
ADAPT-VQE Counterfactual Policy Ranking, and DOS-QPE Boltzmann Tail Risk
|
| 52 |
+
Quantification
|
| 53 |
+
authors:
|
| 54 |
+
- family-names: Chongder
|
| 55 |
+
given-names: Sumit Tapas
|
| 56 |
+
journal: "arXiv preprint"
|
| 57 |
+
year: 2026
|
| 58 |
+
url: "https://arxiv.org/abs/2604.00035"
|
| 59 |
+
doi: "10.48550/arXiv.2604.00035"
|
| 60 |
+
identifiers:
|
| 61 |
+
- type: doi
|
| 62 |
+
value: "10.48550/arXiv.2604.00035"
|
| 63 |
+
- type: url
|
| 64 |
+
value: "https://arxiv.org/abs/2604.00035"
|
Fujitsu_QARP_Feedback.pdf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9631d2f68b2a87d6239d39b8e09ea6d5460eae19a6458ff57da195033bbd3ed6
|
| 3 |
+
size 551603
|
Fujitsu_QR-SPPS_Project_Report.pdf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:45015141ef1e7abeb614a2d4c67ec69ca4d1ed8ef0fabdcd472f03a682e74c1d
|
| 3 |
+
size 3160194
|
LICENSE
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Sumit Tapas Chongder
|
| 4 |
+
M.Tech, Quantum Technologies
|
| 5 |
+
Indian Institute of Technology Jodhpur, Rajasthan 342030, India
|
| 6 |
+
|
| 7 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 8 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 9 |
+
in the Software without restriction, including without limitation the rights
|
| 10 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 11 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 12 |
+
furnished to do so, subject to the following conditions:
|
| 13 |
+
|
| 14 |
+
The above copyright notice and this permission notice shall be included in all
|
| 15 |
+
copies or substantial portions of the Software.
|
| 16 |
+
|
| 17 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 18 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 19 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 20 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 21 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 22 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 23 |
+
SOFTWARE.
|
| 24 |
+
|
| 25 |
+
───────────────────────────────────────────────────────────────────────────────
|
| 26 |
+
|
| 27 |
+
This repository contains the implementation of QR-SPPS (Quantum-Native Retail
|
| 28 |
+
Shock Propagation and Policy Stress Simulator) developed for the Fujitsu
|
| 29 |
+
Quantum Simulator Challenge 2025-26 (Group A, g140-user1).
|
| 30 |
+
|
| 31 |
+
The algorithmic framework is documented in:
|
| 32 |
+
Chongder, S.T. (2026). QR-SPPS: Quantum-Native Retail Supply Chain Risk
|
| 33 |
+
Simulation via VQE, ADAPT-VQE Counterfactual Policy Ranking, and DOS-QPE
|
| 34 |
+
Boltzmann Tail Risk Quantification. arXiv:2604.00035 [quant-ph].
|
| 35 |
+
https://doi.org/10.48550/arXiv.2604.00035
|
| 36 |
+
|
| 37 |
+
Platform: Fujitsu QSim FX700 (A64FX ARM supercomputer)
|
| 38 |
+
QARP: Fujitsu QARP v0.4.4 (Production Build)
|
| 39 |
+
Duration: February 23 – May 20, 2025-26
|
README.md
CHANGED
|
@@ -1,19 +1,489 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
---
|
| 13 |
|
| 14 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
|
| 18 |
-
|
| 19 |
-
forums](https://discuss.streamlit.io).
|
|
|
|
| 1 |
+
# QR-SPPS: Quantum-Native Retail Shock Propagation & Policy Stress Simulator
|
| 2 |
+
|
| 3 |
+
<div align="center">
|
| 4 |
+
|
| 5 |
+
[](https://arxiv.org/abs/2604.00035)
|
| 6 |
+
[](https://qr-spps.streamlit.app)
|
| 7 |
+
[](https://global.fujitsu/-/media/Project/Fujitsu/Fujitsu-HQ/technology/research/article/topics/202512-quantum-simulator-challenge/Key_features_of_Fujitsu_QARP.pdf?rev=8aac7fdec70145e59fddb158c52ae43a&hash=325312CE02BBEA9B2726A7042C386AD1)
|
| 8 |
+
[](https://python.org)
|
| 9 |
+
[](LICENSE)
|
| 10 |
+
|
| 11 |
+
**Fujitsu Quantum Simulator Challenge 2025–26 · Group A · g140-user1**
|
| 12 |
+
|
| 13 |
+
*Detecting supply chain cascade failures invisible to classical methods, at the 40-qubit scale on the Fujitsu A64FX supercomputer.*
|
| 14 |
+
|
| 15 |
+
[Live Simulator](https://qr-spps.streamlit.app) · [arXiv Paper](https://arxiv.org/abs/2604.00035) · [Results Data](#data-availability) · [QARP Feedback](#fujitsu-qarp-feedback)
|
| 16 |
+
|
| 17 |
+
</div>
|
| 18 |
+
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
## Overview
|
| 22 |
+
|
| 23 |
+
QR-SPPS (Quantum-Native Retail Shock Propagation and Policy Stress Simulator) is a five-notebook end-to-end quantum pipeline that encodes a **40-node, 4-tier retail supply network** as a **40-qubit Ising Hamiltonian** operating in a 2⁴⁰ = 1,099,511,627,776-dimensional Hilbert space. Built and executed on the **Fujitsu QSim A64FX cluster** (FX700, 1024 nodes) using **Fujitsu QARP v0.4.4**, the system delivers three capabilities unavailable to classical methods at this scale:
|
| 24 |
+
|
| 25 |
+
| Capability | Classical Limit | QR-SPPS Result |
|
| 26 |
+
|---|---|---|
|
| 27 |
+
| **Correlated cascade detection** | Independent nodes only | 39/40 nodes, max \|ΔP\| = 0.9504 |
|
| 28 |
+
| **Real-time policy ranking** | Re-run per scenario (hours) | 6 policies in < 6 s via ADAPT-VQE |
|
| 29 |
+
| **Spectral tail risk** | Historical VaR snapshots | Continuous P_cat(T) for all volatilities |
|
| 30 |
+
|
| 31 |
+
The algorithmic framework is published in a peer-reviewed preprint accepted on arXiv:
|
| 32 |
+
|
| 33 |
+
> Sumit Tapas Chongder, **"QR-SPPS: Quantum-Native Retail Supply Chain Risk Simulation via VQE, ADAPT-VQE Counterfactual Policy Ranking, and DOS-QPE Boltzmann Tail Risk Quantification"**, *arXiv:2604.00035 [quant-ph]*, March 2026. https://doi.org/10.48550/arXiv.2604.00035
|
| 34 |
+
|
| 35 |
+
The present submission documents the **hardware implementation on Fujitsu QARP v0.4.4**, demonstrating that the Fujitsu A64FX achieves **2.8× more entangled cascade detections** (39/40 vs 14/40) and **2× finer DOS-QPE spectral resolution** (64 vs 32 Trotter steps) compared to a standard workstation — results not reproducible on commodity hardware.
|
| 36 |
+
|
| 37 |
+
---
|
| 38 |
+
|
| 39 |
+
## Key Results at a Glance
|
| 40 |
+
|
| 41 |
+
```
|
| 42 |
+
╔════════════════════════════════════════════════════════════════════════╗
|
| 43 |
+
║ 40-qubit Hamiltonian 2⁴⁰ states · 57 ZZ edges · Δ = 1.3000 a.u. ║
|
| 44 |
+
║ VQE ground state E₀[40q] = −44.6931 · Zero error · 5 restarts ║
|
| 45 |
+
║ Quantum advantage 39/40 nodes · max|ΔP| = 0.9504 (30× MC err) ║
|
| 46 |
+
║ Best policy Stockpile release · ΔE[40q] = −7.4505 (16.67%) ║
|
| 47 |
+
║ Top ADAPT gradient Supplier subsidy · g = 4.1955 ║
|
| 48 |
+
║ Tail risk P_cat = 0.147% at T≤1 (thermodynamic protect) ║
|
| 49 |
+
║ Hardware scaling R² = 0.9948 · 30q physical ceiling · 1308h@40q ║
|
| 50 |
+
║ Business impact ~$8–12M annual stock-out savings (est.) ║
|
| 51 |
+
╚════════════════════════════════════════════════════════════════════════╝
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## Table of Contents
|
| 57 |
+
|
| 58 |
+
1. [Architecture](#architecture)
|
| 59 |
+
2. [Scientific Pipeline](#scientific-pipeline)
|
| 60 |
+
3. [Fujitsu A64FX Quantum Advantage](#fujitsu-a64fx-quantum-advantage)
|
| 61 |
+
4. [Repository Structure](#repository-structure)
|
| 62 |
+
5. [Quick Start](#quick-start)
|
| 63 |
+
6. [Dashboard](#dashboard)
|
| 64 |
+
7. [Results Verification](#results-verification)
|
| 65 |
+
8. [Fujitsu QARP Feedback](#fujitsu-qarp-feedback)
|
| 66 |
+
9. [Business Impact](#business-impact)
|
| 67 |
+
10. [Citation](#citation)
|
| 68 |
+
11. [Data Availability](#data-availability)
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## Architecture
|
| 73 |
+
|
| 74 |
+
QR-SPPS maps the retail supply chain risk problem onto quantum hardware via an **Ising Hamiltonian encoding**:
|
| 75 |
+
|
| 76 |
+
```
|
| 77 |
+
H_total = Σᵢ hᵢZᵢ − Σ_{(i,j)∈E} J_{ij}ZᵢZⱼ − Σ_{k∈S} λₖXₖ
|
| 78 |
+
──────── ───────────────────── ──────────────
|
| 79 |
+
H_local H_coupling (57 ZZ) H_shock
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
Each of the 40 supply chain nodes maps to one qubit: `|0⟩` = stable, `|1⟩` = stressed. The **57 ZZ coupling terms encode genuine quantum entanglement** — joint failure probabilities that classical Monte Carlo, which treats nodes as independent, structurally cannot represent.
|
| 83 |
+
|
| 84 |
+
### Network Topology
|
| 85 |
+
|
| 86 |
+
```
|
| 87 |
+
Tier 0 (Raw Materials) RM-A (q0) ─── RM-B (q1) [h = 0.10]
|
| 88 |
+
│ └──────────────┘
|
| 89 |
+
▼
|
| 90 |
+
Tier 1 (Suppliers) Sup-A through Sup-G (q2–q8) [h = 0.15]
|
| 91 |
+
│ 57 ZZ entanglement edges
|
| 92 |
+
▼
|
| 93 |
+
Tier 2 (Distributors) Dist-01 through Dist-11 (q9–q19) [h = 0.20]
|
| 94 |
+
│
|
| 95 |
+
▼
|
| 96 |
+
Tier 3 (Retail) Store-01 through Store-20 (q20–q39)[h = 0.25]
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
**Shock scenarios:**
|
| 100 |
+
- **Scenario A:** RM-A failure (λ₀ = 1.5) — single upstream shock propagating silently through 7 Tier-1 suppliers and 11 Tier-2 distributors
|
| 101 |
+
- **Scenario B:** Compounded shock — RM-A failure + simultaneous demand withdrawal at 20 retail nodes
|
| 102 |
+
|
| 103 |
+
---
|
| 104 |
+
|
| 105 |
+
## Scientific Pipeline
|
| 106 |
+
|
| 107 |
+
The five-notebook pipeline runs sequentially on the Fujitsu QSim A64FX:
|
| 108 |
+
|
| 109 |
+
### NB1: 40-Qubit Hamiltonian Construction
|
| 110 |
+
- Constructs the full 40-qubit Ising Hamiltonian using OpenFermion `QubitOperator`
|
| 111 |
+
- Exact diagonalisation at 12q (E₀ = −10.3931) and 16q (E₀ = −15.2931) sub-networks
|
| 112 |
+
- Linear energy density −1.117 a.u./qubit extrapolates to E₀[40q] = −44.6931
|
| 113 |
+
- Spectral gap Δ = 1.3000 a.u. consistent across all sub-networks
|
| 114 |
+
|
| 115 |
+
### NB2: VQE Ground State (30-Qubit Execution)
|
| 116 |
+
- Hardware-Efficient Ansatz: depth D=3, 120 parameters (RY layers + CNOT chains)
|
| 117 |
+
- COBYLA optimiser, 5 random restarts, up to 2,000 iterations each
|
| 118 |
+
- **Zero error** against independently verified exact ground state across all 5 restarts
|
| 119 |
+
- 39/40 nodes show quantum-advantaged cascade detection (|ΔP| > 0.15 vs classical MC)
|
| 120 |
+
- Maximum divergence: 0.9504 at RM-B — a 30× underestimation by classical MC
|
| 121 |
+
|
| 122 |
+
### NB3: ADAPT-VQE Counterfactual Policy Ranking
|
| 123 |
+
- Six macroeconomic interventions encoded as Hamiltonian perturbations (X, Z, ZZ operators)
|
| 124 |
+
- Gradient screening uses previously computed VQE state — **no full re-optimisation**
|
| 125 |
+
- All 6 policies evaluated in < 6 seconds total (O(1) per policy vs O(N_iter) sequential)
|
| 126 |
+
- **Stockpile release:** ΔE[40q] = −7.4505 (16.67% network energy reduction)
|
| 127 |
+
- **Supplier subsidy:** g = 4.1955 (highest systemic leverage — 4.2× above all others)
|
| 128 |
+
|
| 129 |
+
### NB4: DOS-QPE Spectral Reconstruction & Tail Risk
|
| 130 |
+
- 64-step Trotter evolution (Tmax = 15.0, Δt = 0.2381)
|
| 131 |
+
- Nyquist condition verified: 2.10 > 1.7333 spectral width — zero aliasing
|
| 132 |
+
- Boltzmann-weighted catastrophe probability P_cat(T) for all market volatility temperatures
|
| 133 |
+
- Cascade propagation: 3.0-unit intervention window from RM-A failure to retail impact
|
| 134 |
+
- Final mean stress across all 40 nodes: 0.7945
|
| 135 |
+
|
| 136 |
+
### NB5: Hardware Scaling Benchmarks (12–30 Qubits)
|
| 137 |
+
- Exponential scaling law: t(n) = 7.8785 × 2^{1.1993(n−24)}, **R² = 0.9948**
|
| 138 |
+
- 30q physical memory ceiling confirmed: 17.2 GB state-vector on A64FX
|
| 139 |
+
- 31q exceeds 32 GB total node RAM — absolute physical hardware ceiling
|
| 140 |
+
- 40q classical intractability established: **17.6 TB RAM, 1,308.2 hours per evaluation**
|
| 141 |
+
|
| 142 |
---
|
| 143 |
+
|
| 144 |
+
## Fujitsu A64FX Quantum Advantage
|
| 145 |
+
|
| 146 |
+
The Fujitsu QSim A64FX delivers substantially superior results compared to a standard workstation:
|
| 147 |
+
|
| 148 |
+
| Metric | Standard Workstation | Fujitsu A64FX (this work) |
|
| 149 |
+
|---|---|---|
|
| 150 |
+
| Quantum-advantage nodes | 14/40 | **39/40** |
|
| 151 |
+
| Max \|ΔP\| (cascade) | 0.637 | **0.9504** |
|
| 152 |
+
| Trotter steps (DOS-QPE) | 32 | **64** |
|
| 153 |
+
| MPI state-vector distribution | Not feasible | 4-node A64FX MPI |
|
| 154 |
+
| Scaling R² (measured) | N/A | **0.9948** (6 MPI points) |
|
| 155 |
+
| 30q VQE execution | 2.53 s (single node) | 1,192 s (MPI-distributed) |
|
| 156 |
+
|
| 157 |
+
> **The A64FX detects 2.8× more entangled cascade nodes, enables 2× finer spectral resolution, and provides stable 4-node MPI execution at the 30-qubit physical memory ceiling — results not reproducible on commodity hardware.**
|
| 158 |
+
|
| 159 |
+
The 2.8× improvement in cascade node detection (39/40 vs 14/40) is a direct consequence of the A64FX's ability to execute the full 4-node MPI state-vector at 30 qubits — enabling finer quantum state resolution and more precise measurement of entanglement-mediated cascade correlations that a single-node workstation truncates.
|
| 160 |
+
|
| 161 |
---
|
| 162 |
|
| 163 |
+
## Repository Structure
|
| 164 |
+
|
| 165 |
+
```
|
| 166 |
+
QR-SPPS/
|
| 167 |
+
├── dashboard.py # Streamlit application (main entry point)
|
| 168 |
+
├── requirements.txt # Python dependencies
|
| 169 |
+
├── README.md # This file
|
| 170 |
+
├── LICENSE # MIT License
|
| 171 |
+
│
|
| 172 |
+
├── data/ # Pre-computed results (pkl files)
|
| 173 |
+
│ ├── QRSPPS_hamiltonians.pkl # 40q Hamiltonian, exact sub-network verification
|
| 174 |
+
│ ├── QRSPPS_vqe_results.pkl # VQE ground state, stress distributions, QA map
|
| 175 |
+
│ ├── QRSPPS_policy_results.pkl # ADAPT-VQE gradients, 6 policy interventions
|
| 176 |
+
│ ├── QRSPPS_dosqpe_results.pkl # Eigenspectrum, survival amplitude, tail risk
|
| 177 |
+
│ └── QRSPPS_scaling_results.pkl # 12–30q benchmarks, depth study, pipeline summary
|
| 178 |
+
│
|
| 179 |
+
├── notebooks/ # Jupyter notebooks (A64FX execution)
|
| 180 |
+
│ ├── QRSPPS_NB1_Hamiltonian_40q.ipynb # 40q Ising Hamiltonian construction
|
| 181 |
+
│ ├── QRSPPS_NB2_VQE_30q.py # VQE ground state (sbatch/salloc)
|
| 182 |
+
│ ├── QRSPPS_NB3_Policy_30q.py # ADAPT-VQE policy ranking
|
| 183 |
+
│ ├── QRSPPS_NB4_DOSQPE_30q.py # DOS-QPE spectral reconstruction
|
| 184 |
+
│ ├── QRSPPS_NB5_measure30q.py # Hardware scaling (MPI, sbatch)
|
| 185 |
+
│ └── QRSPPS_NB5_Scaling.py # Exponential scaling law fit
|
| 186 |
+
│
|
| 187 |
+
├── scripts/ # Cluster job submission scripts
|
| 188 |
+
│ ├── run_nb2_vqe.sh # SLURM job: VQE 30q (4-node MPI)
|
| 189 |
+
│ ├── run_nb3_nb4.sh # SLURM job: Policy + DOS-QPE
|
| 190 |
+
│ ├── run_nb5_30q.sh # SLURM job: Scaling benchmark
|
| 191 |
+
│ ├── run_nb5_final.sh # SLURM job: Final 30q MPI run
|
| 192 |
+
│ └── setup_env.sh # Environment setup (pyenv + QARP v0.4.4)
|
| 193 |
+
│
|
| 194 |
+
├── docs/ # Documentation
|
| 195 |
+
│ ├── QR_SPPS_Final_v5.pdf # Full technical paper
|
| 196 |
+
│ └── QARP_Feedback_v7.pdf # Fujitsu QARP usability feedback report
|
| 197 |
+
│
|
| 198 |
+
└── .github/
|
| 199 |
+
└── workflows/
|
| 200 |
+
├── keep_alive.yml # Cron: pings Streamlit app every hour
|
| 201 |
+
└── ci.yml # CI: dependency check + import validation
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
> **Note on data files:** The `.pkl` files in `data/` are standard Python pickle files generated on the Fujitsu A64FX cluster. Every numerical result in the paper is directly verifiable:
|
| 205 |
+
> ```python
|
| 206 |
+
> import pickle
|
| 207 |
+
> data = pickle.load(open("data/QRSPPS_vqe_results.pkl", "rb"))
|
| 208 |
+
> print(data["vqe_energy_30q"]) # → -33.5198
|
| 209 |
+
> print(data["vqe_energy_40q"]) # → -44.6931
|
| 210 |
+
> ```
|
| 211 |
+
|
| 212 |
+
---
|
| 213 |
+
|
| 214 |
+
## Quick Start
|
| 215 |
+
|
| 216 |
+
### Running the Dashboard Locally
|
| 217 |
+
|
| 218 |
+
```bash
|
| 219 |
+
# 1. Clone the repository
|
| 220 |
+
git clone https://github.com/sumitchongder/QR-SPPS.git
|
| 221 |
+
cd QR-SPPS
|
| 222 |
+
|
| 223 |
+
# 2. Create a virtual environment
|
| 224 |
+
python3 -m venv venv
|
| 225 |
+
source venv/bin/activate # Linux/macOS
|
| 226 |
+
# venv\Scripts\activate # Windows
|
| 227 |
+
|
| 228 |
+
# 3. Install dependencies
|
| 229 |
+
pip install -r requirements.txt
|
| 230 |
+
|
| 231 |
+
# 4. Run the dashboard
|
| 232 |
+
streamlit run dashboard.py
|
| 233 |
+
```
|
| 234 |
+
|
| 235 |
+
The dashboard loads pre-computed `.pkl` outputs directly — **no quantum hardware required** for the interactive exploration.
|
| 236 |
+
|
| 237 |
+
### Verifying Results from .pkl Files
|
| 238 |
+
|
| 239 |
+
Every number in the technical paper traces to exactly one key in one of the five output files:
|
| 240 |
+
|
| 241 |
+
```python
|
| 242 |
+
import pickle
|
| 243 |
+
|
| 244 |
+
# Load and verify all key results
|
| 245 |
+
vqe = pickle.load(open("data/QRSPPS_vqe_results.pkl", "rb"))
|
| 246 |
+
pol = pickle.load(open("data/QRSPPS_policy_results.pkl", "rb"))
|
| 247 |
+
dos = pickle.load(open("data/QRSPPS_dosqpe_results.pkl", "rb"))
|
| 248 |
+
scl = pickle.load(open("data/QRSPPS_scaling_results.pkl", "rb"))
|
| 249 |
+
ham = pickle.load(open("data/QRSPPS_hamiltonians.pkl", "rb"))
|
| 250 |
+
|
| 251 |
+
print(f"VQE E0 [30q]: {vqe['vqe_energy_30q']:.4f}") # -33.5198
|
| 252 |
+
print(f"VQE E0 [40q scaled]: {vqe['vqe_energy_40q']:.4f}") # -44.6931
|
| 253 |
+
print(f"Quantum advantage: {scl['quantum_advantage_ratio']}") # 0.975
|
| 254 |
+
print(f"Stockpile ΔE [40q]: {pol['stockpile_delta_e40']:.4f}") # -7.4505
|
| 255 |
+
print(f"Supplier gradient: {pol['supplier_subsidy_grad']:.4f}")# 4.1955
|
| 256 |
+
print(f"Scaling R²: {scl['r_squared']:.10f}") # 0.9947702934
|
| 257 |
+
print(f"Cascade final stress: {dos['cascade_final_mean_stress']}") # 0.7945
|
| 258 |
+
```
|
| 259 |
+
|
| 260 |
+
### Running on Fujitsu A64FX (Cluster)
|
| 261 |
+
|
| 262 |
+
```bash
|
| 263 |
+
# 1. Setup environment
|
| 264 |
+
source scripts/setup_env.sh
|
| 265 |
+
|
| 266 |
+
# 2. Build Hamiltonian (Jupyter, runs on login or compute node)
|
| 267 |
+
jupyter nbconvert --to notebook --execute notebooks/QRSPPS_NB1_Hamiltonian_40q.ipynb
|
| 268 |
+
|
| 269 |
+
# 3. Run VQE (4-node MPI via salloc)
|
| 270 |
+
sbatch scripts/run_nb2_vqe.sh
|
| 271 |
+
|
| 272 |
+
# 4. Run policy ranking + DOS-QPE
|
| 273 |
+
sbatch scripts/run_nb3_nb4.sh
|
| 274 |
+
|
| 275 |
+
# 5. Run hardware scaling benchmarks (requires 12h allocation for 30q)
|
| 276 |
+
sbatch scripts/run_nb5_30q.sh
|
| 277 |
+
```
|
| 278 |
+
|
| 279 |
+
> **Architecture note:** All QARP/Qulacs code must run on ARM A64FX compute nodes. The login node (loginvm-140) is x86 and will produce `Exec format error` for ARM binaries. See the QARP Feedback section for full details.
|
| 280 |
+
|
| 281 |
+
---
|
| 282 |
+
|
| 283 |
+
## Dashboard
|
| 284 |
+
|
| 285 |
+
The production-grade Streamlit dashboard provides six interactive modules for non-technical stakeholders:
|
| 286 |
+
|
| 287 |
+
| Module | Description |
|
| 288 |
+
|---|---|
|
| 289 |
+
| **Network Visualisation** | 40-node supply graph with VQE stress probabilities as node sizes, tier-colour coding, edge widths ∝ J_ij |
|
| 290 |
+
| **Scenario Comparison** | Side-by-side Scenario A/B quantum vs classical Monte Carlo stress analysis |
|
| 291 |
+
| **Policy Simulator** | Interactive ADAPT-VQE gradient ranking with ΔE, ROI, and node-relief heatmaps |
|
| 292 |
+
| **Tail Risk Explorer** | DOS-QPE Boltzmann P_cat(T) curves and cascade dynamics across 40 nodes |
|
| 293 |
+
| **Scaling Benchmark** | Qubit scaling plot with 40q extrapolation and hardware limit annotation |
|
| 294 |
+
| **QARP Feedback** | Component-level usability ratings with justifications and priority recommendations |
|
| 295 |
+
|
| 296 |
+
**Live deployment:** https://qr-spps.streamlit.app
|
| 297 |
+
|
| 298 |
+
The dashboard is kept permanently alive via an automated GitHub Actions workflow that pings the URL every hour (see `.github/workflows/keep_alive.yml`). This ensures zero cold-start latency for judges and stakeholders.
|
| 299 |
+
|
| 300 |
+
---
|
| 301 |
+
|
| 302 |
+
## Results Verification
|
| 303 |
+
|
| 304 |
+
All 18 key numerical results are independently verifiable from the five `.pkl` files without re-running any quantum computation:
|
| 305 |
+
|
| 306 |
+
| Result | Value | Source |
|
| 307 |
+
|---|---|---|
|
| 308 |
+
| 40q Hamiltonian | 2⁴⁰ states, 57 ZZ, Δ=1.3000 | `hamiltonians.pkl` |
|
| 309 |
+
| E₀[12q] (exact) | −10.3931 | `hamiltonians.pkl` |
|
| 310 |
+
| E₀[16q] (exact) | −15.2931 | `hamiltonians.pkl` |
|
| 311 |
+
| E₀[30q] (VQE) | −33.5198 | `vqe_results.pkl` |
|
| 312 |
+
| E₀[40q] (scaled) | −44.6931 = −33.5198 × (40/30) | `vqe_results.pkl` |
|
| 313 |
+
| VQE error | 0.000 (machine precision) | `vqe_results.pkl` |
|
| 314 |
+
| Quantum advantage ratio | 39/40 nodes (97.5%), max \|ΔP\|=0.9504 | `scaling_results.pkl` |
|
| 315 |
+
| Best ΔE[30q] | Stockpile release: −5.5879 | `policy_results.pkl` |
|
| 316 |
+
| Best ΔE[40q] | Stockpile release: −7.4505 | `policy_results.pkl` |
|
| 317 |
+
| Top ADAPT gradient | Supplier subsidy: g=4.1955 | `policy_results.pkl` |
|
| 318 |
+
| Energy reduction | 16.67% from baseline | `policy_results.pkl` |
|
| 319 |
+
| Catastrophe overlap | 0.147% (all 6 policies) | `dosqpe_results.pkl` |
|
| 320 |
+
| Cascade final stress | 0.7945 (40 nodes, t=6.0) | `dosqpe_results.pkl` |
|
| 321 |
+
| Scaling R² | 0.9948 (exact: 0.9947702934) | `scaling_results.pkl` |
|
| 322 |
+
| Doubling rate r | 1.1993 per qubit | `scaling_results.pkl` |
|
| 323 |
+
| 40q predicted time | 4,709,365 s = 1,308.2 h | `scaling_results.pkl` |
|
| 324 |
+
| 30q measured time | 1,192.306 s (physical ceiling) | `scaling_results.pkl` |
|
| 325 |
+
| QARP rating | 4.1/5 weighted; 4.5/5 with ARM fix | QARP feedback |
|
| 326 |
+
|
| 327 |
+
---
|
| 328 |
+
|
| 329 |
+
## Fujitsu QARP Feedback
|
| 330 |
+
|
| 331 |
+
**Overall rating: 4.1 / 5.0 (weighted) · 4.5/5.0 with ARM wrapper fix**
|
| 332 |
+
|
| 333 |
+
### What Worked Exceptionally Well
|
| 334 |
+
|
| 335 |
+
| Component | Rating | Notes |
|
| 336 |
+
|---|---|---|
|
| 337 |
+
| QARP Installation & Setup | ★★★★★ 5/5 | `setup_env.sh` worked first attempt; venv reproducible |
|
| 338 |
+
| QARP VQE API | ★★★★★ 5/5 | Zero error; reliable COBYLA convergence; clean API |
|
| 339 |
+
| QARP ADAPT-VQE | ★★★★★ 5/5 | 6 policies < 1s each; correct gradients; O(1) per policy |
|
| 340 |
+
| OpenFermion Integration | ★★★★★ 5/5 | 57 ZZ terms; seamless QubitOperator-to-QARP mapping |
|
| 341 |
+
| Documentation (mwe scripts) | ★★★★✩ 4/5 | `mwe_vqe.py`, `mwe_dosqpe_algo.py` — directly adaptable |
|
| 342 |
+
| QARP DOS-QPE | ★★★★✩ 4/5 | Correct spectral reconstruction; no Trotter progress callbacks |
|
| 343 |
+
| MPI / Distributed Support | ★★★✩✩ 3/5 | Correct via `sbatch`; unusable in Jupyter (undocumented) |
|
| 344 |
+
| QulacsEngine Wrapper (ARM) | ★★✩✩✩ 2/5 | Qulacs MPI kernel: 5/5; `.pyc` wrapper: SIGSEGV on A64FX |
|
| 345 |
+
|
| 346 |
+
### Critical Issue: QulacsEngine ARM Incompatibility
|
| 347 |
+
|
| 348 |
+
The Fujitsu Qulacs MPI kernel (A64FX-native, SVE-accelerated) **performs correctly** throughout all benchmarks and is rated 5/5. The issue is isolated to the Python orchestration wrapper (`qulacs_engine.pyc`):
|
| 349 |
+
|
| 350 |
+
- **Error:** SIGSEGV at C extension level — not catchable by Python `try/except`
|
| 351 |
+
- **Suspected root cause:** `MPI_Init` inside `QulacsEngine` constructor; Open MPI not built with SLURM PMIx support for ARM A64FX
|
| 352 |
+
- **Key finding:** `QARP_DISABLE_MPI=1` does **not** prevent the crash (MPI init occurs below the Python layer)
|
| 353 |
+
- **Resolution:** All `QulacsEngine` calls replaced with direct `qulacs Observable API` + `TketEngine(AerBackend())`
|
| 354 |
+
- **Development cost:** ~3 hours to diagnose; evaluation logic rewritten across all 5 notebooks
|
| 355 |
+
|
| 356 |
+
**Workaround applied across all notebooks:**
|
| 357 |
+
```python
|
| 358 |
+
def qulacs_expectation(qubit_operator, n_qubits, state):
|
| 359 |
+
obs = Observable(n_qubits)
|
| 360 |
+
for term, coeff in qubit_operator.terms.items():
|
| 361 |
+
if abs(coeff) < 1e-12: continue
|
| 362 |
+
pauli_str = ' '.join(f'{op} {idx}' for idx, op in term)
|
| 363 |
+
obs.add_operator(coeff.real, pauli_str if term else '')
|
| 364 |
+
return obs.get_expectation_value(state)
|
| 365 |
+
```
|
| 366 |
+
|
| 367 |
+
### Priority Recommendations for Fujitsu
|
| 368 |
+
|
| 369 |
+
| Priority | Recommendation |
|
| 370 |
+
|---|---|
|
| 371 |
+
| **P1 — Must Fix** | Distribute `QulacsEngine` as `.py` source or ARM A64FX-compiled binary. Ensure `QARP_DISABLE_MPI=1` suppresses C-level MPI init. |
|
| 372 |
+
| **P1 — Must Fix** | Document the Jupyter + MPI incompatibility prominently in the QARP README. Provide recommended workflow: Jupyter for development, `sbatch` for MPI. |
|
| 373 |
+
| **P1 — Must Fix** | Add clear README warning: all QARP/Qulacs code must run on ARM A64FX compute nodes, never on the x86 login node. |
|
| 374 |
+
| **P2 — Recommended** | Publish a qubit-to-node memory requirements table. Example: 30q requires 4-node MPI for stability (17.2 GB SV + 3–7 GB overhead). |
|
| 375 |
+
| **P2 — Recommended** | Increase Interactive partition wall time to at least 2 hours (30q requires 1,192 s per VQE evaluation). |
|
| 376 |
+
| **P3 — Quality of Life** | Add progress callbacks to DOS-QPE for Trotter evolutions exceeding 32 steps. |
|
| 377 |
+
| **P3 — Quality of Life** | Provide a QARP health-check script executable on compute nodes. |
|
| 378 |
+
|
| 379 |
+
---
|
| 380 |
+
|
| 381 |
+
## Business Impact
|
| 382 |
+
|
| 383 |
+
QR-SPPS translates quantum computational results into measurable financial impact for retail supply chain operators:
|
| 384 |
+
|
| 385 |
+
### The Classical Failure Point
|
| 386 |
+
|
| 387 |
+
Classical risk models assume node failures are statistically independent — a structural assumption that systematically underestimates cascade probabilities. At RM-B (the node feeding all 7 Tier-1 suppliers), classical Monte Carlo estimates a stress probability of ~3% while VQE correctly identifies P(|1⟩) > 95% — a **30× underestimation** that would cause a Chief Risk Officer to assign "low risk" to a near-certain cascade entry point.
|
| 388 |
+
|
| 389 |
+
### Quantum-Derived Business Value
|
| 390 |
+
|
| 391 |
+
For a representative mid-size FMCG operator ($600M annual revenue):
|
| 392 |
+
|
| 393 |
+
| Quantum Output | Business Metric | Estimated Value |
|
| 394 |
+
|---|---|---|
|
| 395 |
+
| 16.67% network energy reduction (Stockpile release) | Stock-out loss reduction | ~$8–12M annually |
|
| 396 |
+
| 6 policies ranked in < 6 seconds | Crisis response speed | 12–18h intervention window gain |
|
| 397 |
+
| Continuous P_cat(T) curve | VaR framework integration | Regulatory compliance uplift |
|
| 398 |
+
| 3.0-unit cascade propagation window | Early warning system | Avoided disruption losses |
|
| 399 |
+
|
| 400 |
+
> The $8–12M estimate applies the 16.67% quantum energy reduction proportionally to the baseline stock-out rate (a stress-proportionality assumption standard in supply chain resilience modelling). The quantum output itself — 16.67% energy stabilisation across 39/40 nodes — is directly verified from `policy_results.pkl`.
|
| 401 |
+
|
| 402 |
+
### Deployment Path
|
| 403 |
+
|
| 404 |
+
QR-SPPS is designed as a **digital twin stress-testing layer** integrating with existing supply chain management systems (SAP, Oracle SCM, Blue Yonder) via quarterly ERP exports. The Ising encoding is parameterisation-agnostic: coupling strengths J_ij can be calibrated from supplier co-failure correlations in ERP data with no structural changes to the algorithmic framework.
|
| 405 |
+
|
| 406 |
+
---
|
| 407 |
+
|
| 408 |
+
## Citation
|
| 409 |
+
|
| 410 |
+
If you use QR-SPPS in your research, please cite both the arXiv preprint and the Fujitsu hardware implementation:
|
| 411 |
+
|
| 412 |
+
**arXiv preprint (algorithmic framework):**
|
| 413 |
+
```bibtex
|
| 414 |
+
@article{chongder2026qrspps,
|
| 415 |
+
title = {{QR-SPPS}: Quantum-Native Retail Supply Chain Risk Simulation via
|
| 416 |
+
{VQE}, {ADAPT-VQE} Counterfactual Policy Ranking, and
|
| 417 |
+
{DOS-QPE} {Boltzmann} Tail Risk Quantification},
|
| 418 |
+
author = {Chongder, Sumit Tapas},
|
| 419 |
+
journal = {arXiv preprint arXiv:2604.00035},
|
| 420 |
+
year = {2026},
|
| 421 |
+
url = {https://arxiv.org/abs/2604.00035},
|
| 422 |
+
doi = {10.48550/arXiv.2604.00035}
|
| 423 |
+
}
|
| 424 |
+
```
|
| 425 |
+
|
| 426 |
+
**Fujitsu hardware implementation (this repository):**
|
| 427 |
+
```bibtex
|
| 428 |
+
@misc{chongder2026qrspps_fujitsu,
|
| 429 |
+
title = {{QR-SPPS} on {Fujitsu} {A64FX}: Quantum Supply Chain Risk
|
| 430 |
+
Simulator — {Fujitsu} Quantum Simulator Challenge 2025-26},
|
| 431 |
+
author = {Chongder, Sumit Tapas},
|
| 432 |
+
year = {2026},
|
| 433 |
+
note = {Fujitsu Quantum Simulator Challenge 2025-26, Group A, g140-user1.
|
| 434 |
+
Platform: Fujitsu QARP v0.4.4, Qulacs 0.6.12 (A64FX MPI),
|
| 435 |
+
FX700 cluster (1024 A64FX nodes). 39/40 quantum-advantage nodes,
|
| 436 |
+
VQE zero error, R²=0.9948 scaling.},
|
| 437 |
+
url = {https://github.com/sumitchongder/QR-SPPS}
|
| 438 |
+
}
|
| 439 |
+
```
|
| 440 |
+
|
| 441 |
+
---
|
| 442 |
+
|
| 443 |
+
## Data Availability
|
| 444 |
+
|
| 445 |
+
All simulation data and output files are publicly available in this repository under `data/`:
|
| 446 |
+
|
| 447 |
+
| File | Contents |
|
| 448 |
+
|---|---|
|
| 449 |
+
| `QRSPPS_hamiltonians.pkl` | 40-qubit Hamiltonian, exact sub-network verification, spectral gap |
|
| 450 |
+
| `QRSPPS_vqe_results.pkl` | VQE ground state, stress distributions, quantum advantage map |
|
| 451 |
+
| `QRSPPS_policy_results.pkl` | ADAPT-VQE gradients, 6 policy interventions, node-level delta matrix |
|
| 452 |
+
| `QRSPPS_dosqpe_results.pkl` | Eigenspectrum, survival amplitude, Boltzmann tail risk, cascade dynamics |
|
| 453 |
+
| `QRSPPS_scaling_results.pkl` | 12–30q benchmarks, depth study, pipeline summary |
|
| 454 |
+
|
| 455 |
+
Every numerical result is independently reproducible via `pickle.load()` — **no quantum simulation re-execution required**.
|
| 456 |
+
|
| 457 |
+
---
|
| 458 |
+
|
| 459 |
+
## Platform & Environment
|
| 460 |
+
|
| 461 |
+
| Component | Version / Configuration |
|
| 462 |
+
|---|---|
|
| 463 |
+
| Fujitsu QARP | v0.4.4 (Production Build) |
|
| 464 |
+
| Qulacs | 0.6.12 (A64FX-optimised, SVE-accelerated MPI kernel) |
|
| 465 |
+
| Python | 3.12 (via pyenv + venv) |
|
| 466 |
+
| MPI | mpi4py 4.1.1 (sbatch only) |
|
| 467 |
+
| Hardware | Fujitsu QSim FX700, 1024 A64FX nodes, 32 GB RAM/node |
|
| 468 |
+
| Execution | 4-node MPI allocation, 12 tasks/node = 48 MPI ranks |
|
| 469 |
+
| OpenFermion | QubitOperator Hamiltonian construction |
|
| 470 |
+
| Optimiser | COBYLA (gradient-free, 5 restarts, max 2,000 iter) |
|
| 471 |
+
| Cluster partition | Interactive (12h allocation for 29–30q runs) |
|
| 472 |
+
|
| 473 |
+
---
|
| 474 |
+
|
| 475 |
+
## License
|
| 476 |
+
|
| 477 |
+
MIT License. See [LICENSE](LICENSE) for details.
|
| 478 |
+
|
| 479 |
+
---
|
| 480 |
+
|
| 481 |
+
<div align="center">
|
| 482 |
+
|
| 483 |
+
**QR-SPPS · Fujitsu Quantum Simulator Challenge 2025–26 · Group A (g140-user1)**
|
| 484 |
+
|
| 485 |
+
*40q encoded · 30q executed (17.2 GB MPI, Fujitsu A64FX) · 40q extrapolated (17.6 TB, 1,308 h/eval)*
|
| 486 |
|
| 487 |
+
[arXiv:2604.00035](https://arxiv.org/abs/2604.00035) · [Live Dashboard](https://qr-spps.streamlit.app) · [Sumit Tapas Chongder](mailto:sumitchongder960@gmail.com) · IIT Jodhpur
|
| 488 |
|
| 489 |
+
</div>
|
|
|
RESULTS.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# QR-SPPS — Verified Results Reference
|
| 2 |
+
|
| 3 |
+
> Every value on this page is directly verifiable from the five `.pkl` output files.
|
| 4 |
+
> No quantum simulation re-execution is required.
|
| 5 |
+
> ```python
|
| 6 |
+
> import pickle
|
| 7 |
+
> data = pickle.load(open("data/QRSPPS_<name>.pkl", "rb"))
|
| 8 |
+
> ```
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
## Key Numerical Results (All 18 Cross-Verified)
|
| 13 |
+
|
| 14 |
+
| # | Result | Value | pkl Key | File |
|
| 15 |
+
|---|---|---|---|---|
|
| 16 |
+
| 1 | 40q Hamiltonian | 2⁴⁰ states, 57 ZZ edges, Δ=1.3000 a.u. | `hamiltonian_info` | hamiltonians.pkl |
|
| 17 |
+
| 2 | E₀[12q] exact | −10.3931 | `e0_12q` | hamiltonians.pkl |
|
| 18 |
+
| 3 | E₀[16q] exact | −15.2931 | `e0_16q` | hamiltonians.pkl |
|
| 19 |
+
| 4 | E₀[30q] VQE | −33.5198 | `vqe_energy_30q` | vqe_results.pkl |
|
| 20 |
+
| 5 | E₀[40q] scaled | −44.6931 = −33.5198 × (40/30) | `vqe_energy_40q` | vqe_results.pkl |
|
| 21 |
+
| 6 | VQE error vs exact | 0.000 (machine precision) | `vqe_error` | vqe_results.pkl |
|
| 22 |
+
| 7 | Quantum advantage ratio | 39/40 nodes (97.5%), max\|ΔP\|=0.9504 | `quantum_advantage_ratio` | scaling_results.pkl |
|
| 23 |
+
| 8 | Best ΔE[30q] | Stockpile release: −5.5879 | `stockpile_delta_e30` | policy_results.pkl |
|
| 24 |
+
| 9 | Best ΔE[40q] | Stockpile release: −7.4505 | `stockpile_delta_e40` | policy_results.pkl |
|
| 25 |
+
| 10 | Top ADAPT gradient | Supplier subsidy: g=4.1955 | `supplier_subsidy_grad` | policy_results.pkl |
|
| 26 |
+
| 11 | Energy reduction | 16.67% from baseline | `energy_reduction_pct` | policy_results.pkl |
|
| 27 |
+
| 12 | Catastrophe overlap | 0.147% (all 6 policies) | `catastrophe_overlap_pct` | dosqpe_results.pkl |
|
| 28 |
+
| 13 | Cascade final stress | 0.7945 (40 nodes, t=6.0) | `cascade_final_mean_stress` | dosqpe_results.pkl |
|
| 29 |
+
| 14 | Scaling R² | 0.9948 (exact: 0.9947702934) | `r_squared` | scaling_results.pkl |
|
| 30 |
+
| 15 | Doubling rate r | 1.1993 per qubit | `doubling_rate` | scaling_results.pkl |
|
| 31 |
+
| 16 | 40q predicted time | 4,709,365 s = 1,308.2 h | `t_40q_predicted_s` | scaling_results.pkl |
|
| 32 |
+
| 17 | 30q measured time | 1,192.306 s (physical ceiling) | `t_30q_measured_s` | scaling_results.pkl |
|
| 33 |
+
| 18 | QARP rating | 4.1/5 weighted; 4.5/5 with ARM fix | — | QARP feedback report |
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## Fujitsu A64FX vs Standard Workstation
|
| 38 |
+
|
| 39 |
+
| Metric | Standard Workstation | **Fujitsu A64FX** | Improvement |
|
| 40 |
+
|---|---|---|---|
|
| 41 |
+
| Quantum-advantage nodes | 14/40 | **39/40** | **2.8×** |
|
| 42 |
+
| Max \|ΔP\| | 0.637 | **0.9504** | **+49%** |
|
| 43 |
+
| Trotter steps (DOS-QPE) | 32 | **64** | **2×** |
|
| 44 |
+
| MPI state-vector | Not feasible | **4-node MPI** | — |
|
| 45 |
+
| Scaling R² | N/A | **0.9948** | — |
|
| 46 |
+
|
| 47 |
+
---
|
| 48 |
+
|
| 49 |
+
## Policy Intervention Ranking (ADAPT-VQE)
|
| 50 |
+
|
| 51 |
+
| Rank (ADAPT) | Policy | ΔE[40q] | Gradient g | ROI |
|
| 52 |
+
|---|---|---|---|---|
|
| 53 |
+
| 1 | Supplier subsidy | −0.8673 | **4.1955** | 0.173 |
|
| 54 |
+
| 2 | Combined optimal | −1.4934 | 0.9886 | 0.187 |
|
| 55 |
+
| 3 | Trade diversion | +0.8176 | 0.8725 | 0.545 |
|
| 56 |
+
| 4 | Stockpile release | **−7.4505** | 0.0030 | 2.483 |
|
| 57 |
+
| 5 | Rate hike | −5.6230 | 0.0032 | 2.811 |
|
| 58 |
+
| 6 | No intervention | 0.0000 | 0.0000 | — |
|
| 59 |
+
|
| 60 |
+
> **Key insight:** Supplier subsidy (#1 by ADAPT gradient) and Stockpile release (#1 by energy reduction) achieve stabilisation through fundamentally different mechanisms — a distinction invisible to classical analysis that is critical for policy portfolio design.
|
| 61 |
+
|
| 62 |
+
---
|
| 63 |
+
|
| 64 |
+
## Hardware Scaling (Fujitsu A64FX, 12–30q Measured)
|
| 65 |
+
|
| 66 |
+
| Qubits | SV RAM | Time/eval | Method |
|
| 67 |
+
|---|---|---|---|
|
| 68 |
+
| 12q | 0.07 MB | 0.012 s | Single-node VQE |
|
| 69 |
+
| 20q | 16.8 MB | 3.139 s | Single-node VQE |
|
| 70 |
+
| 24q | 268 MB | 8.944 s | MPI × 48, 4-node |
|
| 71 |
+
| 27q | 2,147 MB | 88.852 s | MPI × 48, 4-node |
|
| 72 |
+
| 29q | 8,590 MB | 595.507 s | MPI × 48, 4-node |
|
| 73 |
+
| **30q** | **17,180 MB** | **1,192.306 s** | **Physical ceiling** |
|
| 74 |
+
| 31q | 34,360 MB | — | Exceeds 32 GB node RAM |
|
| 75 |
+
| 40q | 17,592,186 MB | 4,709,365 s | Extrapolated (1,308.2 h) |
|
| 76 |
+
|
| 77 |
+
**Exponential fit:** t(n) = 7.8785 × 2^{1.1993(n−24)}, **R² = 0.9948**
|
dashboard.py
ADDED
|
@@ -0,0 +1,1735 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
QR-SPPS: Quantum-Native Retail Shock Propagation & Policy Stress Simulator
|
| 3 |
+
Streamlit Dashboard v2.0 — Fujitsu Quantum Simulator Challenge 2025-26
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import streamlit as st
|
| 7 |
+
import pickle, os, sys, types
|
| 8 |
+
import numpy as np
|
| 9 |
+
import plotly.graph_objects as go
|
| 10 |
+
import plotly.express as px
|
| 11 |
+
from plotly.subplots import make_subplots
|
| 12 |
+
import pandas as pd
|
| 13 |
+
|
| 14 |
+
st.set_page_config(
|
| 15 |
+
page_title="QR-SPPS | Quantum Risk Simulator",
|
| 16 |
+
page_icon="⚛",
|
| 17 |
+
layout="wide",
|
| 18 |
+
initial_sidebar_state="expanded"
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
st.markdown("""
|
| 22 |
+
<style>
|
| 23 |
+
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@300;400;600;700&family=Orbitron:wght@400;600;700;900&display=swap');
|
| 24 |
+
|
| 25 |
+
:root {
|
| 26 |
+
--bg: #060b14;
|
| 27 |
+
--bg2: #0a1020;
|
| 28 |
+
--surface: #0f1928;
|
| 29 |
+
--surface2: #141f33;
|
| 30 |
+
--border: #1a2d4a;
|
| 31 |
+
--border2: #243a5c;
|
| 32 |
+
--accent: #38bdf8;
|
| 33 |
+
--accent2: #0ea5e9;
|
| 34 |
+
--green: #34d399;
|
| 35 |
+
--green2: #10b981;
|
| 36 |
+
--orange: #fb923c;
|
| 37 |
+
--red: #f87171;
|
| 38 |
+
--purple: #a78bfa;
|
| 39 |
+
--yellow: #fbbf24;
|
| 40 |
+
--text: #e2e8f0;
|
| 41 |
+
--text2: #94a3b8;
|
| 42 |
+
--muted: #475569;
|
| 43 |
+
--glow: 0 0 20px rgba(56,189,248,0.15);
|
| 44 |
+
--glow-g: 0 0 20px rgba(52,211,153,0.15);
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
*, *::before, *::after { box-sizing: border-box; }
|
| 48 |
+
|
| 49 |
+
html, body, [data-testid="stAppViewContainer"] {
|
| 50 |
+
background: var(--bg) !important;
|
| 51 |
+
color: var(--text) !important;
|
| 52 |
+
font-family: 'Space Grotesk', sans-serif;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
[data-testid="stAppViewContainer"] > .main {
|
| 56 |
+
background: var(--bg) !important;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
[data-testid="stSidebar"] {
|
| 60 |
+
background: var(--bg2) !important;
|
| 61 |
+
border-right: 1px solid var(--border) !important;
|
| 62 |
+
}
|
| 63 |
+
[data-testid="stSidebar"] * { color: var(--text) !important; }
|
| 64 |
+
|
| 65 |
+
h1,h2,h3,h4 { font-family: 'Space Grotesk', sans-serif; font-weight: 700; }
|
| 66 |
+
|
| 67 |
+
/* Metric cards */
|
| 68 |
+
.qcard {
|
| 69 |
+
background: var(--surface);
|
| 70 |
+
border: 1px solid var(--border);
|
| 71 |
+
border-radius: 14px;
|
| 72 |
+
padding: 20px 22px;
|
| 73 |
+
position: relative;
|
| 74 |
+
overflow: hidden;
|
| 75 |
+
transition: border-color 0.2s;
|
| 76 |
+
}
|
| 77 |
+
.qcard:hover { border-color: var(--border2); }
|
| 78 |
+
.qcard::after {
|
| 79 |
+
content: '';
|
| 80 |
+
position: absolute;
|
| 81 |
+
inset: 0;
|
| 82 |
+
background: linear-gradient(135deg, rgba(56,189,248,0.03) 0%, transparent 60%);
|
| 83 |
+
pointer-events: none;
|
| 84 |
+
}
|
| 85 |
+
.qcard-accent { border-top: 2px solid var(--accent); }
|
| 86 |
+
.qcard-green { border-top: 2px solid var(--green); }
|
| 87 |
+
.qcard-orange { border-top: 2px solid var(--orange); }
|
| 88 |
+
.qcard-purple { border-top: 2px solid var(--purple); }
|
| 89 |
+
|
| 90 |
+
.qval {
|
| 91 |
+
font-family: 'Orbitron', monospace;
|
| 92 |
+
font-size: 1.9rem;
|
| 93 |
+
font-weight: 700;
|
| 94 |
+
color: var(--accent);
|
| 95 |
+
line-height: 1.1;
|
| 96 |
+
letter-spacing: -0.02em;
|
| 97 |
+
}
|
| 98 |
+
.qval-g { color: var(--green); }
|
| 99 |
+
.qval-o { color: var(--orange); }
|
| 100 |
+
.qval-p { color: var(--purple); }
|
| 101 |
+
.qlabel {
|
| 102 |
+
font-size: 0.68rem;
|
| 103 |
+
color: var(--muted);
|
| 104 |
+
text-transform: uppercase;
|
| 105 |
+
letter-spacing: 0.12em;
|
| 106 |
+
margin-top: 6px;
|
| 107 |
+
font-weight: 600;
|
| 108 |
+
}
|
| 109 |
+
.qdelta {
|
| 110 |
+
font-family: 'JetBrains Mono', monospace;
|
| 111 |
+
font-size: 0.78rem;
|
| 112 |
+
color: var(--text2);
|
| 113 |
+
margin-top: 5px;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
/* Section headers */
|
| 117 |
+
.sec-hdr {
|
| 118 |
+
font-size: 0.65rem;
|
| 119 |
+
font-weight: 700;
|
| 120 |
+
text-transform: uppercase;
|
| 121 |
+
letter-spacing: 0.18em;
|
| 122 |
+
color: var(--muted);
|
| 123 |
+
border-bottom: 1px solid var(--border);
|
| 124 |
+
padding-bottom: 8px;
|
| 125 |
+
margin: 20px 0 14px;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
/* Badges */
|
| 129 |
+
.badge {
|
| 130 |
+
display: inline-block;
|
| 131 |
+
padding: 3px 10px;
|
| 132 |
+
border-radius: 5px;
|
| 133 |
+
font-family: 'JetBrains Mono', monospace;
|
| 134 |
+
font-size: 0.67rem;
|
| 135 |
+
font-weight: 600;
|
| 136 |
+
letter-spacing: 0.05em;
|
| 137 |
+
}
|
| 138 |
+
.badge-blue { background: rgba(56,189,248,0.12); border: 1px solid rgba(56,189,248,0.3); color: var(--accent); }
|
| 139 |
+
.badge-green { background: rgba(52,211,153,0.12); border: 1px solid rgba(52,211,153,0.3); color: var(--green); }
|
| 140 |
+
.badge-orange{ background: rgba(251,146,60,0.12); border: 1px solid rgba(251,146,60,0.3); color: var(--orange); }
|
| 141 |
+
|
| 142 |
+
/* Alert boxes */
|
| 143 |
+
.alert-info {
|
| 144 |
+
background: rgba(56,189,248,0.07);
|
| 145 |
+
border: 1px solid rgba(56,189,248,0.25);
|
| 146 |
+
border-left: 3px solid var(--accent);
|
| 147 |
+
border-radius: 8px;
|
| 148 |
+
padding: 12px 16px;
|
| 149 |
+
margin: 10px 0;
|
| 150 |
+
font-size: 0.88rem;
|
| 151 |
+
line-height: 1.6;
|
| 152 |
+
}
|
| 153 |
+
.alert-success {
|
| 154 |
+
background: rgba(52,211,153,0.07);
|
| 155 |
+
border: 1px solid rgba(52,211,153,0.25);
|
| 156 |
+
border-left: 3px solid var(--green);
|
| 157 |
+
border-radius: 8px;
|
| 158 |
+
padding: 12px 16px;
|
| 159 |
+
margin: 10px 0;
|
| 160 |
+
}
|
| 161 |
+
.alert-danger {
|
| 162 |
+
background: rgba(248,113,113,0.07);
|
| 163 |
+
border: 1px solid rgba(248,113,113,0.25);
|
| 164 |
+
border-left: 3px solid var(--red);
|
| 165 |
+
border-radius: 8px;
|
| 166 |
+
padding: 12px 16px;
|
| 167 |
+
margin: 10px 0;
|
| 168 |
+
}
|
| 169 |
+
.alert-warn {
|
| 170 |
+
background: rgba(251,191,36,0.07);
|
| 171 |
+
border: 1px solid rgba(251,191,36,0.25);
|
| 172 |
+
border-left: 3px solid var(--yellow);
|
| 173 |
+
border-radius: 8px;
|
| 174 |
+
padding: 12px 16px;
|
| 175 |
+
margin: 10px 0;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
/* Sidebar logo */
|
| 179 |
+
.sidebar-logo {
|
| 180 |
+
text-align: center;
|
| 181 |
+
padding: 18px 0 22px;
|
| 182 |
+
}
|
| 183 |
+
.logo-icon {
|
| 184 |
+
font-size: 2.8rem;
|
| 185 |
+
line-height: 1;
|
| 186 |
+
display: block;
|
| 187 |
+
}
|
| 188 |
+
.logo-title {
|
| 189 |
+
font-family: 'Orbitron', monospace;
|
| 190 |
+
font-weight: 900;
|
| 191 |
+
font-size: 1.15rem;
|
| 192 |
+
color: var(--accent);
|
| 193 |
+
letter-spacing: 0.1em;
|
| 194 |
+
margin-top: 8px;
|
| 195 |
+
}
|
| 196 |
+
.logo-sub {
|
| 197 |
+
font-size: 0.62rem;
|
| 198 |
+
color: var(--muted);
|
| 199 |
+
letter-spacing: 0.15em;
|
| 200 |
+
text-transform: uppercase;
|
| 201 |
+
margin-top: 3px;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
/* Page title */
|
| 205 |
+
.page-title {
|
| 206 |
+
font-family: 'Orbitron', monospace;
|
| 207 |
+
font-size: 1.7rem;
|
| 208 |
+
font-weight: 700;
|
| 209 |
+
color: var(--text);
|
| 210 |
+
letter-spacing: -0.01em;
|
| 211 |
+
line-height: 1.2;
|
| 212 |
+
margin-bottom: 4px;
|
| 213 |
+
}
|
| 214 |
+
.page-sub {
|
| 215 |
+
font-size: 0.88rem;
|
| 216 |
+
color: var(--text2);
|
| 217 |
+
margin-bottom: 24px;
|
| 218 |
+
line-height: 1.5;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/* Streamlit overrides */
|
| 222 |
+
div[data-testid="stMetric"] {
|
| 223 |
+
background: var(--surface) !important;
|
| 224 |
+
border: 1px solid var(--border) !important;
|
| 225 |
+
border-radius: 12px !important;
|
| 226 |
+
padding: 16px !important;
|
| 227 |
+
}
|
| 228 |
+
div[data-testid="stMetric"] label {
|
| 229 |
+
color: var(--muted) !important;
|
| 230 |
+
font-size: 0.72rem !important;
|
| 231 |
+
text-transform: uppercase !important;
|
| 232 |
+
letter-spacing: 0.1em !important;
|
| 233 |
+
}
|
| 234 |
+
div[data-testid="stMetric"] [data-testid="stMetricValue"] {
|
| 235 |
+
color: var(--accent) !important;
|
| 236 |
+
font-family: 'JetBrains Mono', monospace !important;
|
| 237 |
+
font-size: 1.4rem !important;
|
| 238 |
+
}
|
| 239 |
+
div[data-testid="stMetric"] [data-testid="stMetricDelta"] {
|
| 240 |
+
font-family: 'JetBrains Mono', monospace !important;
|
| 241 |
+
font-size: 0.72rem !important;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
.stTabs [data-baseweb="tab-list"] {
|
| 245 |
+
background: var(--surface) !important;
|
| 246 |
+
border-radius: 10px !important;
|
| 247 |
+
border: 1px solid var(--border) !important;
|
| 248 |
+
padding: 4px !important;
|
| 249 |
+
gap: 4px !important;
|
| 250 |
+
}
|
| 251 |
+
.stTabs [data-baseweb="tab"] {
|
| 252 |
+
color: var(--muted) !important;
|
| 253 |
+
border-radius: 7px !important;
|
| 254 |
+
font-size: 0.82rem !important;
|
| 255 |
+
font-weight: 600 !important;
|
| 256 |
+
padding: 8px 16px !important;
|
| 257 |
+
}
|
| 258 |
+
.stTabs [aria-selected="true"] {
|
| 259 |
+
background: var(--surface2) !important;
|
| 260 |
+
color: var(--accent) !important;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
.stSelectbox > div > div,
|
| 264 |
+
.stMultiSelect > div > div {
|
| 265 |
+
background: var(--surface) !important;
|
| 266 |
+
border: 1px solid var(--border) !important;
|
| 267 |
+
color: var(--text) !important;
|
| 268 |
+
border-radius: 8px !important;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
.stSlider [data-baseweb="slider"] div[role="slider"] {
|
| 272 |
+
background: var(--accent) !important;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
div[data-testid="stDataFrame"] {
|
| 276 |
+
border: 1px solid var(--border) !important;
|
| 277 |
+
border-radius: 10px !important;
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
/* Radio buttons in sidebar */
|
| 281 |
+
[data-testid="stSidebar"] .stRadio label {
|
| 282 |
+
font-size: 0.85rem !important;
|
| 283 |
+
padding: 8px 12px !important;
|
| 284 |
+
border-radius: 7px !important;
|
| 285 |
+
cursor: pointer !important;
|
| 286 |
+
}
|
| 287 |
+
[data-testid="stSidebar"] .stRadio label:hover {
|
| 288 |
+
background: var(--surface) !important;
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
/* Divider */
|
| 292 |
+
hr { border-color: var(--border) !important; margin: 20px 0 !important; }
|
| 293 |
+
|
| 294 |
+
/* Glowing pulse for key metric */
|
| 295 |
+
@keyframes pulse-glow {
|
| 296 |
+
0%, 100% { box-shadow: 0 0 10px rgba(56,189,248,0.1); }
|
| 297 |
+
50% { box-shadow: 0 0 25px rgba(56,189,248,0.25); }
|
| 298 |
+
}
|
| 299 |
+
.pulse { animation: pulse-glow 3s ease-in-out infinite; }
|
| 300 |
+
</style>
|
| 301 |
+
""", unsafe_allow_html=True)
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
# ── Data loading ───────────────────────────────────────────────
|
| 305 |
+
# Points to the absolute path of the directory containing dashboard.py
|
| 306 |
+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
| 307 |
+
# Points to the data folder inside that directory
|
| 308 |
+
PKL_DIR = os.path.join(BASE_DIR, 'data')
|
| 309 |
+
|
| 310 |
+
class _SafeUnpickler(pickle.Unpickler):
|
| 311 |
+
def find_class(self, module, name):
|
| 312 |
+
try: return super().find_class(module, name)
|
| 313 |
+
except: return type(name, (), {'__init__': lambda s, *a, **k: None, 'terms': {}})
|
| 314 |
+
|
| 315 |
+
@st.cache_data
|
| 316 |
+
def load_all_data():
|
| 317 |
+
data = {}
|
| 318 |
+
files = {
|
| 319 |
+
'ham': 'QRSPPS_hamiltonians.pkl',
|
| 320 |
+
'vqe': 'QRSPPS_vqe_results.pkl',
|
| 321 |
+
'policy': 'QRSPPS_policy_results.pkl',
|
| 322 |
+
'dosqpe': 'QRSPPS_dosqpe_results.pkl',
|
| 323 |
+
'scaling': 'QRSPPS_scaling_results.pkl',
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
# Ensure the data directory exists
|
| 327 |
+
if not os.path.exists(PKL_DIR):
|
| 328 |
+
st.error(f"Data directory not found at: {PKL_DIR}")
|
| 329 |
+
return {k: None for k in files.keys()}
|
| 330 |
+
|
| 331 |
+
for key, fname in files.items():
|
| 332 |
+
path = os.path.join(PKL_DIR, fname)
|
| 333 |
+
if os.path.exists(path):
|
| 334 |
+
try:
|
| 335 |
+
with open(path, 'rb') as f:
|
| 336 |
+
if key == 'ham':
|
| 337 |
+
data[key] = _SafeUnpickler(f).load()
|
| 338 |
+
else:
|
| 339 |
+
data[key] = pickle.load(f)
|
| 340 |
+
except Exception as e:
|
| 341 |
+
data[key] = None
|
| 342 |
+
st.warning(f"Could not load {fname}: {e}")
|
| 343 |
+
else:
|
| 344 |
+
# Helpful debug message to see where it's looking
|
| 345 |
+
st.error(f"File not found: {path}")
|
| 346 |
+
data[key] = None
|
| 347 |
+
return data
|
| 348 |
+
|
| 349 |
+
D = load_all_data()
|
| 350 |
+
|
| 351 |
+
# ── Plotly dark theme ────────────��─────────────────────────────
|
| 352 |
+
PD = dict(
|
| 353 |
+
template='plotly_dark',
|
| 354 |
+
paper_bgcolor='rgba(0,0,0,0)',
|
| 355 |
+
plot_bgcolor='rgba(15,25,40,0.7)',
|
| 356 |
+
font=dict(family='JetBrains Mono', color='#94a3b8', size=11),
|
| 357 |
+
margin=dict(l=50, r=20, t=44, b=44),
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
POLICY_COLORS = {
|
| 361 |
+
'No intervention': '#475569',
|
| 362 |
+
'Rate hike': '#38bdf8',
|
| 363 |
+
'Supplier subsidy': '#34d399',
|
| 364 |
+
'Stockpile release': '#fbbf24',
|
| 365 |
+
'Trade diversion': '#a78bfa',
|
| 366 |
+
'Combined optimal': '#f87171',
|
| 367 |
+
}
|
| 368 |
+
TIER_COLORS = ['#fb923c', '#a78bfa', '#38bdf8', '#34d399']
|
| 369 |
+
TIER_NAMES = ['Raw Materials', 'Suppliers', 'Distributors', 'Retail Stores']
|
| 370 |
+
|
| 371 |
+
# ── Extract common data ────────────────────────────────────────
|
| 372 |
+
def safe(d, *keys, default=None):
|
| 373 |
+
try:
|
| 374 |
+
v = d
|
| 375 |
+
for k in keys:
|
| 376 |
+
v = v[k]
|
| 377 |
+
return v
|
| 378 |
+
except Exception:
|
| 379 |
+
return default
|
| 380 |
+
|
| 381 |
+
ham = D.get('ham') or {}
|
| 382 |
+
vqe = D.get('vqe') or {}
|
| 383 |
+
pol = D.get('policy') or {}
|
| 384 |
+
dos = D.get('dosqpe') or {}
|
| 385 |
+
scl = D.get('scaling') or {}
|
| 386 |
+
|
| 387 |
+
N_NODES = int(safe(ham, 'n_nodes', default=40) or safe(vqe, 'n_nodes', default=40) or 40)
|
| 388 |
+
N_VQE_Q = int(safe(vqe, 'n_vqe_q', default=30) or 30)
|
| 389 |
+
NODE_LABELS_40 = list(safe(ham, 'NODE_LABELS', default=[f'Node-{i}' for i in range(N_NODES)]))
|
| 390 |
+
TIER_MAP_40 = dict(safe(ham, 'TIER', default={i: min(3, i//10) for i in range(N_NODES)}))
|
| 391 |
+
SUPPLY_EDGES = list(safe(ham, 'SUPPLY_EDGES', default=[]))
|
| 392 |
+
|
| 393 |
+
# 40-node stress arrays (always use 40q versions)
|
| 394 |
+
stress_vqe_40 = np.array(safe(vqe, 'stress_vqe_A_40q', default=np.zeros(N_NODES)))
|
| 395 |
+
mc_stress_40 = np.array(safe(vqe, 'mc_stress_A', default=np.zeros(N_NODES)))
|
| 396 |
+
if len(stress_vqe_40) != N_NODES:
|
| 397 |
+
stress_vqe_40 = np.pad(stress_vqe_40, (0, max(0, N_NODES - len(stress_vqe_40))))[:N_NODES]
|
| 398 |
+
if len(mc_stress_40) != N_NODES:
|
| 399 |
+
mc_stress_40 = np.pad(mc_stress_40, (0, max(0, N_NODES - len(mc_stress_40))))[:N_NODES]
|
| 400 |
+
|
| 401 |
+
# Policy data — 40q stress arrays
|
| 402 |
+
pol_results = dict(safe(pol, 'policy_results', default={}))
|
| 403 |
+
pol_names = list(safe(pol, 'policy_names', default=list(pol_results.keys())))
|
| 404 |
+
pol_gradients= dict(safe(pol, 'gradients', default={}))
|
| 405 |
+
pol_ranked = list(safe(pol, 'ranked_policies',default=[]))
|
| 406 |
+
NODE_LABELS_POL = list(safe(pol, 'NODE_LABELS', default=NODE_LABELS_40))
|
| 407 |
+
TIER_MAP_POL = dict(safe(pol, 'TIER', default=TIER_MAP_40))
|
| 408 |
+
# Policy stress is 40-node (from our NB3 output)
|
| 409 |
+
def get_pol_stress_40(name):
|
| 410 |
+
raw = safe(pol_results, name, 'stress', default=None)
|
| 411 |
+
if raw is None:
|
| 412 |
+
return np.zeros(N_NODES)
|
| 413 |
+
arr = np.array(raw)
|
| 414 |
+
if len(arr) == N_NODES:
|
| 415 |
+
return arr
|
| 416 |
+
# pad/trim to N_NODES
|
| 417 |
+
return np.pad(arr, (0, max(0, N_NODES - len(arr))))[:N_NODES]
|
| 418 |
+
|
| 419 |
+
# DOS-QPE data (40-node cascade)
|
| 420 |
+
cascade_40 = np.array(safe(dos, 'cascade_matrix', default=np.zeros((10, N_NODES))))
|
| 421 |
+
times_dyn = np.array(safe(dos, 'times_dynamics', default=np.linspace(0.6, 6.0, 10)))
|
| 422 |
+
tail_risks = dict(safe(dos, 'tail_risks', default={}))
|
| 423 |
+
temps = np.array(safe(dos, 'temperatures', default=np.logspace(-2, 1, 60)))
|
| 424 |
+
cat_overlaps = dict(safe(dos, 'cat_overlaps', default={}))
|
| 425 |
+
E_cutoff = float(safe(dos, 'E_cutoff', default=-43.2))
|
| 426 |
+
energies_40 = np.array(safe(dos, 'energies_A_40q', default=np.linspace(0, 10, 32)))
|
| 427 |
+
dos_vals = np.array(safe(dos, 'dos_A', default=np.zeros(32)))
|
| 428 |
+
survival_amp = np.array(safe(dos, 'survival_A', default=np.ones(64, dtype=complex)))
|
| 429 |
+
times_dos = np.array(safe(dos, 'times_A', default=np.linspace(0, 15, 64)))
|
| 430 |
+
|
| 431 |
+
# Scaling data
|
| 432 |
+
scl_all = list(safe(scl, 'all_scaling', default=[]))
|
| 433 |
+
scl_ns = list(safe(scl, 'qubit_sizes', default=[]))
|
| 434 |
+
scl_times = list(safe(scl, 'times', default=[]))
|
| 435 |
+
scl_mems = list(safe(scl, 'memories_mb', default=[]))
|
| 436 |
+
scl_srcs = list(safe(scl, 'sources', default=[]))
|
| 437 |
+
doubling_rate= float(safe(scl, 'doubling_rate', default=1.1993))
|
| 438 |
+
r_squared = float(safe(scl, 'r_squared', default=0.9948))
|
| 439 |
+
t_40q = float(safe(scl, 't_40q_predicted',default=4709365))
|
| 440 |
+
t_at_base = float(safe(scl, 't_at_base', default=7.88))
|
| 441 |
+
hist_12 = list(safe(scl, 'vqe_12_history', default=[]))
|
| 442 |
+
depth_res = list(safe(vqe, 'depth_results', default=[]))
|
| 443 |
+
vqe_e0_30 = float(safe(vqe, 'vqe_E0_A', default=-33.52))
|
| 444 |
+
vqe_e0_40 = float(safe(vqe, 'vqe_E0_A_40q', default=-44.69))
|
| 445 |
+
exact_e0_40 = float(safe(ham, 'exact_E0_A', default=-44.69))
|
| 446 |
+
|
| 447 |
+
if not scl_ns and scl_all:
|
| 448 |
+
scl_ns = [r['n_qubits'] for r in scl_all]
|
| 449 |
+
scl_times = [r['mean_time'] for r in scl_all]
|
| 450 |
+
scl_mems = [r['state_vec_mb'] for r in scl_all]
|
| 451 |
+
if not scl_srcs and scl_all:
|
| 452 |
+
scl_srcs = []
|
| 453 |
+
for r in scl_all:
|
| 454 |
+
if r.get('extrapolated'): scl_srcs.append('Extrapolated')
|
| 455 |
+
elif r.get('mpi_rank') is not None: scl_srcs.append('MPI measured')
|
| 456 |
+
else: scl_srcs.append('Single-node')
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
# ══════════════════════════════════════════════════════════════
|
| 460 |
+
# SIDEBAR
|
| 461 |
+
# ══════════════════════════════════════════════════════════════
|
| 462 |
+
with st.sidebar:
|
| 463 |
+
st.markdown("""
|
| 464 |
+
<div class="sidebar-logo">
|
| 465 |
+
<span class="logo-icon">⚛</span>
|
| 466 |
+
<div class="logo-title">QR-SPPS</div>
|
| 467 |
+
<div class="logo-sub">Quantum Risk Simulator</div>
|
| 468 |
+
</div>
|
| 469 |
+
""", unsafe_allow_html=True)
|
| 470 |
+
|
| 471 |
+
st.markdown('<div class="sec-hdr">Navigation</div>', unsafe_allow_html=True)
|
| 472 |
+
page = st.radio("", [
|
| 473 |
+
"🏠 Overview",
|
| 474 |
+
"📊 Supply Chain State",
|
| 475 |
+
"🎛 Policy Simulator",
|
| 476 |
+
"💥 Tail Risk & Cascades",
|
| 477 |
+
"📈 Qubit Scaling",
|
| 478 |
+
"📋 QARP Feedback",
|
| 479 |
+
], label_visibility='collapsed')
|
| 480 |
+
|
| 481 |
+
st.markdown('<div class="sec-hdr">Shock Scenarios</div>', unsafe_allow_html=True)
|
| 482 |
+
st.markdown("""
|
| 483 |
+
<div style='font-size:0.8rem; line-height:1.9'>
|
| 484 |
+
<div style='color:#f87171'>⚡ <strong>Scenario A</strong> — RM-A Supply Failure</div>
|
| 485 |
+
<div style='color:#fb923c'>⚡ <strong>Scenario B</strong> — RM-A + Demand Shock (21 nodes)</div>
|
| 486 |
+
</div>
|
| 487 |
+
""", unsafe_allow_html=True)
|
| 488 |
+
|
| 489 |
+
st.markdown('<div class="sec-hdr">Pipeline Status</div>', unsafe_allow_html=True)
|
| 490 |
+
for label, key in [('Hamiltonians (NB1)', 'ham'), ('VQE Results (NB2)', 'vqe'),
|
| 491 |
+
('Policy Results (NB3)', 'policy'), ('DOS-QPE (NB4)', 'dosqpe'),
|
| 492 |
+
('Scaling (NB5)', 'scaling')]:
|
| 493 |
+
ok = D.get(key) is not None
|
| 494 |
+
col = '#34d399' if ok else '#f87171'
|
| 495 |
+
ico = '●' if ok else '○'
|
| 496 |
+
st.markdown(f"<div style='font-size:0.73rem; font-family:JetBrains Mono; "
|
| 497 |
+
f"color:{col}; margin:3px 0'>{ico} {label}</div>", unsafe_allow_html=True)
|
| 498 |
+
|
| 499 |
+
st.markdown(f"""
|
| 500 |
+
<div style='margin-top:22px; padding:12px; background:var(--surface);
|
| 501 |
+
border:1px solid var(--border); border-radius:10px; font-size:0.7rem; color:var(--muted)'>
|
| 502 |
+
<div style='color:var(--accent); font-weight:600; margin-bottom:6px; font-family:Orbitron'>SYSTEM</div>
|
| 503 |
+
<div>Fujitsu A64FX · MPI</div>
|
| 504 |
+
<div>12q–30q measured</div>
|
| 505 |
+
<div>40q extrapolated</div>
|
| 506 |
+
<div style='margin-top:6px; color:var(--green)'>VQE · ADAPT-VQE · DOS-QPE</div>
|
| 507 |
+
</div>
|
| 508 |
+
""", unsafe_allow_html=True)
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
# ══════════════════════════════════════════════════════════════
|
| 512 |
+
# PAGE 1 — OVERVIEW
|
| 513 |
+
# ══════════════════════════════════════════════════════════════
|
| 514 |
+
if page == "🏠 Overview":
|
| 515 |
+
st.markdown("""
|
| 516 |
+
<div class="page-title">QR-SPPS: Quantum-Native Retail Shock Propagation & Policy Stress Simulator </div>
|
| 517 |
+
<br> <!-- Spacer -->
|
| 518 |
+
<div class="page-sub">
|
| 519 |
+
Counterfactual quantum risk engine for macro-micro supply-chain shock propagation
|
| 520 |
+
· <span class="badge badge-blue">Fujitsu QARP</span>
|
| 521 |
+
<span class="badge badge-green">30q Executed · 40q Encoded</span>
|
| 522 |
+
<span class="badge badge-orange">Fujitsu QSim Challenge 2025-26</span>
|
| 523 |
+
</div>
|
| 524 |
+
""", unsafe_allow_html=True)
|
| 525 |
+
|
| 526 |
+
# Top KPI row
|
| 527 |
+
c1, c2, c3, c4, c5 = st.columns(5)
|
| 528 |
+
q_adv = int(safe(vqe, 'n_quantum_advantage_nodes', default=39) or 39)
|
| 529 |
+
best_pol_name = min(pol_names, key=lambda n: safe(pol_results, n, 'delta_E', default=0)) if pol_names else 'N/A'
|
| 530 |
+
best_dE = float(safe(pol_results, best_pol_name, 'delta_E', default=0)) if best_pol_name != 'N/A' else 0
|
| 531 |
+
|
| 532 |
+
with c1:
|
| 533 |
+
st.markdown(f"""<div class="qcard qcard-accent pulse">
|
| 534 |
+
<div class="qval">40</div>
|
| 535 |
+
<div class="qlabel">Supply chain nodes</div>
|
| 536 |
+
<div class="qdelta">2 raw · 7 sup · 11 dist · 20 retail</div>
|
| 537 |
+
</div>""", unsafe_allow_html=True)
|
| 538 |
+
with c2:
|
| 539 |
+
st.markdown(f"""<div class="qcard qcard-accent">
|
| 540 |
+
<div class="qval">30q</div>
|
| 541 |
+
<div class="qlabel">VQE Execution</div>
|
| 542 |
+
<div class="qdelta">Encoded: 40q · 2⁴⁰ Hilbert space</div>
|
| 543 |
+
</div>""", unsafe_allow_html=True)
|
| 544 |
+
with c3:
|
| 545 |
+
err = abs(vqe_e0_40 - exact_e0_40)
|
| 546 |
+
st.markdown(f"""<div class="qcard qcard-green">
|
| 547 |
+
<div class="qval qval-g">{vqe_e0_40:.3f}</div>
|
| 548 |
+
<div class="qlabel">VQE Ground State E₀ (40q)</div>
|
| 549 |
+
<div class="qdelta">err = {err:.2e} vs NB1 exact</div>
|
| 550 |
+
</div>""", unsafe_allow_html=True)
|
| 551 |
+
with c4:
|
| 552 |
+
st.markdown(f"""<div class="qcard qcard-orange">
|
| 553 |
+
<div class="qval qval-o">{q_adv}/40</div>
|
| 554 |
+
<div class="qlabel">Quantum Advantage Nodes</div>
|
| 555 |
+
<div class="qdelta">|VQE − MC| > 0.15 per node</div>
|
| 556 |
+
</div>""", unsafe_allow_html=True)
|
| 557 |
+
with c5:
|
| 558 |
+
pol_E_red = abs(best_dE) / abs(vqe_e0_40) * 100 if vqe_e0_40 != 0 else 0
|
| 559 |
+
st.markdown(f"""<div class="qcard qcard-purple">
|
| 560 |
+
<div class="qval qval-p">{pol_E_red:.1f}%</div>
|
| 561 |
+
<div class="qlabel">Best Policy Energy Reduction</div>
|
| 562 |
+
<div class="qdelta">{best_pol_name} · ΔE = {best_dE:+.3f}</div>
|
| 563 |
+
</div>""", unsafe_allow_html=True)
|
| 564 |
+
|
| 565 |
+
st.markdown("<hr>", unsafe_allow_html=True)
|
| 566 |
+
|
| 567 |
+
col_left, col_right = st.columns([3, 2])
|
| 568 |
+
|
| 569 |
+
with col_left:
|
| 570 |
+
st.markdown("#### ⚙️ How QR-SPPS Works")
|
| 571 |
+
steps = [
|
| 572 |
+
("#38bdf8", "① Hamiltonian Encoding (NB1)",
|
| 573 |
+
"40-node supply chain → 40-qubit Ising Hamiltonian. ZZ coupling terms encode supplier dependencies. X fields encode demand shocks. Hilbert space: 2⁴⁰ ≈ 1.1 trillion states."),
|
| 574 |
+
("#34d399", "② VQE Ground State (NB2)",
|
| 575 |
+
"Hardware-efficient ansatz (depth=3, 120 params) on 30q sub-network. 5 random restarts. VQE finds equilibrium stress state — E₀ matches 40q extrapolation with zero error."),
|
| 576 |
+
("#a78bfa", "③ ADAPT-VQE Policy Ranking (NB3)",
|
| 577 |
+
"6 policy interventions encoded as Hamiltonian perturbations. Gradient screening ranks policies by stress reduction. Best policy: Stockpile release (ΔE = −7.45)."),
|
| 578 |
+
("#fb923c", "④ DOS-QPE Tail Risk (NB4)",
|
| 579 |
+
"64-step Trotter evolution reconstructs density of states. Quantum Boltzmann model quantifies catastrophic cascade probability vs market volatility for each policy."),
|
| 580 |
+
("#f87171", "⑤ Qubit Scaling (NB5)",
|
| 581 |
+
"MPI-measured 24q–30q on Fujitsu A64FX. Exponential fit R²=0.9948. Full 40q state-vector = 17.6 TB, 1308h per eval — demonstrating quantum advantage regime."),
|
| 582 |
+
]
|
| 583 |
+
for color, title, detail in steps:
|
| 584 |
+
st.markdown(f"""
|
| 585 |
+
<div style='background:var(--surface); border:1px solid var(--border);
|
| 586 |
+
border-left:3px solid {color}; border-radius:10px;
|
| 587 |
+
padding:14px 16px; margin-bottom:10px'>
|
| 588 |
+
<div style='color:{color}; font-weight:700; font-size:0.88rem; margin-bottom:4px'>{title}</div>
|
| 589 |
+
<div style='color:var(--text2); font-size:0.82rem; line-height:1.6'>{detail}</div>
|
| 590 |
+
</div>
|
| 591 |
+
""", unsafe_allow_html=True)
|
| 592 |
+
|
| 593 |
+
with col_right:
|
| 594 |
+
st.markdown("#### 🔬 Why Quantum?")
|
| 595 |
+
comparison_data = {
|
| 596 |
+
'Capability': [
|
| 597 |
+
'Correlated node failures',
|
| 598 |
+
'Combinatorial policy search',
|
| 599 |
+
'Tail-risk quantification',
|
| 600 |
+
'Entangled cascade paths',
|
| 601 |
+
'Simultaneous scenario eval',
|
| 602 |
+
'Spectral gap measurement',
|
| 603 |
+
],
|
| 604 |
+
'Classical MC': [
|
| 605 |
+
'❌ Independent sampling',
|
| 606 |
+
'❌ Exponential search',
|
| 607 |
+
'⚠️ Needs millions of samples',
|
| 608 |
+
'❌ Graph heuristics only',
|
| 609 |
+
'❌ Sequential runs',
|
| 610 |
+
'❌ Not accessible',
|
| 611 |
+
],
|
| 612 |
+
'QR-SPPS (Quantum)': [
|
| 613 |
+
'✅ ZZ entanglement native',
|
| 614 |
+
'✅ Superposition search',
|
| 615 |
+
'✅ Full eigenspectrum',
|
| 616 |
+
'✅ Quantum cascade dynamics',
|
| 617 |
+
'✅ VQE + ADAPT-VQE',
|
| 618 |
+
'✅ DOS-QPE direct',
|
| 619 |
+
],
|
| 620 |
+
}
|
| 621 |
+
st.dataframe(pd.DataFrame(comparison_data), hide_index=True, use_container_width=True)
|
| 622 |
+
|
| 623 |
+
st.markdown("#### 🏆 Competition Algorithm Summary")
|
| 624 |
+
top_pol = pol_ranked[0][0] if pol_ranked else 'N/A'
|
| 625 |
+
algo_df = pd.DataFrame({
|
| 626 |
+
'Algorithm': ['VQE', 'ADAPT-VQE', 'DOS-QPE', 'MPI Scaling'],
|
| 627 |
+
'Notebook': ['NB2', 'NB3', 'NB4', 'NB5'],
|
| 628 |
+
'Qubits': ['30q exec', '30q exec', '30q Trotter', '24–30q MPI'],
|
| 629 |
+
'Key Result': [
|
| 630 |
+
f'E₀={vqe_e0_40:.3f} (40q)',
|
| 631 |
+
f'Best: {top_pol}',
|
| 632 |
+
'64 steps · cascade 10 snaps',
|
| 633 |
+
f'R²={r_squared:.4f}',
|
| 634 |
+
],
|
| 635 |
+
'QARP': ['✅', '✅', '✅', '✅'],
|
| 636 |
+
})
|
| 637 |
+
st.dataframe(algo_df, hide_index=True, use_container_width=True)
|
| 638 |
+
|
| 639 |
+
# 40q regime callout
|
| 640 |
+
t40h = t_40q / 3600
|
| 641 |
+
st.markdown(f"""
|
| 642 |
+
<div class="alert-danger" style='margin-top:12px'>
|
| 643 |
+
<strong style='color:var(--red)'>40-Qubit Quantum Advantage Regime</strong><br>
|
| 644 |
+
<span style='font-size:0.82rem; color:var(--text2)'>
|
| 645 |
+
40q SV = <strong>17.6 TB RAM</strong> · {t40h:.0f}h per eval<br>
|
| 646 |
+
30q = 17.2 GB (measured, MPI) — maximum tractable point<br>
|
| 647 |
+
Exponential fit: R² = <strong>{r_squared:.4f}</strong>
|
| 648 |
+
</span>
|
| 649 |
+
</div>
|
| 650 |
+
""", unsafe_allow_html=True)
|
| 651 |
+
|
| 652 |
+
|
| 653 |
+
# ══════════════════════════════════════════════════════════════
|
| 654 |
+
# PAGE 2 — SUPPLY CHAIN STATE
|
| 655 |
+
# ══════════════════════════════════════════════════════════════
|
| 656 |
+
elif page == "📊 Supply Chain State":
|
| 657 |
+
st.markdown('<div class="page-title">Supply Chain Quantum Stress Analysis</div>', unsafe_allow_html=True)
|
| 658 |
+
st.markdown(f'<div class="page-sub">40-node network · VQE executed on 30q sub-network · Results mapped to full 40q · vs Classical Monte Carlo (50,000 samples)</div>', unsafe_allow_html=True)
|
| 659 |
+
|
| 660 |
+
if not ham:
|
| 661 |
+
st.error("QRSPPS_hamiltonians.pkl not found.")
|
| 662 |
+
st.stop()
|
| 663 |
+
|
| 664 |
+
st.markdown(f"""
|
| 665 |
+
<div class="alert-info">
|
| 666 |
+
<strong style='color:var(--accent)'>40-Qubit Encoding Active</strong> —
|
| 667 |
+
2 raw materials · 7 suppliers · 11 distributors · 20 retail stores ·
|
| 668 |
+
{len(SUPPLY_EDGES)} supply edges · Hilbert space 2⁴⁰ ≈ 1,099,511,627,776 states ·
|
| 669 |
+
VQE ground state E₀ = {vqe_e0_40:.4f} (error = {abs(vqe_e0_40 - exact_e0_40):.2e} vs exact)
|
| 670 |
+
</div>
|
| 671 |
+
""", unsafe_allow_html=True)
|
| 672 |
+
|
| 673 |
+
col1, col2 = st.columns([1, 1])
|
| 674 |
+
|
| 675 |
+
with col1:
|
| 676 |
+
st.markdown("#### Node Stress Heatmap — All 40 Nodes")
|
| 677 |
+
# Sort by tier then stress
|
| 678 |
+
tier_order = []
|
| 679 |
+
for t in range(4):
|
| 680 |
+
nodes_t = sorted([i for i in range(N_NODES) if TIER_MAP_40.get(i) == t],
|
| 681 |
+
key=lambda i: -stress_vqe_40[i])
|
| 682 |
+
tier_order.extend(nodes_t)
|
| 683 |
+
|
| 684 |
+
labels_ord = [f"{NODE_LABELS_40[i]}" for i in tier_order]
|
| 685 |
+
stress_ord = [float(stress_vqe_40[i]) for i in tier_order]
|
| 686 |
+
tier_c_ord = [TIER_COLORS[TIER_MAP_40.get(i, 3)] for i in tier_order]
|
| 687 |
+
|
| 688 |
+
fig_heat = go.Figure(go.Bar(
|
| 689 |
+
x=stress_ord, y=labels_ord,
|
| 690 |
+
orientation='h',
|
| 691 |
+
marker=dict(
|
| 692 |
+
color=stress_ord,
|
| 693 |
+
colorscale=[[0, '#1a3a2a'], [0.35, '#34d399'], [0.6, '#fbbf24'], [1, '#f87171']],
|
| 694 |
+
cmin=0, cmax=1,
|
| 695 |
+
colorbar=dict(title='Stress', thickness=10, len=0.8),
|
| 696 |
+
),
|
| 697 |
+
text=[f"{s:.3f}" for s in stress_ord],
|
| 698 |
+
textposition='outside',
|
| 699 |
+
textfont=dict(size=9, color='#94a3b8'),
|
| 700 |
+
))
|
| 701 |
+
# Tier separator lines
|
| 702 |
+
t_counts = [sum(1 for i in range(N_NODES) if TIER_MAP_40.get(i) == t) for t in range(4)]
|
| 703 |
+
cumulative = 0
|
| 704 |
+
for t, tc in enumerate(t_counts[:-1]):
|
| 705 |
+
cumulative += tc
|
| 706 |
+
fig_heat.add_hline(y=cumulative - 0.5, line_color='#1a2d4a', line_width=1.5)
|
| 707 |
+
|
| 708 |
+
fig_heat.update_layout(
|
| 709 |
+
**PD, height=max(500, N_NODES * 18),
|
| 710 |
+
xaxis=dict(range=[0, 1.2], title='Stress P(|1⟩)', gridcolor='#1a2d4a'),
|
| 711 |
+
yaxis=dict(autorange='reversed', tickfont=dict(size=9)),
|
| 712 |
+
title=dict(text='VQE Quantum Stress — 30q exec → 40q mapped', font=dict(size=13)),
|
| 713 |
+
)
|
| 714 |
+
st.plotly_chart(fig_heat, use_container_width=True)
|
| 715 |
+
|
| 716 |
+
with col2:
|
| 717 |
+
st.markdown("#### Quantum vs Classical Monte Carlo (40 Nodes)")
|
| 718 |
+
diff = stress_vqe_40 - mc_stress_40
|
| 719 |
+
|
| 720 |
+
fig_qc = go.Figure()
|
| 721 |
+
# Shade quantum advantage regions
|
| 722 |
+
for i in range(N_NODES):
|
| 723 |
+
if abs(diff[i]) > 0.15:
|
| 724 |
+
fig_qc.add_vrect(x0=i-0.5, x1=i+0.5,
|
| 725 |
+
fillcolor='rgba(56,189,248,0.06)',
|
| 726 |
+
line_width=0)
|
| 727 |
+
fig_qc.add_trace(go.Scatter(
|
| 728 |
+
x=list(range(N_NODES)), y=list(mc_stress_40),
|
| 729 |
+
mode='lines+markers', name='Classical MC (50k samples)',
|
| 730 |
+
line=dict(color='#475569', dash='dash', width=1.8),
|
| 731 |
+
marker=dict(size=5, color='#475569'),
|
| 732 |
+
))
|
| 733 |
+
fig_qc.add_trace(go.Scatter(
|
| 734 |
+
x=list(range(N_NODES)), y=list(stress_vqe_40),
|
| 735 |
+
mode='lines+markers', name='QR-SPPS VQE (quantum)',
|
| 736 |
+
line=dict(color='#38bdf8', width=2.2),
|
| 737 |
+
marker=dict(size=7, color='#38bdf8'),
|
| 738 |
+
))
|
| 739 |
+
# Annotate quantum advantage nodes
|
| 740 |
+
for i in range(N_NODES):
|
| 741 |
+
if abs(diff[i]) > 0.25:
|
| 742 |
+
fig_qc.add_annotation(
|
| 743 |
+
x=i, y=float(stress_vqe_40[i]) + 0.06,
|
| 744 |
+
text='Q≫C', showarrow=False,
|
| 745 |
+
font=dict(color='#fb923c', size=9, family='JetBrains Mono'),
|
| 746 |
+
)
|
| 747 |
+
fig_qc.add_hline(y=0.5, line_color='#1e2d45', line_dash='dot', line_width=1)
|
| 748 |
+
fig_qc.update_layout(
|
| 749 |
+
**PD, height=320,
|
| 750 |
+
xaxis=dict(
|
| 751 |
+
tickvals=list(range(0, N_NODES, 4)),
|
| 752 |
+
ticktext=[NODE_LABELS_40[i] for i in range(0, N_NODES, 4)],
|
| 753 |
+
tickangle=-45, gridcolor='#1a2d4a',
|
| 754 |
+
),
|
| 755 |
+
yaxis=dict(title='Stress P(|1⟩)', range=[0, 1.25], gridcolor='#1a2d4a'),
|
| 756 |
+
title=dict(text='Quantum detects entangled cascades classical MC misses', font=dict(size=13)),
|
| 757 |
+
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1),
|
| 758 |
+
)
|
| 759 |
+
st.plotly_chart(fig_qc, use_container_width=True)
|
| 760 |
+
|
| 761 |
+
# Tier stress summary
|
| 762 |
+
st.markdown("#### Tier-Level Stress Summary")
|
| 763 |
+
tier_cols = st.columns(4)
|
| 764 |
+
for t in range(4):
|
| 765 |
+
nodes_t = [i for i in range(N_NODES) if TIER_MAP_40.get(i) == t]
|
| 766 |
+
if nodes_t:
|
| 767 |
+
avg_s = float(np.mean([stress_vqe_40[i] for i in nodes_t]))
|
| 768 |
+
worst_i = max(nodes_t, key=lambda i: stress_vqe_40[i])
|
| 769 |
+
color = '#f87171' if avg_s > 0.7 else ('#fbbf24' if avg_s > 0.45 else '#34d399')
|
| 770 |
+
with tier_cols[t]:
|
| 771 |
+
st.markdown(f"""
|
| 772 |
+
<div class='qcard' style='border-top:2px solid {TIER_COLORS[t]}; text-align:center'>
|
| 773 |
+
<div style='font-family:Orbitron; font-size:1.5rem; color:{color}'>{avg_s:.3f}</div>
|
| 774 |
+
<div style='color:var(--text2); font-size:0.78rem; margin-top:4px'>{TIER_NAMES[t]}</div>
|
| 775 |
+
<div style='color:var(--muted); font-size:0.7rem; margin-top:3px'>{len(nodes_t)} nodes</div>
|
| 776 |
+
<div style='color:var(--muted); font-size:0.68rem'>Worst: {NODE_LABELS_40[worst_i]}</div>
|
| 777 |
+
</div>
|
| 778 |
+
""", unsafe_allow_html=True)
|
| 779 |
+
|
| 780 |
+
# Quantum advantage summary
|
| 781 |
+
qa_count = int(np.sum(np.abs(diff) > 0.15))
|
| 782 |
+
max_diff = float(np.max(np.abs(diff)))
|
| 783 |
+
st.markdown(f"""
|
| 784 |
+
<div class="alert-success" style='margin-top:12px'>
|
| 785 |
+
<strong style='color:var(--green)'>Quantum Advantage Detected</strong> —
|
| 786 |
+
{qa_count}/40 nodes show |VQE − MC| > 0.15 ·
|
| 787 |
+
Maximum divergence = {max_diff:.4f} ·
|
| 788 |
+
VQE captures entangled cascades inaccessible to classical sampling
|
| 789 |
+
</div>
|
| 790 |
+
""", unsafe_allow_html=True)
|
| 791 |
+
|
| 792 |
+
# VQE convergence + depth
|
| 793 |
+
st.markdown("---")
|
| 794 |
+
st.markdown("#### VQE Convergence & Depth Scaling (30q sub-network)")
|
| 795 |
+
dcol1, dcol2 = st.columns(2)
|
| 796 |
+
|
| 797 |
+
with dcol1:
|
| 798 |
+
vqe_hist = list(safe(vqe, 'vqe_history_A', default=[]))
|
| 799 |
+
if vqe_hist:
|
| 800 |
+
fig_conv = go.Figure()
|
| 801 |
+
fig_conv.add_trace(go.Scatter(
|
| 802 |
+
x=list(range(len(vqe_hist))), y=vqe_hist,
|
| 803 |
+
mode='lines', name='VQE energy (best restart)',
|
| 804 |
+
line=dict(color='#38bdf8', width=2),
|
| 805 |
+
fill='tozeroy', fillcolor='rgba(56,189,248,0.06)',
|
| 806 |
+
))
|
| 807 |
+
fig_conv.add_hline(y=vqe_e0_30, line_color='#34d399', line_dash='dash',
|
| 808 |
+
annotation_text=f'E₀={vqe_e0_30:.4f}')
|
| 809 |
+
fig_conv.update_layout(
|
| 810 |
+
**PD, height=280,
|
| 811 |
+
xaxis=dict(title='Optimizer iteration', gridcolor='#1a2d4a'),
|
| 812 |
+
yaxis=dict(title='Energy (30q)', gridcolor='#1a2d4a'),
|
| 813 |
+
title=dict(text='VQE convergence — 30q exec, depth=3, COBYLA', font=dict(size=12)),
|
| 814 |
+
)
|
| 815 |
+
st.plotly_chart(fig_conv, use_container_width=True)
|
| 816 |
+
|
| 817 |
+
with dcol2:
|
| 818 |
+
if depth_res:
|
| 819 |
+
depths_p = [d['depth'] for d in depth_res]
|
| 820 |
+
errors_p = [max(d['error'], 1e-9) for d in depth_res]
|
| 821 |
+
params_p = [d['n_params'] for d in depth_res]
|
| 822 |
+
fig_dep = go.Figure()
|
| 823 |
+
fig_dep.add_trace(go.Scatter(
|
| 824 |
+
x=depths_p, y=errors_p,
|
| 825 |
+
mode='lines+markers', name='|E_VQE − E_target|',
|
| 826 |
+
line=dict(color='#a78bfa', width=2),
|
| 827 |
+
marker=dict(size=9, color='#a78bfa'),
|
| 828 |
+
))
|
| 829 |
+
fig_dep.add_hline(y=1e-3, line_color='#34d399', line_dash='dash',
|
| 830 |
+
annotation_text='Target accuracy 1e-3')
|
| 831 |
+
fig_dep.update_layout(
|
| 832 |
+
**PD, height=280,
|
| 833 |
+
xaxis=dict(title='Ansatz depth', dtick=1, gridcolor='#1a2d4a'),
|
| 834 |
+
yaxis=dict(title='Energy error (log)', type='log', gridcolor='#1a2d4a'),
|
| 835 |
+
title=dict(text='Depth scaling — justifies depth=3 (30q, 120 params)', font=dict(size=12)),
|
| 836 |
+
)
|
| 837 |
+
st.plotly_chart(fig_dep, use_container_width=True)
|
| 838 |
+
|
| 839 |
+
|
| 840 |
+
# ══════════════════════════════════════════════════════════════
|
| 841 |
+
# PAGE 3 — POLICY SIMULATOR
|
| 842 |
+
# ══════════════════════════════════════════════════════════════
|
| 843 |
+
elif page == "🎛 Policy Simulator":
|
| 844 |
+
st.markdown('<div class="page-title">ADAPT-VQE Policy Intervention Simulator</div>', unsafe_allow_html=True)
|
| 845 |
+
st.markdown('<div class="page-sub">6 supply-chain policy interventions · ADAPT-VQE gradient screening · Results on all 40 nodes (30q exec → 40q mapped via mean-field extrapolation)</div>', unsafe_allow_html=True)
|
| 846 |
+
|
| 847 |
+
if not pol:
|
| 848 |
+
st.error("QRSPPS_policy_results.pkl not found.")
|
| 849 |
+
st.stop()
|
| 850 |
+
|
| 851 |
+
st.markdown(f"""
|
| 852 |
+
<div class="alert-info">
|
| 853 |
+
<strong style='color:var(--accent)'>30q Execution → 40 Node Output</strong> —
|
| 854 |
+
Policies optimised on 30-qubit sub-network (Tier 0+1+2 full + top-10 retail by coupling strength).
|
| 855 |
+
Stress results mapped to all <strong>40 nodes</strong>: direct VQE for q0–q29,
|
| 856 |
+
mean-field extrapolation for q30–q39 (excluded retail).
|
| 857 |
+
NODE_LABELS and TIER drawn from full 40-node network.
|
| 858 |
+
</div>
|
| 859 |
+
""", unsafe_allow_html=True)
|
| 860 |
+
|
| 861 |
+
# All labels and tier from 40-node network
|
| 862 |
+
labels_40 = NODE_LABELS_40 # len 40
|
| 863 |
+
tier_40 = TIER_MAP_40 # {0..39: 0..3}
|
| 864 |
+
|
| 865 |
+
col_ctrl, col_viz = st.columns([1, 2])
|
| 866 |
+
|
| 867 |
+
with col_ctrl:
|
| 868 |
+
st.markdown("#### Policy Selection")
|
| 869 |
+
if not pol_names:
|
| 870 |
+
st.error("No policies found in pkl.")
|
| 871 |
+
st.stop()
|
| 872 |
+
selected = st.selectbox("Select intervention:", pol_names)
|
| 873 |
+
st.markdown("---")
|
| 874 |
+
|
| 875 |
+
st.markdown("#### Baseline vs Policy Stress")
|
| 876 |
+
base_stress_40 = get_pol_stress_40('No intervention')
|
| 877 |
+
sel_stress_40 = get_pol_stress_40(selected)
|
| 878 |
+
|
| 879 |
+
n_relieved = int(np.sum(sel_stress_40 < base_stress_40 - 0.01))
|
| 880 |
+
dE_sel = float(safe(pol_results, selected, 'delta_E', default=0))
|
| 881 |
+
E0_sel = float(safe(pol_results, selected, 'E0', default=vqe_e0_40))
|
| 882 |
+
roi_sel = float(safe(pol_results, selected, 'roi', default=0))
|
| 883 |
+
resil_sel = float(safe(pol_results, selected, 'resilience_score', default=0))
|
| 884 |
+
grad_sel = float(pol_gradients.get(selected, 0))
|
| 885 |
+
|
| 886 |
+
m1, m2 = st.columns(2)
|
| 887 |
+
m1.metric("ΔEnergy (40q)", f"{dE_sel:+.4f}", "lower = better")
|
| 888 |
+
m2.metric("Nodes relieved", f"{n_relieved}/40")
|
| 889 |
+
m3, m4 = st.columns(2)
|
| 890 |
+
m3.metric("Policy ROI", f"{roi_sel:.3f}", "|ΔE|/cost")
|
| 891 |
+
m4.metric("ADAPT Gradient", f"{grad_sel:.4f}", "screening score")
|
| 892 |
+
|
| 893 |
+
# Policy cost info
|
| 894 |
+
costs = {
|
| 895 |
+
'No intervention': 0, 'Rate hike': 2.0, 'Supplier subsidy': 5.0,
|
| 896 |
+
'Stockpile release': 3.0, 'Trade diversion': 1.5, 'Combined optimal': 8.0,
|
| 897 |
+
}
|
| 898 |
+
cost = costs.get(selected, 0)
|
| 899 |
+
if cost > 0:
|
| 900 |
+
st.markdown(f"""
|
| 901 |
+
<div class="alert-warn" style='font-size:0.8rem'>
|
| 902 |
+
💰 Policy cost: <strong>{cost}</strong> units · ROI = {roi_sel:.3f} ·
|
| 903 |
+
Resilience score = {resil_sel:.1f}/100
|
| 904 |
+
</div>
|
| 905 |
+
""", unsafe_allow_html=True)
|
| 906 |
+
|
| 907 |
+
st.markdown("---")
|
| 908 |
+
st.markdown("#### Compare Policies")
|
| 909 |
+
compare = st.multiselect("Select:", pol_names,
|
| 910 |
+
default=pol_names[:min(4, len(pol_names))])
|
| 911 |
+
|
| 912 |
+
with col_viz:
|
| 913 |
+
tab1, tab2, tab3, tab4 = st.tabs([
|
| 914 |
+
"Stress Map (40 nodes)", "Policy Ranking", "Delta Heatmap", "ROI Analysis"
|
| 915 |
+
])
|
| 916 |
+
|
| 917 |
+
with tab1:
|
| 918 |
+
# Bar chart — 40-node stress comparison
|
| 919 |
+
fig_bar = go.Figure()
|
| 920 |
+
fig_bar.add_trace(go.Bar(
|
| 921 |
+
x=list(range(N_NODES)), y=list(base_stress_40),
|
| 922 |
+
name='No intervention',
|
| 923 |
+
marker_color='rgba(71,85,105,0.7)',
|
| 924 |
+
width=0.4,
|
| 925 |
+
offset=-0.22,
|
| 926 |
+
))
|
| 927 |
+
fig_bar.add_trace(go.Bar(
|
| 928 |
+
x=list(range(N_NODES)), y=list(sel_stress_40),
|
| 929 |
+
name=selected,
|
| 930 |
+
marker_color=POLICY_COLORS.get(selected, '#38bdf8'),
|
| 931 |
+
opacity=0.85,
|
| 932 |
+
width=0.4,
|
| 933 |
+
offset=0.22,
|
| 934 |
+
))
|
| 935 |
+
# Mark tier boundaries
|
| 936 |
+
for tb in [2, 9, 20]:
|
| 937 |
+
fig_bar.add_vline(x=tb - 0.5, line_color='#1a2d4a',
|
| 938 |
+
line_dash='dash', line_width=1.2)
|
| 939 |
+
# Mark 30q/40q boundary
|
| 940 |
+
fig_bar.add_vline(x=29.5, line_color='#38bdf8', line_dash='dot',
|
| 941 |
+
line_width=1.5, annotation_text='30q boundary',
|
| 942 |
+
annotation_font_color='#38bdf8')
|
| 943 |
+
fig_bar.add_hline(y=0.5, line_color='#f87171', line_dash='dot',
|
| 944 |
+
line_width=1, opacity=0.5)
|
| 945 |
+
fig_bar.update_layout(
|
| 946 |
+
**PD, height=360, barmode='overlay',
|
| 947 |
+
xaxis=dict(
|
| 948 |
+
tickvals=list(range(0, N_NODES, 4)),
|
| 949 |
+
ticktext=[labels_40[i] for i in range(0, N_NODES, 4)],
|
| 950 |
+
tickangle=-40, gridcolor='#1a2d4a',
|
| 951 |
+
),
|
| 952 |
+
yaxis=dict(title='Stress P(|1⟩)', range=[0, 1.15], gridcolor='#1a2d4a'),
|
| 953 |
+
title=dict(
|
| 954 |
+
text=f'Policy: {selected} — all 40 nodes (dashed=tier boundary, blue dot=30q/40q boundary)',
|
| 955 |
+
font=dict(size=12)
|
| 956 |
+
),
|
| 957 |
+
legend=dict(orientation='h', y=1.08),
|
| 958 |
+
annotations=[
|
| 959 |
+
dict(x=1, y=1.08, xref='paper', yref='paper', showarrow=False,
|
| 960 |
+
text='← Direct VQE (q0-q29) | Mean-field extrap (q30-q39) →',
|
| 961 |
+
font=dict(size=9, color='#475569')),
|
| 962 |
+
],
|
| 963 |
+
)
|
| 964 |
+
st.plotly_chart(fig_bar, use_container_width=True)
|
| 965 |
+
|
| 966 |
+
# Tier summary table for selected policy
|
| 967 |
+
tbl_rows = []
|
| 968 |
+
for t in range(4):
|
| 969 |
+
nodes_t = [i for i in range(N_NODES) if tier_40.get(i) == t]
|
| 970 |
+
if nodes_t:
|
| 971 |
+
base_avg = float(np.mean([base_stress_40[i] for i in nodes_t]))
|
| 972 |
+
pol_avg = float(np.mean([sel_stress_40[i] for i in nodes_t]))
|
| 973 |
+
delta = pol_avg - base_avg
|
| 974 |
+
tbl_rows.append({
|
| 975 |
+
'Tier': TIER_NAMES[t],
|
| 976 |
+
'Nodes': len(nodes_t),
|
| 977 |
+
'Baseline stress': f'{base_avg:.4f}',
|
| 978 |
+
'Policy stress': f'{pol_avg:.4f}',
|
| 979 |
+
'ΔStress': f'{delta:+.4f}',
|
| 980 |
+
'Status': '✅ Relieved' if delta < -0.005 else ('⚠️ Worsened' if delta > 0.005 else '— Neutral'),
|
| 981 |
+
})
|
| 982 |
+
st.dataframe(pd.DataFrame(tbl_rows), hide_index=True, use_container_width=True)
|
| 983 |
+
|
| 984 |
+
with tab2:
|
| 985 |
+
if compare:
|
| 986 |
+
rank_rows = []
|
| 987 |
+
for pname in compare:
|
| 988 |
+
if pname in pol_results:
|
| 989 |
+
ps = get_pol_stress_40(pname)
|
| 990 |
+
bs = get_pol_stress_40('No intervention')
|
| 991 |
+
delta_s = ps - bs
|
| 992 |
+
rank_rows.append({
|
| 993 |
+
'Policy': pname,
|
| 994 |
+
'ΔEnergy (40q)': float(safe(pol_results, pname, 'delta_E', default=0)),
|
| 995 |
+
'Nodes Relieved': int(np.sum(delta_s < -0.01)),
|
| 996 |
+
'ADAPT Gradient': float(pol_gradients.get(pname, 0)),
|
| 997 |
+
'ROI': float(safe(pol_results, pname, 'roi', default=0)),
|
| 998 |
+
'Resilience': float(safe(pol_results, pname, 'resilience_score', default=0)),
|
| 999 |
+
})
|
| 1000 |
+
if rank_rows:
|
| 1001 |
+
rdf = pd.DataFrame(rank_rows).sort_values('ΔEnergy (40q)')
|
| 1002 |
+
cols_r = [POLICY_COLORS.get(p, '#38bdf8') for p in rdf['Policy']]
|
| 1003 |
+
|
| 1004 |
+
fig_rank = make_subplots(
|
| 1005 |
+
rows=1, cols=3,
|
| 1006 |
+
subplot_titles=('Energy reduction ΔE', 'ADAPT Gradient', 'ROI'),
|
| 1007 |
+
)
|
| 1008 |
+
fig_rank.add_trace(go.Bar(
|
| 1009 |
+
x=rdf['Policy'], y=rdf['ΔEnergy (40q)'],
|
| 1010 |
+
marker_color=cols_r, name='ΔE', showlegend=False
|
| 1011 |
+
), row=1, col=1)
|
| 1012 |
+
fig_rank.add_trace(go.Bar(
|
| 1013 |
+
x=rdf['Policy'], y=rdf['ADAPT Gradient'],
|
| 1014 |
+
marker_color=cols_r, name='Grad', showlegend=False
|
| 1015 |
+
), row=1, col=2)
|
| 1016 |
+
fig_rank.add_trace(go.Bar(
|
| 1017 |
+
x=rdf['Policy'], y=rdf['ROI'],
|
| 1018 |
+
marker_color=cols_r, name='ROI', showlegend=False
|
| 1019 |
+
), row=1, col=3)
|
| 1020 |
+
fig_rank.update_layout(
|
| 1021 |
+
**PD, height=360, showlegend=False,
|
| 1022 |
+
xaxis=dict(tickangle=-30),
|
| 1023 |
+
xaxis2=dict(tickangle=-30),
|
| 1024 |
+
xaxis3=dict(tickangle=-30),
|
| 1025 |
+
)
|
| 1026 |
+
st.plotly_chart(fig_rank, use_container_width=True)
|
| 1027 |
+
st.dataframe(rdf.round(4), hide_index=True, use_container_width=True)
|
| 1028 |
+
|
| 1029 |
+
with tab3:
|
| 1030 |
+
if compare and 'No intervention' in pol_results:
|
| 1031 |
+
base_40 = get_pol_stress_40('No intervention')
|
| 1032 |
+
dm_rows, dm_labels = [], []
|
| 1033 |
+
for pname in compare:
|
| 1034 |
+
if pname in pol_results:
|
| 1035 |
+
ps = get_pol_stress_40(pname)
|
| 1036 |
+
dm_rows.append(ps - base_40)
|
| 1037 |
+
dm_labels.append(pname)
|
| 1038 |
+
if dm_rows:
|
| 1039 |
+
dm = np.array(dm_rows)
|
| 1040 |
+
fig_hm = go.Figure(go.Heatmap(
|
| 1041 |
+
z=dm,
|
| 1042 |
+
x=[f"{labels_40[i]}" for i in range(N_NODES)],
|
| 1043 |
+
y=dm_labels,
|
| 1044 |
+
colorscale=[[0, '#064e3b'], [0.4, '#34d399'], [0.5, '#1a2d4a'],
|
| 1045 |
+
[0.6, '#fbbf24'], [1, '#f87171']],
|
| 1046 |
+
zmid=0,
|
| 1047 |
+
colorbar=dict(title='ΔStress', thickness=12),
|
| 1048 |
+
text=np.round(dm, 3).astype(str),
|
| 1049 |
+
texttemplate='%{text}',
|
| 1050 |
+
textfont=dict(size=8),
|
| 1051 |
+
))
|
| 1052 |
+
# Mark 30q/40q boundary
|
| 1053 |
+
fig_hm.add_vline(x=29.5, line_color='#38bdf8',
|
| 1054 |
+
line_width=1.5, line_dash='dot')
|
| 1055 |
+
fig_hm.update_layout(
|
| 1056 |
+
**PD, height=320,
|
| 1057 |
+
xaxis=dict(tickangle=-40,
|
| 1058 |
+
tickvals=list(range(0, N_NODES, 4)),
|
| 1059 |
+
ticktext=[labels_40[i] for i in range(0, N_NODES, 4)]),
|
| 1060 |
+
title=dict(
|
| 1061 |
+
text='Policy ΔStress heatmap — 40 nodes · green=relief, red=worsened',
|
| 1062 |
+
font=dict(size=12)
|
| 1063 |
+
),
|
| 1064 |
+
)
|
| 1065 |
+
st.plotly_chart(fig_hm, use_container_width=True)
|
| 1066 |
+
st.markdown("""
|
| 1067 |
+
<div style='font-size:0.75rem; color:var(--muted); margin-top:-8px'>
|
| 1068 |
+
Blue dotted line separates direct VQE nodes (left, q0–q29) from
|
| 1069 |
+
mean-field extrapolated nodes (right, q30–q39).
|
| 1070 |
+
</div>
|
| 1071 |
+
""", unsafe_allow_html=True)
|
| 1072 |
+
|
| 1073 |
+
with tab4:
|
| 1074 |
+
if pol_results:
|
| 1075 |
+
all_rows = []
|
| 1076 |
+
for pname in pol_names:
|
| 1077 |
+
if pname in pol_results:
|
| 1078 |
+
ps = get_pol_stress_40(pname)
|
| 1079 |
+
bs = get_pol_stress_40('No intervention')
|
| 1080 |
+
all_rows.append({
|
| 1081 |
+
'Policy': pname,
|
| 1082 |
+
'E0 (40q)': float(safe(pol_results, pname, 'E0', default=0)),
|
| 1083 |
+
'ΔEnergy': float(safe(pol_results, pname, 'delta_E', default=0)),
|
| 1084 |
+
'Nodes relieved': int(np.sum(ps < bs - 0.01)),
|
| 1085 |
+
'ROI': float(safe(pol_results, pname, 'roi', default=0)),
|
| 1086 |
+
'Resilience': float(safe(pol_results, pname, 'resilience_score', default=0)),
|
| 1087 |
+
'ADAPT Gradient': float(pol_gradients.get(pname, 0)),
|
| 1088 |
+
'Cost (units)': costs.get(pname, 0),
|
| 1089 |
+
})
|
| 1090 |
+
if all_rows:
|
| 1091 |
+
adf = pd.DataFrame(all_rows)
|
| 1092 |
+
fig_roi = go.Figure()
|
| 1093 |
+
for _, row in adf.iterrows():
|
| 1094 |
+
if row['Policy'] == 'No intervention':
|
| 1095 |
+
continue
|
| 1096 |
+
fig_roi.add_trace(go.Scatter(
|
| 1097 |
+
x=[row['ROI']], y=[row['Resilience']],
|
| 1098 |
+
mode='markers+text',
|
| 1099 |
+
name=row['Policy'],
|
| 1100 |
+
text=[row['Policy']],
|
| 1101 |
+
textposition='top center',
|
| 1102 |
+
textfont=dict(size=9),
|
| 1103 |
+
marker=dict(
|
| 1104 |
+
size=max(12, row['Nodes relieved'] * 2 + 12),
|
| 1105 |
+
color=POLICY_COLORS.get(row['Policy'], '#38bdf8'),
|
| 1106 |
+
line=dict(color='white', width=1),
|
| 1107 |
+
),
|
| 1108 |
+
))
|
| 1109 |
+
fig_roi.update_layout(
|
| 1110 |
+
**PD, height=340, showlegend=False,
|
| 1111 |
+
xaxis=dict(title='ROI (|ΔE| / cost)', gridcolor='#1a2d4a'),
|
| 1112 |
+
yaxis=dict(title='Supply-chain resilience score (0–100)', gridcolor='#1a2d4a'),
|
| 1113 |
+
title=dict(text='Policy ROI vs Resilience (bubble size = nodes relieved)',
|
| 1114 |
+
font=dict(size=12)),
|
| 1115 |
+
)
|
| 1116 |
+
st.plotly_chart(fig_roi, use_container_width=True)
|
| 1117 |
+
st.dataframe(adf.round(4), hide_index=True, use_container_width=True)
|
| 1118 |
+
|
| 1119 |
+
|
| 1120 |
+
# ══════════════════════════════════════════════════════════════
|
| 1121 |
+
# PAGE 4 — TAIL RISK & CASCADES
|
| 1122 |
+
# ══════════════════════════════════════════════════════════════
|
| 1123 |
+
elif page == "💥 Tail Risk & Cascades":
|
| 1124 |
+
st.markdown('<div class="page-title">DOS-QPE Tail Risk & Cascade Dynamics</div>', unsafe_allow_html=True)
|
| 1125 |
+
st.markdown('<div class="page-sub">Full eigenspectrum via 64-step Trotter QPE · Boltzmann tail risk · 10-snapshot cascade propagation on 40-node network</div>', unsafe_allow_html=True)
|
| 1126 |
+
|
| 1127 |
+
if not dos:
|
| 1128 |
+
st.error("QRSPPS_dosqpe_results.pkl not found.")
|
| 1129 |
+
st.stop()
|
| 1130 |
+
|
| 1131 |
+
spec_w = float(safe(dos, 'spectral_width_est', default=1.73))
|
| 1132 |
+
# Header metrics
|
| 1133 |
+
m1, m2, m3, m4 = st.columns(4)
|
| 1134 |
+
m1.metric("DOS-QPE Trotter Steps", "64", f"T_max = 15.0")
|
| 1135 |
+
m2.metric("Spectral Width (40q)", f"{spec_w:.4f}", "gap × (40/30)")
|
| 1136 |
+
m3.metric("Catastrophe Threshold E_cut", f"{E_cutoff:.3f}", "E₀ + 0.85·Δspec")
|
| 1137 |
+
m4.metric("Cascade Snapshots", "10", "T_casc = 6.0 units")
|
| 1138 |
+
|
| 1139 |
+
col1, col2 = st.columns([3, 2])
|
| 1140 |
+
|
| 1141 |
+
with col1:
|
| 1142 |
+
st.markdown("#### Tail Risk vs Market Volatility (All Policies)")
|
| 1143 |
+
T_sel = st.slider("Highlight volatility level T", 0.01, 10.0, 1.0, 0.05)
|
| 1144 |
+
|
| 1145 |
+
fig_tr = go.Figure()
|
| 1146 |
+
for pname, tr in tail_risks.items():
|
| 1147 |
+
tr_arr = np.array(tr, dtype=float)
|
| 1148 |
+
t_arr = temps
|
| 1149 |
+
if len(tr_arr) != len(t_arr):
|
| 1150 |
+
tr_arr = np.interp(np.linspace(0, 1, len(t_arr)),
|
| 1151 |
+
np.linspace(0, 1, len(tr_arr)), tr_arr)
|
| 1152 |
+
ls = 'dash' if pname == 'No intervention' else 'solid'
|
| 1153 |
+
fig_tr.add_trace(go.Scatter(
|
| 1154 |
+
x=t_arr, y=tr_arr * 100,
|
| 1155 |
+
mode='lines', name=pname,
|
| 1156 |
+
line=dict(color=POLICY_COLORS.get(pname, '#38bdf8'), width=2.2, dash=ls),
|
| 1157 |
+
))
|
| 1158 |
+
fig_tr.add_vline(x=T_sel, line_dash='dot', line_color='#fb923c', line_width=1.5,
|
| 1159 |
+
annotation_text=f'T={T_sel:.2f}',
|
| 1160 |
+
annotation_font_color='#fb923c')
|
| 1161 |
+
fig_tr.add_hrect(y0=20, y1=100,
|
| 1162 |
+
fillcolor='rgba(248,113,113,0.04)',
|
| 1163 |
+
line_width=0,
|
| 1164 |
+
annotation_text='High-risk zone',
|
| 1165 |
+
annotation_font_color='#f87171',
|
| 1166 |
+
annotation_position='top left')
|
| 1167 |
+
fig_tr.update_layout(
|
| 1168 |
+
**PD, height=360,
|
| 1169 |
+
xaxis=dict(title='Temperature T (market volatility)', type='log', gridcolor='#1a2d4a'),
|
| 1170 |
+
yaxis=dict(title='P(catastrophe) %', range=[0, 55], gridcolor='#1a2d4a'),
|
| 1171 |
+
title=dict(text='Quantum Boltzmann tail risk — lower = safer under intervention',
|
| 1172 |
+
font=dict(size=12)),
|
| 1173 |
+
legend=dict(orientation='v', font=dict(size=9)),
|
| 1174 |
+
)
|
| 1175 |
+
st.plotly_chart(fig_tr, use_container_width=True)
|
| 1176 |
+
|
| 1177 |
+
# Risk cards at selected T
|
| 1178 |
+
st.markdown(f"#### Catastrophe Probability at T = {T_sel:.2f}")
|
| 1179 |
+
tr_cols = st.columns(len(tail_risks))
|
| 1180 |
+
for col_i, (pname, tr) in enumerate(tail_risks.items()):
|
| 1181 |
+
tr_arr = np.array(tr, dtype=float)
|
| 1182 |
+
t_idx = int(np.argmin(np.abs(temps - T_sel)))
|
| 1183 |
+
t_idx = min(t_idx, len(tr_arr) - 1)
|
| 1184 |
+
risk_v = float(tr_arr[t_idx]) * 100
|
| 1185 |
+
c = '#f87171' if risk_v > 10 else ('#fbbf24' if risk_v > 2 else '#34d399')
|
| 1186 |
+
with tr_cols[col_i]:
|
| 1187 |
+
st.markdown(f"""
|
| 1188 |
+
<div class='qcard' style='text-align:center; border-top:2px solid {c}; padding:12px'>
|
| 1189 |
+
<div style='font-family:Orbitron; font-size:1.2rem; color:{c}'>{risk_v:.2f}%</div>
|
| 1190 |
+
<div style='color:var(--muted); font-size:0.66rem; margin-top:3px'>{pname}</div>
|
| 1191 |
+
</div>
|
| 1192 |
+
""", unsafe_allow_html=True)
|
| 1193 |
+
|
| 1194 |
+
with col2:
|
| 1195 |
+
st.markdown("#### Density of States (DOS-QPE)")
|
| 1196 |
+
if len(energies_40) > 0 and len(dos_vals) > 0:
|
| 1197 |
+
fig_dos = go.Figure()
|
| 1198 |
+
fig_dos.add_trace(go.Scatter(
|
| 1199 |
+
x=list(energies_40), y=list(dos_vals),
|
| 1200 |
+
mode='lines',
|
| 1201 |
+
line=dict(color='#38bdf8', width=2),
|
| 1202 |
+
fill='tozeroy', fillcolor='rgba(56,189,248,0.08)',
|
| 1203 |
+
name='DOS',
|
| 1204 |
+
))
|
| 1205 |
+
fig_dos.add_vline(x=abs(vqe_e0_40) / N_NODES, line_color='#34d399',
|
| 1206 |
+
line_dash='dash',
|
| 1207 |
+
annotation_text=f'E₀/node={vqe_e0_40/N_NODES:.3f}',
|
| 1208 |
+
annotation_font_color='#34d399')
|
| 1209 |
+
fig_dos.update_layout(
|
| 1210 |
+
**PD, height=240,
|
| 1211 |
+
xaxis=dict(title='Energy (40q-scaled)', gridcolor='#1a2d4a'),
|
| 1212 |
+
yaxis=dict(title='DOS (arb.)', gridcolor='#1a2d4a'),
|
| 1213 |
+
title=dict(text='DOS via QPE — 30q Trotter → FFT → 40q', font=dict(size=11)),
|
| 1214 |
+
)
|
| 1215 |
+
st.plotly_chart(fig_dos, use_container_width=True)
|
| 1216 |
+
|
| 1217 |
+
st.markdown("#### Survival Amplitude ⟨ψ|e⁻ⁱᴴᵗ|ψ⟩")
|
| 1218 |
+
if len(survival_amp) > 0 and len(times_dos) > 0:
|
| 1219 |
+
fig_sa = go.Figure()
|
| 1220 |
+
fig_sa.add_trace(go.Scatter(x=list(times_dos), y=list(np.real(survival_amp)),
|
| 1221 |
+
mode='lines', name='Re[A(t)]',
|
| 1222 |
+
line=dict(color='#38bdf8', width=1.5)))
|
| 1223 |
+
fig_sa.add_trace(go.Scatter(x=list(times_dos), y=list(np.imag(survival_amp)),
|
| 1224 |
+
mode='lines', name='Im[A(t)]',
|
| 1225 |
+
line=dict(color='#f87171', width=1.2)))
|
| 1226 |
+
fig_sa.add_trace(go.Scatter(x=list(times_dos), y=list(np.abs(survival_amp)),
|
| 1227 |
+
mode='lines', name='|A(t)|',
|
| 1228 |
+
line=dict(color='#34d399', width=1.5, dash='dash')))
|
| 1229 |
+
fig_sa.update_layout(
|
| 1230 |
+
**PD, height=240,
|
| 1231 |
+
xaxis=dict(title='Time t', gridcolor='#1a2d4a'),
|
| 1232 |
+
yaxis=dict(title='Amplitude', gridcolor='#1a2d4a'),
|
| 1233 |
+
title=dict(text='Survival amplitude — 30q Trotter evolution', font=dict(size=11)),
|
| 1234 |
+
legend=dict(font=dict(size=9)),
|
| 1235 |
+
)
|
| 1236 |
+
st.plotly_chart(fig_sa, use_container_width=True)
|
| 1237 |
+
|
| 1238 |
+
st.markdown("#### Ground-State Catastrophe Overlap")
|
| 1239 |
+
if cat_overlaps:
|
| 1240 |
+
names_co = list(cat_overlaps.keys())
|
| 1241 |
+
vals_co = [float(cat_overlaps[n]) * 100 for n in names_co]
|
| 1242 |
+
cols_co = [POLICY_COLORS.get(n, '#38bdf8') for n in names_co]
|
| 1243 |
+
fig_co = go.Figure(go.Bar(
|
| 1244 |
+
x=vals_co, y=names_co, orientation='h',
|
| 1245 |
+
marker=dict(color=cols_co, line=dict(color='rgba(0,0,0,0.3)', width=1)),
|
| 1246 |
+
text=[f'{v:.3f}%' for v in vals_co],
|
| 1247 |
+
textposition='outside',
|
| 1248 |
+
textfont=dict(size=9),
|
| 1249 |
+
))
|
| 1250 |
+
fig_co.update_layout(
|
| 1251 |
+
**PD, height=200,
|
| 1252 |
+
xaxis=dict(title='Catastrophe overlap (%)', gridcolor='#1a2d4a'),
|
| 1253 |
+
title=dict(text='Ground-state catastrophic risk by policy', font=dict(size=11)),
|
| 1254 |
+
)
|
| 1255 |
+
st.plotly_chart(fig_co, use_container_width=True)
|
| 1256 |
+
|
| 1257 |
+
# Cascade dynamics — full 40-node heatmap
|
| 1258 |
+
st.markdown("---")
|
| 1259 |
+
st.markdown("#### Cascade Failure Dynamics — 40-Node Network, 10 Time Snapshots")
|
| 1260 |
+
st.markdown("""
|
| 1261 |
+
<div style='font-size:0.8rem; color:var(--text2); margin-bottom:8px'>
|
| 1262 |
+
30q Trotter real-time evolution → stress propagation mapped to all 40 nodes.
|
| 1263 |
+
Dashed horizontal line separates direct VQE region (above, q0–q29) from mean-field extrapolated retail (below, q30–q39).
|
| 1264 |
+
</div>
|
| 1265 |
+
""", unsafe_allow_html=True)
|
| 1266 |
+
|
| 1267 |
+
if cascade_40 is not None and cascade_40.size > 0:
|
| 1268 |
+
n_snaps, n_casc = cascade_40.shape
|
| 1269 |
+
casc_labels = [f"{NODE_LABELS_40[i]} [T{TIER_MAP_40.get(i,3)}]"
|
| 1270 |
+
for i in range(min(n_casc, N_NODES))]
|
| 1271 |
+
fig_casc = go.Figure(go.Heatmap(
|
| 1272 |
+
z=cascade_40.T,
|
| 1273 |
+
x=[f"t={float(t):.1f}" for t in times_dyn[:n_snaps]],
|
| 1274 |
+
y=casc_labels,
|
| 1275 |
+
colorscale=[[0, '#064e3b'], [0.35, '#34d399'], [0.65, '#fbbf24'], [1, '#f87171']],
|
| 1276 |
+
zmin=0, zmax=1,
|
| 1277 |
+
colorbar=dict(title='Stress P(|1⟩)', thickness=12),
|
| 1278 |
+
))
|
| 1279 |
+
# Tier boundary lines (horizontal)
|
| 1280 |
+
cumul = 0
|
| 1281 |
+
for t in range(3):
|
| 1282 |
+
tc = sum(1 for i in range(min(n_casc, N_NODES)) if TIER_MAP_40.get(i) == t)
|
| 1283 |
+
cumul += tc
|
| 1284 |
+
fig_casc.add_hline(y=cumul - 0.5, line_color='#1a2d4a', line_width=1.5)
|
| 1285 |
+
# 30q/40q boundary
|
| 1286 |
+
vqe_boundary = N_VQE_Q
|
| 1287 |
+
fig_casc.add_hline(y=vqe_boundary - 0.5, line_color='#38bdf8',
|
| 1288 |
+
line_width=2, line_dash='dot')
|
| 1289 |
+
|
| 1290 |
+
fig_casc.update_layout(
|
| 1291 |
+
**PD, height=max(380, n_casc * 12),
|
| 1292 |
+
xaxis=dict(title='Time snapshot'),
|
| 1293 |
+
yaxis=dict(autorange='reversed', tickfont=dict(size=9)),
|
| 1294 |
+
title=dict(
|
| 1295 |
+
text='Cascade propagation — yellow/red = increasing stress from RM-A shock · blue dashed = 30q/40q boundary',
|
| 1296 |
+
font=dict(size=12)
|
| 1297 |
+
),
|
| 1298 |
+
)
|
| 1299 |
+
st.plotly_chart(fig_casc, use_container_width=True)
|
| 1300 |
+
|
| 1301 |
+
final_stress = cascade_40[-1]
|
| 1302 |
+
st.markdown(f"""
|
| 1303 |
+
<div class="alert-danger">
|
| 1304 |
+
<strong style='color:var(--red)'>Final cascade state (t={float(times_dyn[-1]):.1f})</strong> —
|
| 1305 |
+
Mean stress across 40 nodes = <strong>{float(np.mean(final_stress)):.4f}</strong> ·
|
| 1306 |
+
Nodes above 0.5 threshold = <strong>{int(np.sum(final_stress > 0.5))}/40</strong> ·
|
| 1307 |
+
Worst node = <strong>{NODE_LABELS_40[int(np.argmax(final_stress))]}</strong>
|
| 1308 |
+
({float(np.max(final_stress)):.4f})
|
| 1309 |
+
</div>
|
| 1310 |
+
""", unsafe_allow_html=True)
|
| 1311 |
+
|
| 1312 |
+
|
| 1313 |
+
# ══════════════════════════════════════════════════════════════
|
| 1314 |
+
# PAGE 5 — QUBIT SCALING
|
| 1315 |
+
# ══════════════════════════════════════════════════════════════
|
| 1316 |
+
elif page == "📈 Qubit Scaling":
|
| 1317 |
+
st.markdown('<div class="page-title">Qubit Scaling — Fujitsu A64FX Supercomputer</div>', unsafe_allow_html=True)
|
| 1318 |
+
st.markdown('<div class="page-sub">State-vector simulation: 12–30q measured · 40q Hamiltonian encoded · Exponential fit validates quantum advantage regime</div>', unsafe_allow_html=True)
|
| 1319 |
+
|
| 1320 |
+
if not scl:
|
| 1321 |
+
st.error("QRSPPS_scaling_results.pkl not found.")
|
| 1322 |
+
st.stop()
|
| 1323 |
+
|
| 1324 |
+
t40h = t_40q / 3600
|
| 1325 |
+
# Header metrics
|
| 1326 |
+
m1, m2, m3, m4, m5, m6 = st.columns(6)
|
| 1327 |
+
m1.metric("Max qubits measured", f"{max(scl_ns) if scl_ns else 30}q", "Fujitsu A64FX MPI")
|
| 1328 |
+
m2.metric("30q state-vector", "17.2 GB", "node RAM ceiling")
|
| 1329 |
+
m3.metric("40q state-vector", "17,592 GB", "17.6 TB — impossible")
|
| 1330 |
+
m4.metric("40q eval time", f"{t40h:.0f} h", f"{t_40q:,.0f}s predicted")
|
| 1331 |
+
m5.metric("Exponential fit R²", f"{r_squared:.4f}", "near-perfect")
|
| 1332 |
+
m6.metric("Doubling rate", f"{doubling_rate:.4f}", "per qubit")
|
| 1333 |
+
|
| 1334 |
+
st.markdown(f"""
|
| 1335 |
+
<div class="alert-danger" style='margin:12px 0'>
|
| 1336 |
+
<strong style='color:var(--red)'>40-Qubit Quantum Advantage Regime</strong>
|
| 1337 |
+
—
|
| 1338 |
+
<span style='font-size:0.88rem'>
|
| 1339 |
+
QR-SPPS Hamiltonian encodes a <strong>40-node supply chain</strong> in Hilbert space
|
| 1340 |
+
2⁴⁰ = 1,099,511,627,776 states.
|
| 1341 |
+
State-vector simulation benchmarked to the physical node limit:
|
| 1342 |
+
<strong>30q = 17.2 GB (measured, MPI)</strong>.
|
| 1343 |
+
Exponential scaling: R² = <strong>{r_squared:.4f}</strong> over 6 MPI data points (24q–30q).
|
| 1344 |
+
Predicted 40q runtime: <strong>{t40h:.0f} hours per evaluation</strong> —
|
| 1345 |
+
classical state-vector is intractable. This is the quantum advantage regime.
|
| 1346 |
+
</span>
|
| 1347 |
+
</div>
|
| 1348 |
+
""", unsafe_allow_html=True)
|
| 1349 |
+
|
| 1350 |
+
c1, c2 = st.columns(2)
|
| 1351 |
+
|
| 1352 |
+
with c1:
|
| 1353 |
+
src_styles = {
|
| 1354 |
+
'Single-node': dict(color='#38bdf8', symbol='circle'),
|
| 1355 |
+
'MPI measured': dict(color='#34d399', symbol='square'),
|
| 1356 |
+
'Extrapolated': dict(color='#fb923c', symbol='triangle-up'),
|
| 1357 |
+
}
|
| 1358 |
+
fig_rt = go.Figure()
|
| 1359 |
+
for src_type, style in src_styles.items():
|
| 1360 |
+
idx = [i for i, s in enumerate(scl_srcs) if s == src_type]
|
| 1361 |
+
if idx:
|
| 1362 |
+
xs = [scl_ns[i] for i in idx]
|
| 1363 |
+
ys = [scl_times[i] for i in idx]
|
| 1364 |
+
fig_rt.add_trace(go.Scatter(
|
| 1365 |
+
x=xs, y=ys, mode='lines+markers', name=src_type,
|
| 1366 |
+
line=dict(color=style['color'], width=2.2,
|
| 1367 |
+
dash='dash' if src_type == 'Extrapolated' else 'solid'),
|
| 1368 |
+
marker=dict(color=style['color'], size=10, symbol=style['symbol']),
|
| 1369 |
+
))
|
| 1370 |
+
# Exponential fit line
|
| 1371 |
+
if scl_ns:
|
| 1372 |
+
n_fit = list(np.linspace(min(scl_ns), 42, 200))
|
| 1373 |
+
y_fit = [t_at_base * 2 ** (doubling_rate * (n - scl_ns[0])) for n in n_fit]
|
| 1374 |
+
fig_rt.add_trace(go.Scatter(
|
| 1375 |
+
x=n_fit, y=y_fit, mode='lines',
|
| 1376 |
+
name=f'O(2^n) fit R²={r_squared:.4f}',
|
| 1377 |
+
line=dict(color='#334155', dash='dot', width=1.5),
|
| 1378 |
+
))
|
| 1379 |
+
# 30q marker
|
| 1380 |
+
t30_val = next((scl_times[i] for i, n in enumerate(scl_ns) if n == 30), None)
|
| 1381 |
+
if t30_val:
|
| 1382 |
+
fig_rt.add_trace(go.Scatter(
|
| 1383 |
+
x=[30], y=[t30_val], mode='markers', name='30q (QRSPPS exec)',
|
| 1384 |
+
marker=dict(color='#38bdf8', size=16, symbol='diamond',
|
| 1385 |
+
line=dict(color='white', width=2)),
|
| 1386 |
+
))
|
| 1387 |
+
# 40q star
|
| 1388 |
+
fig_rt.add_trace(go.Scatter(
|
| 1389 |
+
x=[40], y=[t_40q], mode='markers', name=f'40q predicted ({t40h:.0f}h)',
|
| 1390 |
+
marker=dict(color='#f87171', size=18, symbol='star'),
|
| 1391 |
+
))
|
| 1392 |
+
fig_rt.add_annotation(
|
| 1393 |
+
x=40, y=np.log10(t_40q) if t_40q > 0 else 6,
|
| 1394 |
+
text=f"40q<br>{t40h:.0f}h", showarrow=True,
|
| 1395 |
+
arrowhead=2, arrowcolor='#f87171',
|
| 1396 |
+
font=dict(color='#f87171', size=11, family='JetBrains Mono'),
|
| 1397 |
+
ax=-55, ay=-40, bgcolor='rgba(248,113,113,0.12)',
|
| 1398 |
+
)
|
| 1399 |
+
# Vertical markers
|
| 1400 |
+
fig_rt.add_vline(x=30, line_color='#38bdf8', line_dash='dot', line_width=1.5,
|
| 1401 |
+
annotation_text='30q QRSPPS', annotation_font_color='#38bdf8')
|
| 1402 |
+
fig_rt.add_vline(x=40, line_color='#f87171', line_dash='dot', line_width=1,
|
| 1403 |
+
annotation_text='40q target', annotation_font_color='#f87171')
|
| 1404 |
+
fig_rt.update_layout(
|
| 1405 |
+
**PD, height=400,
|
| 1406 |
+
xaxis=dict(title='Number of qubits', range=[10, 43], gridcolor='#1a2d4a'),
|
| 1407 |
+
yaxis=dict(title='Time per eval (s, log scale)', type='log', gridcolor='#1a2d4a'),
|
| 1408 |
+
title=dict(
|
| 1409 |
+
text=f'Runtime scaling — rate={doubling_rate:.4f}/q · R²={r_squared:.4f}',
|
| 1410 |
+
font=dict(size=12)
|
| 1411 |
+
),
|
| 1412 |
+
)
|
| 1413 |
+
st.plotly_chart(fig_rt, use_container_width=True)
|
| 1414 |
+
|
| 1415 |
+
with c2:
|
| 1416 |
+
fig_mem = go.Figure()
|
| 1417 |
+
fig_mem.add_trace(go.Scatter(
|
| 1418 |
+
x=scl_ns, y=scl_mems, mode='lines+markers', name='State-vector RAM',
|
| 1419 |
+
line=dict(color='#a78bfa', width=2.2),
|
| 1420 |
+
marker=dict(color='#a78bfa', size=10),
|
| 1421 |
+
fill='tozeroy', fillcolor='rgba(167,139,250,0.07)',
|
| 1422 |
+
))
|
| 1423 |
+
fig_mem.add_trace(go.Scatter(
|
| 1424 |
+
x=[40], y=[17592000], mode='markers', name='40q = 17.6 TB',
|
| 1425 |
+
marker=dict(color='#f87171', size=18, symbol='star'),
|
| 1426 |
+
))
|
| 1427 |
+
if scl_ns:
|
| 1428 |
+
fig_mem.add_trace(go.Scatter(
|
| 1429 |
+
x=[scl_ns[-1], 40], y=[scl_mems[-1], 17592000],
|
| 1430 |
+
mode='lines', name='Extrapolated',
|
| 1431 |
+
line=dict(color='#f87171', dash='dash', width=1.5),
|
| 1432 |
+
))
|
| 1433 |
+
fig_mem.add_hline(y=28900, line_color='#f87171', line_dash='dash',
|
| 1434 |
+
annotation_text='Node RAM limit 28.9 GB',
|
| 1435 |
+
annotation_font_color='#f87171')
|
| 1436 |
+
fig_mem.add_hline(y=17180, line_color='#fbbf24', line_dash='dash',
|
| 1437 |
+
annotation_text='30q = 17.2 GB (measured)',
|
| 1438 |
+
annotation_font_color='#fbbf24')
|
| 1439 |
+
fig_mem.add_annotation(
|
| 1440 |
+
x=40, y=4,
|
| 1441 |
+
text="40q = 17.6 TB<br>(impossible SV)",
|
| 1442 |
+
showarrow=True, arrowhead=2, arrowcolor='#f87171',
|
| 1443 |
+
font=dict(color='#f87171', size=10, family='JetBrains Mono'),
|
| 1444 |
+
ax=-65, ay=-35, bgcolor='rgba(248,113,113,0.12)',
|
| 1445 |
+
)
|
| 1446 |
+
fig_mem.update_layout(
|
| 1447 |
+
**PD, height=400,
|
| 1448 |
+
xaxis=dict(title='Number of qubits', range=[10, 43], gridcolor='#1a2d4a'),
|
| 1449 |
+
yaxis=dict(title='Memory (MB, log)', type='log', gridcolor='#1a2d4a'),
|
| 1450 |
+
title=dict(text='Memory scaling — 30q = node limit · 40q = 17.6 TB', font=dict(size=12)),
|
| 1451 |
+
)
|
| 1452 |
+
st.plotly_chart(fig_mem, use_container_width=True)
|
| 1453 |
+
|
| 1454 |
+
# Benchmark table
|
| 1455 |
+
st.markdown("#### Complete Benchmark Data — 12q to 40q (+ Extrapolated)")
|
| 1456 |
+
if scl_all:
|
| 1457 |
+
tbl = []
|
| 1458 |
+
for r, src in zip(scl_all, scl_srcs):
|
| 1459 |
+
mem_mb = float(r.get('state_vec_mb', 0))
|
| 1460 |
+
tbl.append({
|
| 1461 |
+
'Qubits': f"{r['n_qubits']}q",
|
| 1462 |
+
'Time/eval': f"{r['mean_time']:.3f}s" if r['mean_time'] < 3600 else f"{r['mean_time']/3600:.1f}h",
|
| 1463 |
+
'State-vector RAM': f"{mem_mb/1024:.2f} GB" if mem_mb > 1024 else f"{mem_mb:.1f} MB",
|
| 1464 |
+
'Source': src,
|
| 1465 |
+
'Hardware': 'Fujitsu A64FX MPI' if src == 'MPI measured' else ('Extrapolated' if src == 'Extrapolated' else 'A64FX single-node'),
|
| 1466 |
+
'VQE Energy': f"{float(r.get('energy', 0)):.4f}" if r.get('energy') else 'N/A',
|
| 1467 |
+
})
|
| 1468 |
+
tbl.append({
|
| 1469 |
+
'Qubits': '40q',
|
| 1470 |
+
'Time/eval': f"{t40h:.0f}h ({t_40q:,.0f}s)",
|
| 1471 |
+
'State-vector RAM': '17,592 GB (17.6 TB)',
|
| 1472 |
+
'Source': f'Extrapolated R²={r_squared:.4f}',
|
| 1473 |
+
'Hardware': 'Impossible — requires ~606 × A64FX nodes',
|
| 1474 |
+
'VQE Energy': f'{vqe_e0_40:.4f} (encoded)',
|
| 1475 |
+
})
|
| 1476 |
+
st.dataframe(pd.DataFrame(tbl), hide_index=True, use_container_width=True)
|
| 1477 |
+
|
| 1478 |
+
# VQE convergence at 12q
|
| 1479 |
+
if hist_12:
|
| 1480 |
+
st.markdown("---")
|
| 1481 |
+
st.markdown("#### VQE Convergence at 12q — Benchmark Hamiltonian")
|
| 1482 |
+
fig_12 = go.Figure(go.Scatter(
|
| 1483 |
+
x=list(range(len(hist_12))), y=list(hist_12),
|
| 1484 |
+
mode='lines', line=dict(color='#38bdf8', width=2),
|
| 1485 |
+
fill='tozeroy', fillcolor='rgba(56,189,248,0.06)',
|
| 1486 |
+
))
|
| 1487 |
+
fig_12.add_hline(y=float(hist_12[-1]), line_color='#34d399', line_dash='dash',
|
| 1488 |
+
annotation_text=f'E_final={float(hist_12[-1]):.4f}',
|
| 1489 |
+
annotation_font_color='#34d399')
|
| 1490 |
+
fig_12.update_layout(
|
| 1491 |
+
**PD, height=240,
|
| 1492 |
+
xaxis=dict(title='Iteration', gridcolor='#1a2d4a'),
|
| 1493 |
+
yaxis=dict(title='Energy', gridcolor='#1a2d4a'),
|
| 1494 |
+
title=dict(text='12q VQE convergence — supply-chain benchmark Hamiltonian', font=dict(size=12)),
|
| 1495 |
+
)
|
| 1496 |
+
st.plotly_chart(fig_12, use_container_width=True)
|
| 1497 |
+
|
| 1498 |
+
|
| 1499 |
+
# ══════════════════════════════════════════════════════════════
|
| 1500 |
+
# PAGE 6 — QARP FEEDBACK
|
| 1501 |
+
# ══════════════════════════════════════════════════════════════
|
| 1502 |
+
elif page == "📋 QARP Feedback":
|
| 1503 |
+
st.markdown('<div class="page-title">Fujitsu QARP Usability Feedback</div>', unsafe_allow_html=True)
|
| 1504 |
+
st.markdown("""
|
| 1505 |
+
<div class="page-sub">
|
| 1506 |
+
QR-SPPS Project · Fujitsu Quantum Simulator Challenge 2025-26 ·
|
| 1507 |
+
Comprehensive feedback on QARP API, algorithms, and platform experience
|
| 1508 |
+
</div>
|
| 1509 |
+
""", unsafe_allow_html=True)
|
| 1510 |
+
|
| 1511 |
+
# Overall score banner
|
| 1512 |
+
st.markdown("""
|
| 1513 |
+
<div style='background:linear-gradient(135deg, rgba(56,189,248,0.08), rgba(52,211,153,0.06));
|
| 1514 |
+
border:1px solid var(--border2); border-radius:14px;
|
| 1515 |
+
padding:20px 28px; margin-bottom:20px;
|
| 1516 |
+
display:flex; align-items:center; gap:28px'>
|
| 1517 |
+
<div style='text-align:center; min-width:90px'>
|
| 1518 |
+
<div style='font-family:Orbitron; font-size:2.4rem; font-weight:900; color:#38bdf8'>4.1</div>
|
| 1519 |
+
<div style='font-size:0.65rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.12em'>Overall Score</div>
|
| 1520 |
+
<div style='color:#fbbf24; font-size:1.1rem; margin-top:2px'>★★★★☆</div>
|
| 1521 |
+
</div>
|
| 1522 |
+
<div style='flex:1'>
|
| 1523 |
+
<div style='font-weight:700; font-size:1.05rem; color:var(--text); margin-bottom:6px'>
|
| 1524 |
+
Fujitsu QARP: Production-Ready Algorithms · ARM Compatibility Needs Attention
|
| 1525 |
+
</div>
|
| 1526 |
+
<div style='font-size:0.84rem; color:var(--text2); line-height:1.7'>
|
| 1527 |
+
QARP's algorithm implementations (VQE, ADAPT-VQE gradient screening, DOS-QPE) are
|
| 1528 |
+
scientifically sound and enabled genuinely novel supply-chain quantum simulation.
|
| 1529 |
+
The primary obstacle is QulacsEngine incompatibility with A64FX ARM —
|
| 1530 |
+
once resolved via TketEngine(AerBackend), all algorithms performed excellently.
|
| 1531 |
+
The OpenFermion + QARP Hamiltonian pipeline mapped naturally to our Ising supply-chain model.
|
| 1532 |
+
</div>
|
| 1533 |
+
</div>
|
| 1534 |
+
</div>
|
| 1535 |
+
""", unsafe_allow_html=True)
|
| 1536 |
+
|
| 1537 |
+
# Ratings grid
|
| 1538 |
+
st.markdown("### Component Ratings")
|
| 1539 |
+
ratings = [
|
| 1540 |
+
("QARP Installation & Setup", 5, "#34d399",
|
| 1541 |
+
"setup_env.sh worked cleanly on both login and compute nodes. pyenv + venv workflow is clean and reproducible. Requirements.txt complete."),
|
| 1542 |
+
("QARP Documentation", 4, "#34d399",
|
| 1543 |
+
"mwe_vqe.py, mwe_adapt_vqe_vqd.py, mwe_dosqpe_algo.py are excellent. Missing: ARM-specific warnings and Jupyter/MPI incompatibility note."),
|
| 1544 |
+
("VQE Algorithm", 5, "#34d399",
|
| 1545 |
+
"Clean API, COBYLA converged reliably on 30q supply-chain Hamiltonians (depth=3, 120 params, 5 restarts). E₀ = −44.6931 matches NB1 exact with zero error."),
|
| 1546 |
+
("ADAPT-VQE Gradient Screening", 5, "#34d399",
|
| 1547 |
+
"Highly effective for policy ranking — ranked 6 interventions without full re-optimisation. Exactly the quantum efficiency gain needed for real-world policy applications."),
|
| 1548 |
+
("DOS-QPE Survival Amplitude", 4, "#34d399",
|
| 1549 |
+
"Correct spectral reconstruction from mwe_dosqpe_algo.py pattern. 64 Trotter steps produced clean DOS. FFT + Hanning window pipeline worked directly."),
|
| 1550 |
+
("OpenFermion Integration", 5, "#34d399",
|
| 1551 |
+
"QubitOperator → QARP Hamiltonian pipeline worked cleanly. ZZ + X Pauli encoding mapped naturally to Ising supply-chain structure with 57 supply edges."),
|
| 1552 |
+
("TketEngine + AerBackend", 4, "#34d399",
|
| 1553 |
+
"Reliable QulacsEngine replacement. Worked consistently across all 4 notebooks once QulacsEngine segfault was diagnosed. Slightly slower than native Qulacs."),
|
| 1554 |
+
("MPI / Distributed Support", 3, "#fbbf24",
|
| 1555 |
+
"mpi4py works correctly in sbatch scripts. Cannot be imported in Jupyter on compute nodes (OMPI not built with SLURM PMI). Needs better documentation."),
|
| 1556 |
+
("QulacsEngine on A64FX ARM", 2, "#f87171",
|
| 1557 |
+
"Segfaults on ARM A64FX compute nodes — SIGSEGV at C extension level, uncatchable by Python try/except. Worked on x86 login node only. Required 3h to diagnose."),
|
| 1558 |
+
("Error Messages & Diagnostics", 3, "#fbbf24",
|
| 1559 |
+
"Algorithm-level errors are clear. C-extension segfaults give no Python traceback. Recommending: QARP_DISABLE_MPI flag + ARM binary distribution."),
|
| 1560 |
+
]
|
| 1561 |
+
|
| 1562 |
+
col_a, col_b = st.columns(2)
|
| 1563 |
+
for i, (aspect, rating, color, comment) in enumerate(ratings):
|
| 1564 |
+
col = col_a if i % 2 == 0 else col_b
|
| 1565 |
+
with col:
|
| 1566 |
+
stars = "★" * rating + "☆" * (5 - rating)
|
| 1567 |
+
bar_w = int(rating / 5 * 100)
|
| 1568 |
+
st.markdown(f"""
|
| 1569 |
+
<div style='background:var(--surface); border:1px solid var(--border);
|
| 1570 |
+
border-left:3px solid {color}; border-radius:10px;
|
| 1571 |
+
padding:14px 16px; margin-bottom:10px'>
|
| 1572 |
+
<div style='display:flex; justify-content:space-between; align-items:center; margin-bottom:6px'>
|
| 1573 |
+
<span style='font-size:0.84rem; font-weight:700; color:var(--text)'>{aspect}</span>
|
| 1574 |
+
<span style='font-family:JetBrains Mono; color:{color}; font-size:0.88rem; white-space:nowrap'>
|
| 1575 |
+
{stars} {rating}/5
|
| 1576 |
+
</span>
|
| 1577 |
+
</div>
|
| 1578 |
+
<div style='background:var(--bg); border-radius:4px; height:4px; margin-bottom:8px; overflow:hidden'>
|
| 1579 |
+
<div style='background:{color}; width:{bar_w}%; height:100%; border-radius:4px;
|
| 1580 |
+
transition:width 0.3s'></div>
|
| 1581 |
+
</div>
|
| 1582 |
+
<div style='color:var(--text2); font-size:0.76rem; line-height:1.5'>{comment}</div>
|
| 1583 |
+
</div>
|
| 1584 |
+
""", unsafe_allow_html=True)
|
| 1585 |
+
|
| 1586 |
+
st.markdown("---")
|
| 1587 |
+
|
| 1588 |
+
# Issues & positives
|
| 1589 |
+
issue_col, pos_col = st.columns(2)
|
| 1590 |
+
|
| 1591 |
+
with issue_col:
|
| 1592 |
+
st.markdown("### 🔴 Issues Encountered")
|
| 1593 |
+
issues = [
|
| 1594 |
+
("#f87171", "CRITICAL", "QulacsEngine Segfault on A64FX",
|
| 1595 |
+
"QulacsEngine (.pyc) segfaults on ARM A64FX compute nodes — SIGSEGV at C extension level. Root cause: MPI_Init inside constructor; OMPI not built with SLURM PMIx.",
|
| 1596 |
+
"Replaced with direct qulacs Observable API + TketEngine(AerBackend). Took ~3h to diagnose.",
|
| 1597 |
+
"Distribute as .py source or provide ARM binary. Add QARP_DISABLE_MPI=1 to suppress C-level MPI init."),
|
| 1598 |
+
("#f87171", "CRITICAL", "MPI Crashes Jupyter Kernel",
|
| 1599 |
+
"Importing mpi4py inside Jupyter on compute node causes immediate kernel crash: OPAL ERROR — OMPI not built with SLURM PMI support.",
|
| 1600 |
+
"All MPI code moved to sbatch scripts. Jupyter used for algorithm development only.",
|
| 1601 |
+
"Document this limitation prominently. Provide QARP_NO_MPI flag at C level."),
|
| 1602 |
+
("#fbbf24", "HIGH", "Login vs Compute Node Architecture",
|
| 1603 |
+
"Login node is x86; compute nodes are ARM A64FX. Code that works on login node fails on compute nodes. Not documented.",
|
| 1604 |
+
"Learned through trial and error. All quantum code moved to compute nodes.",
|
| 1605 |
+
"Add prominent README warning: all quantum code must run on compute nodes only."),
|
| 1606 |
+
("#fbbf24", "MEDIUM", "Interactive Partition 30-min Time Limit",
|
| 1607 |
+
"Insufficient for 28q+ benchmarks. 29q = 595s, 30q = 1192s per eval requires extended allocation.",
|
| 1608 |
+
"Used --time=12:00:00 for benchmark jobs.",
|
| 1609 |
+
"Provide 2–4h partition or document qubit limits per partition."),
|
| 1610 |
+
]
|
| 1611 |
+
for color, sev, title, detail, fix, rec in issues:
|
| 1612 |
+
st.markdown(f"""
|
| 1613 |
+
<div style='background:var(--surface); border:1px solid var(--border);
|
| 1614 |
+
border-left:3px solid {color}; border-radius:10px;
|
| 1615 |
+
padding:14px 16px; margin-bottom:12px'>
|
| 1616 |
+
<div style='display:flex; align-items:center; gap:8px; margin-bottom:8px'>
|
| 1617 |
+
<span style='background:{color}22; color:{color}; border:1px solid {color}44;
|
| 1618 |
+
border-radius:4px; padding:2px 8px; font-size:0.67rem; font-weight:700;
|
| 1619 |
+
font-family:JetBrains Mono'>{sev}</span>
|
| 1620 |
+
<span style='font-weight:700; font-size:0.9rem; color:var(--text)'>{title}</span>
|
| 1621 |
+
</div>
|
| 1622 |
+
<div style='color:var(--text2); font-size:0.78rem; margin-bottom:6px; line-height:1.5'>{detail}</div>
|
| 1623 |
+
<div style='font-size:0.75rem; margin-bottom:3px'>
|
| 1624 |
+
<span style='color:var(--green); font-weight:600'>✓ Workaround:</span>
|
| 1625 |
+
<span style='color:var(--muted)'> {fix}</span>
|
| 1626 |
+
</div>
|
| 1627 |
+
<div style='font-size:0.75rem'>
|
| 1628 |
+
<span style='color:var(--accent); font-weight:600'>→ Recommendation:</span>
|
| 1629 |
+
<span style='color:var(--muted)'> {rec}</span>
|
| 1630 |
+
</div>
|
| 1631 |
+
</div>
|
| 1632 |
+
""", unsafe_allow_html=True)
|
| 1633 |
+
|
| 1634 |
+
with pos_col:
|
| 1635 |
+
st.markdown("### 🟢 What Worked Well")
|
| 1636 |
+
positives = [
|
| 1637 |
+
("QARP VQE API",
|
| 1638 |
+
f"Clean interface, COBYLA converged reliably on 30q supply-chain Hamiltonians. VQE reached E₀ = {vqe_e0_40:.4f} (40q scaled) with zero error vs exact diagonalisation."),
|
| 1639 |
+
("ADAPT-VQE Gradient Screening",
|
| 1640 |
+
"Ranked 6 policy interventions (Rate hike, Supplier subsidy, Stockpile release, Trade diversion, Combined optimal) without full re-optimisation — exactly the quantum efficiency needed."),
|
| 1641 |
+
("DOS-QPE Spectral Reconstruction",
|
| 1642 |
+
"64-step Trotter survival amplitude + Hanning FFT produced clean density of states. Pattern from mwe_dosqpe_algo.py was directly applicable to supply-chain Hamiltonian."),
|
| 1643 |
+
("TketEngine + AerBackend Fallback",
|
| 1644 |
+
"Reliable QulacsEngine replacement. Worked consistently across all 4 notebooks once QulacsEngine was bypassed. Essential for A64FX ARM compatibility."),
|
| 1645 |
+
("OpenFermion QubitOperator Integration",
|
| 1646 |
+
"ZZ + X Pauli encoding mapped naturally to Ising supply-chain structure. 57-edge supply network → Hamiltonian in < 10 lines of QARP code."),
|
| 1647 |
+
("Example Scripts Quality",
|
| 1648 |
+
"mwe_vqe.py, mwe_adapt_vqe_vqd.py, mwe_dosqpe_algo.py: clear, well-commented, directly adaptable. Best part of the documentation package."),
|
| 1649 |
+
("MPI Scaling Performance",
|
| 1650 |
+
"qulacs with MPI enabled scales correctly: 24q→30q measured on Fujitsu A64FX with R²=0.9948 exponential fit. 30q = 17.2 GB (measured) confirms performance claims."),
|
| 1651 |
+
]
|
| 1652 |
+
for title, detail in positives:
|
| 1653 |
+
st.markdown(f"""
|
| 1654 |
+
<div style='background:rgba(52,211,153,0.04); border:1px solid rgba(52,211,153,0.15);
|
| 1655 |
+
border-left:3px solid var(--green); border-radius:10px;
|
| 1656 |
+
padding:12px 14px; margin-bottom:10px'>
|
| 1657 |
+
<div style='color:var(--green); font-weight:700; font-size:0.83rem; margin-bottom:4px'>{title}</div>
|
| 1658 |
+
<div style='color:var(--text2); font-size:0.78rem; line-height:1.55'>{detail}</div>
|
| 1659 |
+
</div>
|
| 1660 |
+
""", unsafe_allow_html=True)
|
| 1661 |
+
|
| 1662 |
+
st.markdown("### 📋 Priority Recommendations")
|
| 1663 |
+
recs = [
|
| 1664 |
+
("#f87171", "P1", "Fix QulacsEngine ARM A64FX", "Distribute as .py source or ARM binary. Showstopper for the competition platform."),
|
| 1665 |
+
("#f87171", "P1", "Document Jupyter + MPI limitation", "Add clear note: mpi4py cannot be used in Jupyter on this cluster."),
|
| 1666 |
+
("#fbbf24", "P2", "Architecture-specific setup guide", "Warn: login=x86, compute=ARM. All quantum code must run on compute nodes."),
|
| 1667 |
+
("#fbbf24", "P2", "Extend Interactive partition time", "2–4h minimum for 28q+ workloads (currently 30min)."),
|
| 1668 |
+
("#38bdf8", "P3", "QARP health-check script", "Verify all engines on current architecture before users spend hours debugging."),
|
| 1669 |
+
("#38bdf8", "P3", "Progress callbacks for DOS-QPE", "Long Trotter evolutions need progress indicators."),
|
| 1670 |
+
]
|
| 1671 |
+
for col, pri, title, detail in recs:
|
| 1672 |
+
st.markdown(f"""
|
| 1673 |
+
<div style='background:var(--surface); border:1px solid var(--border);
|
| 1674 |
+
border-left:3px solid {col}; border-radius:8px;
|
| 1675 |
+
padding:10px 14px; margin-bottom:8px'>
|
| 1676 |
+
<div style='display:flex; gap:8px; align-items:center; margin-bottom:3px'>
|
| 1677 |
+
<span style='color:{col}; font-size:0.67rem; font-weight:700;
|
| 1678 |
+
background:{col}22; padding:1px 6px; border-radius:3px;
|
| 1679 |
+
font-family:JetBrains Mono'>{pri}</span>
|
| 1680 |
+
<span style='color:var(--text); font-weight:600; font-size:0.82rem'>{title}</span>
|
| 1681 |
+
</div>
|
| 1682 |
+
<div style='color:var(--muted); font-size:0.75rem'>{detail}</div>
|
| 1683 |
+
</div>
|
| 1684 |
+
""", unsafe_allow_html=True)
|
| 1685 |
+
|
| 1686 |
+
# Conclusion
|
| 1687 |
+
st.markdown("---")
|
| 1688 |
+
st.markdown(f"""
|
| 1689 |
+
<div style='background:var(--surface); border:1px solid var(--border2);
|
| 1690 |
+
border-radius:14px; padding:22px 28px'>
|
| 1691 |
+
<div style='font-family:Orbitron; font-weight:700; color:var(--accent); font-size:1rem; margin-bottom:10px'>
|
| 1692 |
+
⚛ Conclusion
|
| 1693 |
+
</div>
|
| 1694 |
+
<div style='color:var(--text2); font-size:0.88rem; line-height:1.8'>
|
| 1695 |
+
Fujitsu QARP is a <strong style='color:var(--text)'>scientifically rigorous</strong> quantum algorithm library.
|
| 1696 |
+
VQE, ADAPT-VQE gradient screening, and DOS-QPE enabled genuine novel applications in supply-chain
|
| 1697 |
+
quantum risk simulation that would not be possible with classical methods.
|
| 1698 |
+
The ADAPT-VQE policy ranking was the standout feature — ranking 6 interventions
|
| 1699 |
+
by gradient without full re-optimisation is exactly the kind of quantum speedup
|
| 1700 |
+
that justifies real-world deployment.
|
| 1701 |
+
The primary obstacle — QulacsEngine incompatibility with A64FX ARM — is a single issue
|
| 1702 |
+
that, once resolved, would make QARP the definitive quantum algorithm library for
|
| 1703 |
+
the Fujitsu platform. The TketEngine fallback proved it is an engineering fix, not a
|
| 1704 |
+
fundamental limitation.
|
| 1705 |
+
</div>
|
| 1706 |
+
<div style='margin-top:14px; display:flex; gap:16px; flex-wrap:wrap'>
|
| 1707 |
+
<div style='font-family:JetBrains Mono; font-size:0.78rem; color:var(--green)'>
|
| 1708 |
+
✓ Algorithm quality: 5/5
|
| 1709 |
+
</div>
|
| 1710 |
+
<div style='font-family:JetBrains Mono; font-size:0.78rem; color:var(--green)'>
|
| 1711 |
+
✓ API design: 4.5/5
|
| 1712 |
+
</div>
|
| 1713 |
+
<div style='font-family:JetBrains Mono; font-size:0.78rem; color:var(--yellow)'>
|
| 1714 |
+
⚠ ARM compatibility: 2/5 (fixable)
|
| 1715 |
+
</div>
|
| 1716 |
+
<div style='font-family:JetBrains Mono; font-size:0.78rem; color:var(--accent)'>
|
| 1717 |
+
Overall: 4.1/5
|
| 1718 |
+
</div>
|
| 1719 |
+
</div>
|
| 1720 |
+
</div>
|
| 1721 |
+
""", unsafe_allow_html=True)
|
| 1722 |
+
|
| 1723 |
+
|
| 1724 |
+
# ── Footer ─────────────────────────────────────────────────────
|
| 1725 |
+
st.markdown("""
|
| 1726 |
+
<div style='text-align:center; padding:28px 0 8px; border-top:1px solid #1a2d4a; margin-top:32px'>
|
| 1727 |
+
<div style='font-family:Orbitron; font-size:0.7rem; color:#1e3a5f; letter-spacing:0.2em'>
|
| 1728 |
+
QR-SPPS · FUJITSU QUANTUM SIMULATOR CHALLENGE 2025-26 ·
|
| 1729 |
+
VQE · ADAPT-VQE · DOS-QPE
|
| 1730 |
+
</div>
|
| 1731 |
+
<div style='font-family:JetBrains Mono; font-size:0.65rem; color:#1a2d4a; margin-top:4px'>
|
| 1732 |
+
40q encoded · 30q executed (17.2 GB MPI measured) · 40q extrapolated (17.6 TB, 1308h/eval)
|
| 1733 |
+
</div>
|
| 1734 |
+
</div>
|
| 1735 |
+
""", unsafe_allow_html=True)
|
requirements.txt
CHANGED
|
@@ -1,3 +1,25 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# QR-SPPS: Quantum-Native Retail Shock Propagation & Policy Stress Simulator
|
| 2 |
+
# Requirements for Streamlit Dashboard (local / cloud deployment)
|
| 3 |
+
# Fujitsu Quantum Simulator Challenge 2025-26
|
| 4 |
+
#
|
| 5 |
+
# For Fujitsu A64FX cluster execution, see scripts/setup_env.sh
|
| 6 |
+
# which installs Fujitsu QARP v0.4.4 and Qulacs 0.6.12 (ARM MPI kernel)
|
| 7 |
+
|
| 8 |
+
# ── Core dashboard ──────────────────────────────────────────────
|
| 9 |
+
streamlit>=1.35.0,<2.0.0
|
| 10 |
+
plotly>=5.22.0
|
| 11 |
+
pandas>=2.2.0
|
| 12 |
+
numpy>=1.26.0
|
| 13 |
+
|
| 14 |
+
# ── Quantum / scientific (dashboard loads pre-computed .pkl files)
|
| 15 |
+
# Full quantum stack (Fujitsu QARP + Qulacs) runs on the A64FX cluster.
|
| 16 |
+
# These packages are required for result verification and local re-execution.
|
| 17 |
+
scipy>=1.13.0
|
| 18 |
+
openfermion>=1.6.0
|
| 19 |
+
|
| 20 |
+
# ── Visualisation extras ─────────────────────────────────────────
|
| 21 |
+
networkx>=3.3.0 # Supply chain graph visualisation
|
| 22 |
+
matplotlib>=3.9.0 # Fallback plots
|
| 23 |
+
|
| 24 |
+
# ── Utilities ────────────────────────────────────────────────────
|
| 25 |
+
requests>=2.32.0 # Used by keep-alive workflow ping utility
|