callensxavier's picture
Release v1.0: data/code for the hyperbolic conditioning preprint
52b9a68 verified
Raw History Blame Contribute Delete
28.9 kB
#!/usr/bin/env python3
"""Build the release bundles (Hugging Face dataset, Hugging Face "model"
repository for the numerical simulator, Zenodo archive) from the code and
data in this directory. Everything is regenerated; nothing is hand-edited.
python3 release/export.py -> release/build/{hf_dataset,hf_model,zenodo}
Contents
hf_dataset/ graphs (edge lists, coordinates, boundary), per-instance
conditioning, exact ranks, spectral gaps, integrator controls,
RC time series, DtN matrices (npz), the paper PDF.
hf_model/ the simulator code (no weights) with a model card.
zenodo/ paper.pdf, dataset.zip, code.zip -- the files the Zenodo draft
script uploads.
"""
from __future__ import annotations
import csv
import json
import shutil
import subprocess
import sys
import zipfile
from pathlib import Path
import numpy as np
TRACK = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(TRACK))
from hyperbolic_network import ( # noqa: E402
boundary_nodes, build_hyperbolic, build_square_disk, build_triangular_disk, dtn, laplacian,
)
BUILD = TRACK / "release" / "build"
DS, MD, ZD = BUILD / "hf_dataset", BUILD / "hf_model", BUILD / "zenodo"
CODE_FILES = ["hyperbolic_network.py", "hyperbolic_exact.py", "probe_matched.py", "identifiability.py",
"rc_network.py", "h2_explore.py", "interior_degree.py", "flat_scaling_mp.py", "disorder.py",
"subspace_control.py", "prereg3_predictions.py", "paper/make_assets.py", "paper/build.py",
"paper/main.tex", "paper/refs.bib", "paper/reviews/REVIEW_2026-09-27_v1.0.md",
"paper/reviews/RESPONSE_2026-09-27_v1.0.md", "PREREGISTRATION_3.md",
"release/export.py", "release/hf_upload.py", "release/zenodo_new_version.py", "release/zenodo_publish.py",
"release/zenodo_draft.py", "release/zenodo_metadata.json", "release/check_bundle.py",
"release/rusty_sundials_contrib/examples/python/rc_network/rc_network_benchmark.py",
"release/rusty_sundials_contrib/examples/python/rc_network/test_rc_network.py",
"release/rusty_sundials_contrib/examples/python/rc_network/README.md",
"release/rusty_sundials_contrib/examples/python/rc_network/fixtures/hyperbolic_7_3_L2.json",
"release/rusty_sundials_contrib/examples/python/rc_network/fixtures/square_R6.json",
"release/rusty_sundials_contrib/apply.sh",
"PREREGISTRATION.md", "PREREGISTRATION_2.md"]
HERE_TEX = TRACK / "release"
GRAPHS = ([("{7,3}", f"L={L}", lambda L=L: build_hyperbolic(7, 3, L)) for L in range(1, 7)]
+ [("square", f"R={R}", lambda R=R: build_square_disk(R)) for R in (3, 6, 10, 16)]
+ [("triangular", f"R={R}", lambda R=R: build_triangular_disk(R))
for R in (3.225, 6.45, 7.0, 10.75, 11.5, 17.2, 18.5)])
DTN_MAX_N = 900 # dense DtN matrices stored up to this size
def git_commit():
try:
return subprocess.run(["git", "rev-parse", "HEAD"], cwd=TRACK, capture_output=True, text=True).stdout.strip()
except Exception:
return "unknown"
def write_csv(path, rows):
with open(path, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
w.writeheader()
w.writerows(rows)
def export_graphs():
(DS / "graphs").mkdir(parents=True)
(DS / "dtn").mkdir()
index = []
for fam, par, build in GRAPHS:
g = build()
n = len(g["nodes"])
bnd = boundary_nodes(g)
tag = f"{fam.strip('{}').replace(',', '_')}_{par.replace('=', '')}".replace(".", "p")
z = np.asarray(g["nodes"])
rec = {"family": fam, "param": par, "N": n, "E": len(g["edges"]), "boundary_size": int(len(bnd)),
"x": z.real.round(12).tolist(), "y": z.imag.round(12).tolist(),
"edges": [list(map(int, e)) for e in g["edges"]], "boundary": [int(b) for b in bnd]}
(DS / "graphs" / f"{tag}.json").write_text(json.dumps(rec))
stored = n <= DTN_MAX_N
if stored:
lam = dtn(n, g["edges"], np.ones(len(g["edges"])), bnd)
np.savez_compressed(DS / "dtn" / f"{tag}.npz", Lambda=lam, boundary=bnd)
index.append({"graph_id": tag, "family": fam, "param": par, "N": n, "E": len(g["edges"]),
"boundary_size": int(len(bnd)), "graph_file": f"graphs/{tag}.json",
"dtn_file": f"dtn/{tag}.npz" if stored else ""})
print(f" graph {tag}: N={n}")
write_csv(DS / "graphs_index.csv", index)
def export_tables():
D = TRACK / "data"
h0 = json.loads((D / "h0.json").read_text())
write_csv(DS / "conditioning.csv", [
{"lattice": r["name"], "N": r["N"], "E": r["E"], "boundary_size": r["boundary"], "max_depth": r["max_depth"],
"log10_kappa": "" if r["log10_kappa"] is None else r["log10_kappa"],
"numerically_singular_float64": r["numerically_singular"]} for r in h0])
pm = json.loads((D / "probe_matched.json").read_text())
write_csv(DS / "probe_matched.csv", [
{"case": r["name"], "N": r["N"], "E": r["E"], "probes": r["probes"],
"log10_kappa_DtN": "" if r["log10_kappa_DtN"] is None else r["log10_kappa_DtN"],
"log10_kappa_NtD": "" if r["log10_kappa_NtD"] is None else r["log10_kappa_NtD"]} for r in pm])
ident = json.loads((D / "identifiability.json").read_text())
write_csv(DS / "exact_rank.csv", [
{"case": r["name"], "N": r["N"], "E": r["E"], "probes": r["probes"],
"rank_mod_2147483647": r["exact_ranks"]["2147483647"], "rank_mod_998244353": r["exact_ranks"]["998244353"],
"deficiency": r["deficiency"], "unmeasured_degree2_nodes": r["unmeasured_degree2_nodes"],
"float_gap_at_rank": "" if r.get("float_gap_at_rank") is None else r["float_gap_at_rank"],
"log10_kappa_identifiable_subspace": r["log10_kappa_identifiable"]} for r in ident])
full = json.loads((D / "exact_rank_full.json").read_text())
write_csv(DS / "exact_rank_full_boundary.csv", [
{"case": r["case"], "N": r["N"], "E": r["E"], "rank_mod_2147483647": r["ranks"]["2147483647"],
"rank_mod_998244353": r["ranks"]["998244353"], "primes_agree": r["primes_agree"],
"full_rank_certified": r["full_rank_certified"]} for r in full])
# v1.1 additions (peer-review revisions); each file is optional so export works at any stage
for name, csvname, conv in (
("flat_scaling_mp.json", "conditioning_arb.csv", lambda rows: [
{"case": r["case"], "N": r["N"], "E": r["E"], "boundary_size": r["boundary"], "prec_bits": r["prec_bits"],
"log10_kappa_arb": r["log10_kappa_arb"], "max_rel_radius_Ginv": r["max_rel_radius_Ginv"],
"float64_log10_kappa": "" if r["float64_log10_kappa"] is None else r["float64_log10_kappa"],
"control_pass": "" if r["control_pass"] is None else r["control_pass"]} for r in rows]),
("disorder.json", "disorder.csv", lambda d: [
{"graph": r["graph"], "N": r["N"], "regime": r["regime"], "seed": "" if r["seed"] is None else r["seed"],
"defect_node": r.get("defect_node", ""), "defect_depth": r.get("defect_depth", ""),
"log10_kappa_logparam": "" if r["log10_kappa_log"] is None else r["log10_kappa_log"],
"log10_kappa_raw": "" if r["log10_kappa_raw"] is None else r["log10_kappa_raw"]} for r in d["rows"]]),
("subspace_control.json", "subspace_control.csv", lambda rows: rows),
("tilings_kappa.json", "tilings_kappa.csv", lambda d: [
{"tiling": "{%d,%d}" % (r["p"], r["q"]), "L": r["L"], "N": r["N"], "E": r["E"], "boundary": r["boundary"],
"d_max": r["d_max"], "interior_degree_q": r["interior_degree_q"], "log10_kappa": r["log10_kappa"],
"log10_kappa_error_bound": r["log10_kappa_error_bound"]} for r in d["rows"]]),
("localize_tolerance.json", "localize_tolerance.csv", lambda d: [
{"lattice": r["lattice"], "N": r["N"], "tolerance": c["tau"], "noise_eps": c["eps"], "contrast": c["f"],
"nodes_in_class": r["nodes_in_class"], "top1": c["top1"]}
for r in d["results"] for c in r["cells"].values()]),
("localize_noise.json", "localize_noise.csv", lambda d: [
{"lattice": r["lattice"], "N": r["N"], "contrast": cell, "nodes_in_class": r["nodes_in_class"],
"eps": e, "top1": t, "eps_loc": c["eps_loc"]}
for r in d["results"] for cell, c in r["cells"].items() for e, t in zip(c["eps"], c["top1"])]),
("localize_defect.json", "localize_defect.csv", lambda d: [
{"lattice": r["lattice"], "N": r["N"], "interior_nodes": r["interior_nodes"], "cell": cell,
"depth": c["depth"], "nodes_in_class": c["nodes_in_class"], "top1": c["top1"],
"mean_hops": c["mean_hops"], "contrast_acc": c["contrast_acc"], "noise_eps": d["eps"]}
for r in d["results"] for cell, c in r["cells"].items()]),
("tolerance_null.json", "tolerance_null.csv", lambda d: [
{"lattice": r["lattice"], "N": r["N"], "tolerance": tau, "defect_node": r["defect_node"],
"defect_depth": r["defect_depth"], "B_null_max": t["B_null_max"], "B_defect_min": t["B_defect_min"],
"B_detected": t["B_detected"], "A_null_max": t["A_null_max"], "A_defect_min": t["A_defect_min"],
"A_detected": t["A_detected"]}
for r in d["results"] for tau, t in r["tau"].items()]),
("tda_noise.json", "tda_noise.csv", lambda d: [
{"lattice": r["lattice"], "N": r["N"], "config": cfg, "node": c["node"], "depth": c["depth"],
"eps_max_metric": c["eps_max"]["metric"], "eps_max_H1": c["eps_max"]["H1"],
"detected_metric_by_eps": "".join("Y" if x else "." for x in c["detected"]["metric"]),
"detected_H1_by_eps": "".join("Y" if x else "." for x in c["detected"]["H1"]),
"eps_grid": " ".join(str(e) for e in d["eps"])}
for r in d["results"] for cfg, c in r["configs"].items()]),
("tda_defect.json", "tda_defect.csv", lambda d: [
{"lattice": r["lattice"], "N": r["N"], "boundary_size": r["boundary"], "config": cfg,
"node": s.get("node", ""), "depth": s.get("depth", ""), "bottleneck_H0": s["bottleneck_H0"],
"bottleneck_H1": s["bottleneck_H1"], "rel_metric_change": s["rel_metric_change"],
"null_p95_H0": r["null_p95"]["bottleneck_H0"], "null_p95_H1": r["null_p95"]["bottleneck_H1"],
"null_p95_metric": r["null_p95"]["rel_metric_change"],
"detected_H1": s["detected"]["bottleneck_H1"], "detected_metric": s["detected"]["rel_metric_change"]}
for r in d["results"] for cfg, s in r["configs"].items()]),
# post-v1.1, low-tier workflow rounds (preregistrations 12, 14-19)
("exact_rank_certificates_for_other_tiling.json", "exact_rank_tilings.csv", lambda d: [
{"tiling": r["tiling"], "L": r["L"], "N": r["N"], "E": r["E"], "boundary": r["boundary"], "prime": p,
"rank": v["rank"], "rows_used": v["rows_used"], "certified_full": v["certified_full"],
"certified_full_both_primes": r["certified_full_both"]}
for r in d["rows"] for p, v in r["primes"].items()]),
("rc_benchmark_on_other_tilings.json", "rc_benchmark_tilings.csv", lambda d: [
{"tiling": r["tiling"], "L": r["L"], "N": r["N"], "d_max": r["d_max"], "lambda_min": r["lambda_min"],
"lambda_max": r["lambda_max"], "tau": r["tau"], "stiffness": r["stiffness"], "backend": b,
"K1_rel_err": c["K1_rel_err"], "K1_pass": c["K1_pass"], "K2_rel_err": c["K2_rel_err"], "K2_pass": c["K2_pass"]}
for r in d["rows"] for b, c in r["controls"].items()]),
("minimum_detectable_contrast.json", "minimum_contrast.csv", lambda d: [
{"lattice": r["lattice"], "N": r["N"], "nodes_in_class": r["nodes_in_class"], "contrast": c["f"],
"noise_eps": c["eps"], "top1": c["top1"], "trials": d["trials"]}
for r in d["results"] for c in r["cells"].values()]),
("localisation_under_correlated_hardware_n.json", "hardware_noise.csv", lambda d: [
{"board": b, "contrast": d["f"], "iid_eps": d["eps_iid"], "trials": d["trials"], "cell": cell, "top1": t}
for b, v in d["boards"].items() for cell, t in v["cells"].items()]),
("coherence_versus_depth_across_tilings.json", "coherence_depth.csv", lambda d: [
{"family": r["family"], "param": r["param"], "N": r["N"], "E": r["E"], "edge_depth": k, "n_edges": v["n_edges"],
"n_pairs": v["n_pairs"], "median_abs_cos": v["median_abs_cos"], "mean_abs_cos": v["mean_abs_cos"],
"p90_abs_cos": v["p90_abs_cos"], "participation_ratio": v["participation_ratio"], "eff_dim_fraction": v["eff_dim_fraction"]}
for r in d["rows"] for k, v in r["by_depth"].items()]),
("coherence_mechanism_prediction_test.json", "coherence_mechanism_test.csv", lambda d: [
{"instance": r["name"], "N": r["N"], "E": r["E"], "d_max": r["d_max"], "kappa_method": r["kappa_method"], "d_star": r["d_star"],
"delta_bar": r["delta_bar"], "edge_depth": k, "n_edges": v["n_edges"], "eff_dim_fraction": v["eff_dim_fraction"],
"log10_kappa_depth_le_d": r["log10_kappa_by_depth"][int(k)]}
for r in d["rows"] for k, v in r["by_depth"].items()]),
("flat_growth_rate_over_matched_relative_w.json", "flat_rate_windows.csv", lambda d: [
{"instance": r["name"], "family": r["family"], "R": r["R"], "N": r["N"], "d_max": r["d_max"], "d_star": r["d_star"],
"window_used": " ".join(str(x) for x in r["window_used"]), "window_extended": r["window_extended"],
"window_truncated": r["window_truncated"], "rate_decades_per_depth": r["rate"], "edge_depth": k,
"log10_kappa_depth_le_d": r["log10_kappa_by_depth"][int(k)], "n_edges": v["n_edges"],
"eff_dim_fraction": v["eff_dim_fraction"], "lambda_min_normalised_gram": v["lambda_min_normalised_gram"]}
for r in d["rows"] for k, v in r["by_depth"].items() if int(k) <= r["d_star"]]),
("coherence_mechanism_at_matched_size.json", "coherence_matched.csv", lambda d: [
{"instance": r["name"], "N": r["N"], "E": r["E"], "d_max": r["d_max"], "kappa_method": r["kappa_method"],
"delta_bar": r["delta_bar"], "edge_depth": k, "n_edges": v["n_edges"],
"log10_kappa_depth_le_d": r["log10_kappa_by_depth"][int(k)], "eff_dim_fraction_full": v["eff_dim_fraction_full"],
"matched_pr_fraction_median": v.get("matched_pr_fraction_median", ""), "n_match": d["n_match"], "n_draws": d["n_draws"]}
for r in d["rows"] for k, v in r["by_depth"].items()]),
):
p = D / name
if p.exists():
write_csv(DS / csvname, conv(json.loads(p.read_text())))
rc = json.loads((D / "rc_network.json").read_text())
ex =json.loads((D / "h2_explore.json").read_text())
gap = [{"family": r["family"], "N": r["N"], "lambda_min": r["lambda_min"], "lambda_max": r["lambda_max"],
"tau": r["tau"], "stiffness": r["stiffness"], "exploratory": False} for r in rc["H2"]]
gap += [{"family": "{7,3}", "N": r["N"], "lambda_min": r["lambda_min"], "lambda_max": "",
"tau": 1 / r["lambda_min"], "stiffness": "", "exploratory": True} for r in ex["rows"] if r["L"] >= 5]
write_csv(DS / "spectral_gap.csv", gap)
ctrl = [{"graph": r.get("graph", "all"), "backend": r["backend"], "K1_rel_err": r.get("K1_rel_err", ""),
"K1_pass": r.get("K1_pass", ""), "K2_rel_err": r.get("K2_rel_err", ""), "K2_pass": r.get("K2_pass", ""),
"status": r.get("status", "run")} for r in rc["controls"]]
write_csv(DS / "integrator_controls.csv", ctrl)
shutil.copy(D / "interior_degree.json", DS / "interior_degree.json")
def export_timeseries():
"""RC step response V_i(t) on the two control networks, reference (expm) solution."""
from scipy.linalg import expm
rows = []
rng = np.random.default_rng(3)
for name, g in (("{7,3} L=2", build_hyperbolic(7, 3, 2)), ("square R=6", build_square_disk(6))):
n = len(g["nodes"])
bnd = boundary_nodes(g)
L = laplacian(n, g["edges"], np.ones(len(g["edges"])))
interior = np.setdiff1d(np.arange(n), bnd)
Lii, Lib = L[np.ix_(interior, interior)], L[np.ix_(interior, bnd)]
Vb = rng.uniform(-1, 1, len(bnd))
Vinf = np.linalg.solve(Lii, -(Lib @ Vb))
tau = 1.0 / np.linalg.eigvalsh(Lii)[0]
for t in np.linspace(0, 5 * tau, 41):
V = expm(-Lii * t) @ (0 - Vinf) + Vinf
rows += [{"network": name, "t": round(float(t), 10), "node": int(interior[k]), "V": float(V[k])}
for k in range(len(interior))]
write_csv(DS / "rc_step_response.csv", rows)
def dataset_card(commit):
return f"""---
license: cc-by-4.0
pretty_name: Hyperbolic vs flat resistor networks - inverse conductance conditioning
tags:
- physics
- inverse-problems
- electrical-impedance-tomography
- hyperbolic-lattices
- resistor-networks
- numerical-linear-algebra
size_categories:
- n<1K
configs:
- config_name: graphs_index
data_files: graphs_index.csv
- config_name: conditioning
data_files: conditioning.csv
- config_name: probe_matched
data_files: probe_matched.csv
- config_name: exact_rank
data_files: exact_rank.csv
- config_name: exact_rank_full_boundary
data_files: exact_rank_full_boundary.csv
- config_name: conditioning_arb
data_files: conditioning_arb.csv
- config_name: disorder
data_files: disorder.csv
- config_name: subspace_control
data_files: subspace_control.csv
- config_name: tda_defect
data_files: tda_defect.csv
- config_name: tda_noise
data_files: tda_noise.csv
- config_name: tolerance_null
data_files: tolerance_null.csv
- config_name: localize_defect
data_files: localize_defect.csv
- config_name: localize_noise
data_files: localize_noise.csv
- config_name: localize_tolerance
data_files: localize_tolerance.csv
- config_name: tilings_kappa
data_files: tilings_kappa.csv
- config_name: exact_rank_tilings
data_files: exact_rank_tilings.csv
- config_name: rc_benchmark_tilings
data_files: rc_benchmark_tilings.csv
- config_name: minimum_contrast
data_files: minimum_contrast.csv
- config_name: hardware_noise
data_files: hardware_noise.csv
- config_name: coherence_depth
data_files: coherence_depth.csv
- config_name: coherence_matched
data_files: coherence_matched.csv
- config_name: coherence_mechanism_test
data_files: coherence_mechanism_test.csv
- config_name: flat_rate_windows
data_files: flat_rate_windows.csv
- config_name: spectral_gap
data_files: spectral_gap.csv
- config_name: integrator_controls
data_files: integrator_controls.csv
- config_name: rc_step_response
data_files: rc_step_response.csv
---
# Hyperbolic vs flat resistor networks: conditioning of the inverse conductance problem
Data accompanying the preprint *Logarithmic boundary depth and the conditioning of the discrete
inverse conductance problem on hyperbolic lattices* (X. Callens, 2026), included as `paper.pdf`.
## Contents
| File | What it contains |
|---|---|
| `graphs/*.json` | Layer-truncated {{7,3}} tilings (L=1..6) and square/triangular lattice disks: node coordinates (Poincare disk / plane), edge list, boundary nodes (face incidence) |
| `dtn/*.npz` | Dirichlet-to-Neumann map `Lambda` (unit conductances) for graphs with N <= {DTN_MAX_N} |
| `conditioning.csv` | log10 condition number of the DtN sensitivity Jacobian; empty and `numerically_singular_float64=True` where float64 cannot resolve it |
| `probe_matched.csv` | DtN and Neumann-to-Dirichlet conditioning, full vs subsampled boundary |
| `exact_rank.csv` | Exact Jacobian ranks over GF(p) for two primes; deficiency vs number of unmeasured degree-2 nodes; condition number on the identifiable subspace |
| `exact_rank_full_boundary.csv` | Exact full-boundary Jacobian ranks over GF(p), two primes: full-rank certificates |
| `conditioning_arb.csv` | (v1.1) log10 condition number in 512-bit ball arithmetic for the float64-singular flat lattices, with certified radii and the two unsaturated controls |
| `disorder.csv` | (v1.1) log10 kappa (log-parametrised and raw Jacobian) under U[0.5,1.5] and log-uniform [0.1,10] conductances (5 seeds) and x100 / x0.01 defects |
| `subspace_control.csv` | (v1.1) probe-matched dimensionality control: square lattice sigma_1/sigma_r vs hyperbolic identifiable-subspace kappa |
| `tda_defect.csv` | (post-v1.1, not in the paper) persistent homology (Gudhi) of the boundary resistance metric with and without a bulk defect, vs a global-disorder null. The null was mis-sized; see `TDA_RESULTS.md` |
| `tda_noise.csv` | (post-v1.1, not in the paper) defect detection vs measurement-noise level (eps_max), metric and H1 detectors |
| `localize_defect.csv` | (post-v1.1, not in the paper) single-node defect localisation from two noisy NtD maps (oracle-dictionary matched filter); 60/60 cells perfect at noise 3e-4, so it is a ceiling, not a measurement of the failure boundary |
| `tilings_kappa.csv` | (post-v1.1, not in the paper) log10 condition number for {7,3}, {8,3}, {5,4}, {6,4}, {4,5} at L=1..6, with d_max and float64 error bounds (Gram-matrix method, controls in the repository) |
| `localize_tolerance.csv` | (post-v1.1, not in the paper) localisation top-1 on random boards with component tolerance 0.1 %, 1 %, 5 % and an ideal-model decoder; tolerance has no effect in this differential setting |
| `localize_noise.csv` | (post-v1.1, not in the paper) localisation top-1 vs noise level and eps_loc (largest noise with top-1 >= 0.9) at the deepest node, contrasts x1.25 and x2 |
| `tolerance_null.csv` | (post-v1.1, not in the paper) defect detection vs component tolerance (0.1 %, 1 %, 5 %), model-based and differential regimes; only the extreme x100 contrast is tested |
| `exact_rank_tilings.csv` | (v1.2) exact full-column-rank certificates (random row-combination method, two primes) for 11 instances of {{7,3}}, {{8,3}}, {{5,4}}, {{6,4}}, {{4,5}} up to E = 1604 |
| `rc_benchmark_tilings.csv` | (v1.2) RC relaxation time, stiffness and the K1/K2 integrator controls (SciPy BDF and rusty-SUNDIALS CVODE) on {{7,3}} L=2, {{8,3}} L=2, {{5,4}} L=3, {{6,4}} L=2, {{4,5}} L=4 |
| `minimum_contrast.csv` | (v1.2) localisation top-1 vs true contrast (x0.5 to x2) and noise (1e-4 to 3e-3) at the deepest node of four lattices; both geometries localise +-10 % at the 3e-4 budget |
| `hardware_noise.csv` | (v1.2) localisation top-1 under common-mode offset, gain drift and ADC quantisation at N~112, contrast x2; a floor (49/50 cells at 1.00), not a failure boundary |
| `coherence_depth.csv` | (v1.2) per edge depth and instance: exhaustive abs-cosine statistics between Jacobian columns of equal-depth edges and the effective dimension fraction (participation ratio / count) of their normalised Gram matrix |
| `coherence_matched.csv` | (v1.2) per depth on the largest instance of each family: log10 kappa of the Jacobian restricted to columns of depth <= d, the full-class effective dimension fraction and the six-column matched-size median of 50 draws |
| `coherence_mechanism_test.csv` | (v1.2) test of the band-counting argument on unseen instances ({{7,3}} L=5, {{4,5}} L=7, square R=16, triangular R=10.75): effective dimension fraction and depth-restricted log10 kappa per depth |
| `flat_rate_windows.csv` | (v1.2, exploratory) growth rate of the depth-restricted log10 kappa over windows fixed in relative depth on square R=6..22 and triangular R=3.2..17.2, with per-class effective dimension fraction and smallest normalised-Gram eigenvalue |
| `spectral_gap.csv` | Dirichlet spectral gap, RC relaxation time and stiffness; `exploratory=True` rows were not preregistered |
| `interior_degree.json` | Integer check that interior nodes of the {{7,3}} truncations have degree 3, and that the interior of G_L is G_(L-1) (coordinates and edge sets) |
| `integrator_controls.csv` | K1 (matrix-exponential known answer) and K2 (steady state = DtN column) controls per integrator; rows with `status` other than `run` were not executed |
| `rc_step_response.csv` | Reference (matrix-exponential) RC step response V_i(t), C=1, on {{7,3}} L=2 and square R=6 |
## Provenance and reproduction
Generated from commit `{commit}` of
https://github.com/xaviercallens/SocrateAI-Scientific-CondensedMatterTheory, directory
`experiments/track_h_hyperbolic_network`:
```
python3 hyperbolic_network.py --self-test && python3 hyperbolic_exact.py --self-test
python3 hyperbolic_exact.py && python3 probe_matched.py && python3 identifiability.py && python3 rc_network.py
python3 h2_explore.py && python3 interior_degree.py
python3 release/export.py
```
Each claim built on these data carries an evidence tier in `docs/elenchus/ledger.json` of that repository.
## Known limitations
- Condition numbers are float64 SVD values; "numerically singular" means unresolved, not infinite.
- The two-integrator (SciPy BDF, rusty-SUNDIALS CVODE) cross-validation covers two networks of ~110 nodes.
- The probe-matched identifiable-subspace metric was chosen post hoc (deviation from preregistration).
## License
Data and paper: CC BY 4.0. Code (see the companion model repository and the Zenodo archive): MIT.
## Citation
Callens, X. (2026). Logarithmic boundary depth and the conditioning of the discrete inverse
conductance problem on hyperbolic lattices. Preprint.
"""
def model_card(commit):
return f"""---
license: mit
tags:
- physics
- numerical-simulation
- inverse-problems
- resistor-networks
- hyperbolic-lattices
library_name: numpy
---
# Hyperbolic resistor-network simulator (numerical model, no trained weights)
This repository holds a **numerical simulator**, not a machine-learning model: there are no weights and
no training. It builds layer-truncated hyperbolic {{p,q}} tilings and flat lattice disks, and computes
- the Dirichlet-to-Neumann map (Schur complement) and its exact sensitivity Jacobian,
- exact Jacobian ranks over GF(p),
- the Neumann-to-Dirichlet map and its conditioning,
- the RC network dynamics `C dV/dt = -(L_ii V + L_ib V_b)` with SciPy BDF or, when installed, the
rusty-SUNDIALS CVODE integrator (https://github.com/xaviercallens/rusty-SUNDIALS), gated by
analytic controls K1 (matrix exponential) and K2 (steady state = DtN).
## Use
```
pip install numpy scipy
python3 hyperbolic_network.py --self-test
python3 -c "from hyperbolic_network import build_hyperbolic, boundary_nodes, dtn; import numpy as np; \\
g = build_hyperbolic(7, 3, 2); b = boundary_nodes(g); print(dtn(len(g['nodes']), g['edges'], np.ones(len(g['edges'])), b).shape)"
```
To run the CVODE leg, build the rusty-SUNDIALS Python extension (`maturin develop` in
`crates/rusty-sundials-py`) and re-run `rc_network.py`.
## Intended use and limits
Research on conditioning of discrete inverse problems and design of tabletop resistor/RC network experiments.
Results are linearised sensitivities at unit conductances; see the paper's limitations section.
Source commit: `{commit}`. Dataset: companion Hugging Face dataset. Paper: `paper.pdf`.
"""
def zip_dir(src, dst):
with zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as z:
for p in sorted(src.rglob("*")):
if p.is_file():
z.write(p, p.relative_to(src))
def main():
if BUILD.exists(): # generated output only
shutil.rmtree(BUILD)
DS.mkdir(parents=True)
commit = git_commit()
# default: the published v1.1 PDF (keeps the v1.1 bundles reproducible); --v12 bundles the v1.2 draft instead
pdf = TRACK / "paper" / ("main_v1_2.pdf" if "--v12" in sys.argv else "main.pdf")
print("bundling", pdf.name)
export_graphs()
export_tables()
export_timeseries()
shutil.copy(pdf, DS / "paper.pdf")
(DS / "README.md").write_text(dataset_card(commit))
MD.mkdir()
for f in CODE_FILES:
dst = MD / f
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(TRACK / f, dst)
shutil.copy(pdf, MD / "paper.pdf")
(MD / "README.md").write_text(model_card(commit))
(MD / "LICENSE").write_text((HERE_TEX / "LICENSE-MIT").read_text())
ZD.mkdir()
shutil.copy(pdf, ZD / "paper.pdf")
zip_dir(DS, ZD / "dataset.zip")
zip_dir(MD, ZD / "code.zip")
(BUILD / "MANIFEST.json").write_text(json.dumps({
"commit": commit,
"files": {str(p.relative_to(BUILD)): p.stat().st_size for p in sorted(BUILD.rglob("*")) if p.is_file()}},
indent=1))
print(f"built {BUILD} from commit {commit[:12]}")
if __name__ == "__main__":
main()