# DANCE‑ST Supplementary Utilities

This README ships with the **empirical‑validation tools** we release
alongside our NeurIPS 2025 submission
*“DANCE‑ST: Dynamic Adaptive Neurosymbolic Causal Embedding for
Spatiotemporal Reasoning.”*  The scripts were requested by reviewers to
bridge the theory–practice gap: they (i) audit the strong‑monotonicity
assumption behind our Douglas–Rachford (DR) rate, and (ii) optimise the
relevance‑weight simplex used in Phase I.

```
├── check_mu.py       # empirical strong‑monotonicity audit
├── opt_weights.py    # Newton–KL optimiser for (α, β, γ)

```

Both scripts are pure‑Python (≥3.8) and have no heavy dependencies—
NumPy, SciPy, pandas, matplotlib, and tqdm are enough.

---
## 1  Installation
```bash
# (optionally) create a venv
python3 -m venv venv && source venv/bin/activate

pip install -r requirements.txt      # tiny: numpy, scipy, pandas, matplotlib, tqdm
```
If you do not ship a `requirements.txt`, reviewers can simply run
`pip install numpy scipy pandas matplotlib tqdm`.

---
## 2  `check_mu.py`
### 2.1 Purpose
Computes the smallest eigenvalue $\hat\mu$ of the symmetrised projection
Jacobian $S = \tfrac12(A + A^{\top})$ for each stored matrix $A$,
verifying the **strong‑monotonicity** condition $\hat\mu>0$ required by
our linear DR proof.

### 2.2 Inputs
* One or more `*.npy` or `*.npz` files containing square matrices $A$.
  Each file may hold:
  * a single 2‑D array named `A`, or
  * a stack `A_stack` of shape `(N, d, d)`.

### 2.3 Usage
```bash
python check_mu.py runs/*.npy \
    --csv mu_values.csv     # optional: write per‑snapshot values
    --hist mu_hist.png      # optional: save histogram
    --top 5                 # show 5 largest μ̂ as well
```
### 2.4 Outputs
* **Terminal:** table with file name, snapshot index, $\hat\mu$, and flag
  (`OK` if $\hat\mu>0$ else `VIOLATION`).
* **CSV (optional):** two‑column file `snapshot,mu_hat`.
* **PNG (optional):** histogram of $\hat\mu$ values with vertical line at 0.

### 2.5 Typical run time
Sub‑second for $d\le1024$ and $N\le1000$ snapshots on a laptop CPU.
Eigen‑decomposition leverages NumPy’s LAPACK; no GPU needed.

---
## 3  `opt_weights.py`
### 3.1 Purpose
Optimises the Phase I relevance weights $(\alpha,\beta,\gamma) \in
\Delta_2$ by **Newton–KL** on the convex objective
$$\min_{w\in\Delta_2} D_{\mathrm{KL}}(p \| w)$$
where $p$ are aggregated relevance proportions.

### 3.2 Inputs
* A CSV or NPY/NPZ file holding either
  * three counts `count_causal,count_spatial,count_temporal`, or
  * raw per‑node relevance triplets which are summed inside the script.

### 3.3 Usage
```bash
python opt_weights.py Lambda.csv           # basic run
python opt_weights.py Lambda.npy \
      --tol 1e-8 --max-it 20              # tighter tolerance
python opt_weights.py Lambda.csv \
      --convexity-check                   # print Hessian eigenvalues
```
* `--tol`: Newton termination tolerance on $\|\nabla f\|_2$ (default `1e-6`).
* `--max-it`: maximum Newton iterations (default `10`).
* `--convexity-check`: after convergence, report the three Hessian
eigenvalues to verify positive‑definiteness.

### 3.4 Outputs
* **Terminal:** iteration log with objective, gradient norm, step size.
* **JSON:** `weights.json` containing `{"alpha":…, "beta":…, "gamma":…}`.

### 3.5 Typical run time
< 5 ms on a modern CPU (problem is 2‑D after simplex elimination).

---
## 4  Reproducibility notes
* The exact commands used to generate Fig. `weight_conv` and
  Fig. `mu_hist` in the paper are embedded as comments at the top of
  each script.
* Random seeds (NumPy + Python RNG) are fixed at `42` unless you pass
  `--seed`.

---
## 5  Troubleshooting
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `LinAlgError: Eigenvalues did not converge` when running `check_mu.py` | ill‑conditioned matrix | pass `--dtype float64` when saving snapshots; ensure they are finite |
| Newton iterations stall in `opt_weights.py` | counts contain zeros → initial gradient infinite | add `--eps 1e-12` (Laplace smoothing) |
