sync code from github@d2dbab26ef57a01a14fbe811b084a3716af12e52
Browse files- README.md +22 -23
- analysis/code/build_nb_composite_models.py +9 -9
- analysis/code/build_nb_leveling.py +13 -9
- analysis/code/build_nb_scaling.py +3 -3
- analysis/code/composite_models.py +3 -3
- analysis/code/composite_plots.py +2 -2
- analysis/code/delta22.py +7 -7
- analysis/code/leveling.py +36 -37
- analysis/code/test_delta22_analysis.py +5 -4
- analysis/manuscript_figures/fig5b_dft8k.ipynb +1 -7
- analysis/si_figures/si_composite_models_ablations.ipynb +21 -21
- analysis/si_figures/si_figure_s03_leveling.ipynb +37 -33
- analysis/si_tables/si_table_s10_s11_scaling.ipynb +14 -14
- data/applications/applications_reader.py +1 -1
- data/applications/test_applications.py +11 -0
- data/delta22/delta22_reader.py +2 -2
- data/delta22/test_delta22.py +11 -0
- data/delta50/delta50.hdf5 +0 -0
- magnet/eqV2/edge_rot_mat.py +10 -8
- magnet/eqV2/equiformer_v2_NMR.py +1 -3
- magnet/test_api.py +22 -0
README.md
CHANGED
|
@@ -26,10 +26,10 @@ MagNET is a family of neural networks for predicting NMR chemical shifts. This r
|
|
| 26 |
|
| 27 |
| Model | Description | Details |
|
| 28 |
|---|---|---|
|
| 29 |
-
| **MagNET** | foundation model |
|
| 30 |
-
| **MagNET-Zero** | high-quality gas-phase solute shieldings |
|
| 31 |
-
| **MagNET-PCM** | implicit solvent corrections |
|
| 32 |
-
| **MagNET-x** | explicit solvent corrections |
|
| 33 |
|
| 34 |
All models use Equiformer-V2 and have approximately 10M weights. Separate weights are given for <sup>1</sup>H and <sup>13</sup>C prediction. Input structures can contain H, C, N, O, F, Cl, and S (inference should not be performed on structures with unsupported elements).
|
| 35 |
|
|
@@ -37,11 +37,11 @@ All models use Equiformer-V2 and have approximately 10M weights. Separate weight
|
|
| 37 |
|
| 38 |
| Dataset | Details |
|
| 39 |
|---|---|
|
| 40 |
-
| **_sigma_-shake** |
|
| 41 |
-
| **_sigma_-fresh** |
|
| 42 |
-
| **_sigma_-pepper** (part 1) |
|
| 43 |
-
| **_sigma_-pepper** (part 2) |
|
| 44 |
-
| **_sigma_-concentrate** |
|
| 45 |
|
| 46 |
### Installing MagNET
|
| 47 |
|
|
@@ -128,8 +128,7 @@ for name, indices in sites.items():
|
|
| 128 |
print(f"{name:<13} {shifts[indices].mean():6.2f} ppm")
|
| 129 |
```
|
| 130 |
|
| 131 |
-
The predictions
|
| 132 |
-
dataset):
|
| 133 |
|
| 134 |
| site | MagNET | experiment (CDCl<sub>3</sub>) |
|
| 135 |
|---|---|---|
|
|
@@ -140,7 +139,7 @@ dataset):
|
|
| 140 |
**Notes:**
|
| 141 |
|
| 142 |
- **Passes and symmetry.** `n_passes` (default 10) averages out equivariance error over the specified number of forward passes. If `symmetrize` (default True) is set, an additional `n_passes` are also performed on the mirror image of the input geometry (for a total of `2*n_passes`).
|
| 143 |
-
- **Geometry.** MagNET-Zero and MagNET-PCM require AIMNet2-optimized geometries. Do not use other geometries.
|
| 144 |
- **Components.** Pass `return_components=True` to also get the MagNET-Zero shielding, the MagNET-PCM correction, and the scaling coefficients behind each shift, as a `dict`.
|
| 145 |
- **Other models.** For raw shieldings and solvent corrections, see the [API documentation](#api-documentation).
|
| 146 |
|
|
@@ -155,16 +154,16 @@ If you only want to download a single dataset, add
|
|
| 155 |
|
| 156 |
| dataset | size | contents |
|
| 157 |
|---|---|---|
|
| 158 |
-
| `sigma-shake` | 2.2 GB |
|
| 159 |
-
| `sigma-fresh` | 30 GB |
|
| 160 |
| `sigma-pepper` | 501 MB | AIMNet2 GDB structures used to train MagNET-Zero and MagNET-PCM |
|
| 161 |
| `sigma-concentrate` | 17 MB | PCM corrections on 50K *sigma*-shake structures |
|
| 162 |
| `delta22` | 2.5 GB | experimental <sup>1</sup>H/<sup>13</sup>C shifts with matched DFT and MagNET predictions |
|
| 163 |
-
| `dft8k` | 12 MB | external benchmark of 7111 organics
|
| 164 |
-
| `gdb_qcd` | 291 MB | quasiclassical
|
| 165 |
-
| `magnet_test_predictions` | 822 MB | MagNET
|
| 166 |
-
| `applications` | 5.1 GB | natural
|
| 167 |
-
| `supertestset_magnet_x` | 1.2 MB | explicit
|
| 168 |
|
| 169 |
### Reproducing Figures and Tables
|
| 170 |
|
|
@@ -186,7 +185,7 @@ pip install -r requirements.txt "pytest>=7"
|
|
| 186 |
pytest
|
| 187 |
```
|
| 188 |
|
| 189 |
-
The tests use small synthetic fixtures
|
| 190 |
|
| 191 |
### API Documentation
|
| 192 |
|
|
@@ -203,7 +202,7 @@ API: `predict_shifts`, `predict_shieldings`, `implicit_solvent_correction`,
|
|
| 203 |
|
| 204 |
"Chemical Shift Prediction Beyond the Electronic Structure Limit."
|
| 205 |
|
| 206 |
-
Adams, K.; Wagen, C.C.
|
| 207 |
|
| 208 |
*submitted*, July 2026.
|
| 209 |
|
|
@@ -211,5 +210,5 @@ Adams, K.; Wagen, C.C.W.; Wolford, J.; Sak, M.H.; Saurí, J.; Feng, Z.; Bhadauri
|
|
| 211 |
|
| 212 |
- The original code, model weights, and datasets in this repository are released under the MIT License (see [`LICENSE`](LICENSE)).
|
| 213 |
- Third-party literature data redistributed here remains subject to its original publications' terms and should be
|
| 214 |
-
cited accordingly:
|
| 215 |
-
|
|
|
|
| 26 |
|
| 27 |
| Model | Description | Details |
|
| 28 |
|---|---|---|
|
| 29 |
+
| **MagNET** | foundation model | <ul><li>for near-equilibrium geometries</li><li>trained on PBE0/pcSseg-1/gas shieldings</li></ul> |
|
| 30 |
+
| **MagNET-Zero** | high-quality gas-phase solute shieldings | <ul><li>use AIMNet2-optimized geometries</li><li>WP04/pcSseg-2 (<sup>1</sup>H shieldings)</li><li>ωB97X-D/pcSseg-2 (<sup>13</sup>C shieldings)</li></ul> |
|
| 31 |
+
| **MagNET-PCM** | implicit solvent corrections | <ul><li>shielding(PCM) - shielding(gas)</li><li>use AIMNet2-optimized geometries</li><li>computed at B3LYP-D3(BJ)/pcSseg-2/chloroform</li></ul> |
|
| 32 |
+
| **MagNET-x** | explicit solvent corrections | <ul><li>shielding(solute+solvent) - shielding(solute)</li><li>supports chloroform, benzene, methanol, and water</li><li>use classical MD geometries</li><li>trained at PBE0/pcSseg-1</li></ul> |
|
| 33 |
|
| 34 |
All models use Equiformer-V2 and have approximately 10M weights. Separate weights are given for <sup>1</sup>H and <sup>13</sup>C prediction. Input structures can contain H, C, N, O, F, Cl, and S (inference should not be performed on structures with unsupported elements).
|
| 35 |
|
|
|
|
| 37 |
|
| 38 |
| Dataset | Details |
|
| 39 |
|---|---|
|
| 40 |
+
| **_sigma_-shake** | <ul><li>4.4M solutes from GDB-13/17 with functional group augmentation</li><li>stationary and quasiclassically perturbed structures at B3LYP-D3(BJ)/6-31G\*</li><li>PBE0/pcSseg-1/gas shieldings</li></ul> |
|
| 41 |
+
| **_sigma_-fresh** | <ul><li>10K representative natural product, drug-like, sugar, and peptide solutes dissolved in benzene, chloroform, methanol, and water</li><li>~10 computed poses/solute with many more solvated geometries available</li><li>462K poses have computed PBE0/pcSseg-1 shieldings</li></ul> |
|
| 42 |
+
| **_sigma_-pepper** (part 1) | <ul><li>GDB molecules with ≤ 10 heavy atoms</li><li>AIMNet2 stationary structures</li><li>PBE0/pcSseg-1 shieldings</li></ul> |
|
| 43 |
+
| **_sigma_-pepper** (part 2) | <ul><li>GDB molecules with ≤ 9 heavy atoms</li><li>AIMNet2 stationary structures</li><li>WP04/pcSseg-2/gas (<sup>1</sup>H shieldings)</li><li>ωB97X-D/pcSseg-2/gas (<sup>13</sup>C shieldings)</li></ul> |
|
| 44 |
+
| **_sigma_-concentrate** | <ul><li>50K random structures from *sigma*-shake</li><li>PCM(chloroform) corrections at B3LYP/pcSseg-2</li></ul> |
|
| 45 |
|
| 46 |
### Installing MagNET
|
| 47 |
|
|
|
|
| 128 |
print(f"{name:<13} {shifts[indices].mean():6.2f} ppm")
|
| 129 |
```
|
| 130 |
|
| 131 |
+
The predictions are close to experiment:
|
|
|
|
| 132 |
|
| 133 |
| site | MagNET | experiment (CDCl<sub>3</sub>) |
|
| 134 |
|---|---|---|
|
|
|
|
| 139 |
**Notes:**
|
| 140 |
|
| 141 |
- **Passes and symmetry.** `n_passes` (default 10) averages out equivariance error over the specified number of forward passes. If `symmetrize` (default True) is set, an additional `n_passes` are also performed on the mirror image of the input geometry (for a total of `2*n_passes`).
|
| 142 |
+
- **Geometry.** MagNET-Zero and MagNET-PCM require [AIMNet2](https://github.com/isayevlab/AIMNet2)-optimized geometries. Do not use other geometries.
|
| 143 |
- **Components.** Pass `return_components=True` to also get the MagNET-Zero shielding, the MagNET-PCM correction, and the scaling coefficients behind each shift, as a `dict`.
|
| 144 |
- **Other models.** For raw shieldings and solvent corrections, see the [API documentation](#api-documentation).
|
| 145 |
|
|
|
|
| 154 |
|
| 155 |
| dataset | size | contents |
|
| 156 |
|---|---|---|
|
| 157 |
+
| `sigma-shake` | 2.2 GB | 4.4M GDB solutes, perturbed geometries, PBE0/pcSseg-1 shieldings |
|
| 158 |
+
| `sigma-fresh` | 30 GB | 10K solutes in explicit benzene, chloroform, methanol, and water |
|
| 159 |
| `sigma-pepper` | 501 MB | AIMNet2 GDB structures used to train MagNET-Zero and MagNET-PCM |
|
| 160 |
| `sigma-concentrate` | 17 MB | PCM corrections on 50K *sigma*-shake structures |
|
| 161 |
| `delta22` | 2.5 GB | experimental <sup>1</sup>H/<sup>13</sup>C shifts with matched DFT and MagNET predictions |
|
| 162 |
+
| `dft8k` | 12 MB | external benchmark of 7111 organics |
|
| 163 |
+
| `gdb_qcd` | 291 MB | quasiclassical dynamics of 2461 GDB molecules |
|
| 164 |
+
| `magnet_test_predictions` | 822 MB | MagNET predictions and DFT targets |
|
| 165 |
+
| `applications` | 5.1 GB | natural product MD geometries and experimental shifts |
|
| 166 |
+
| `supertestset_magnet_x` | 1.2 MB | explicit solvent test set for MagNET-x |
|
| 167 |
|
| 168 |
### Reproducing Figures and Tables
|
| 169 |
|
|
|
|
| 185 |
pytest
|
| 186 |
```
|
| 187 |
|
| 188 |
+
The tests use small synthetic fixtures. If the large files are present, then more comprehensive tests will run. The `magnet` package tests need the Option B checkout with the inference stack installed (`pip install -r magnet/requirements.txt`); without PyTorch they are skipped.
|
| 189 |
|
| 190 |
### API Documentation
|
| 191 |
|
|
|
|
| 202 |
|
| 203 |
"Chemical Shift Prediction Beyond the Electronic Structure Limit."
|
| 204 |
|
| 205 |
+
Adams, K.; Wagen, C.C.; Wolford, J.; Sak, M.H.; Saurí, J.; Feng, Z.; Bhadauria, A.S.; Bailey, M.A.; Downs, J.S.; Li, S.Z.; Liu, A.I.; Smidt, T.; Paton, R.S.; Liu, R.Y.; Coley, C.W.\*; Kwan, E.E.\*
|
| 206 |
|
| 207 |
*submitted*, July 2026.
|
| 208 |
|
|
|
|
| 210 |
|
| 211 |
- The original code, model weights, and datasets in this repository are released under the MIT License (see [`LICENSE`](LICENSE)).
|
| 212 |
- Third-party literature data redistributed here remains subject to its original publications' terms and should be
|
| 213 |
+
cited accordingly: CP3 (`data/cp3/`, Smith and Goodman), NS372
|
| 214 |
+
(`data/ns372/`, Schattenberg and Kaupp), and DFT8K (`data/dft8k/`, Guan and Paton).
|
analysis/code/build_nb_composite_models.py
CHANGED
|
@@ -54,13 +54,13 @@ N_SPLITS = 250
|
|
| 54 |
|
| 55 |
si_composite_models_ablations = [
|
| 56 |
md(r"""
|
| 57 |
-
#
|
| 58 |
|
| 59 |
-
Rebuilds the
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
"""),
|
| 65 |
code(_BOOTSTRAP),
|
| 66 |
code(_IMPORTS),
|
|
@@ -93,13 +93,13 @@ PCM-vs-Desmond table.
|
|
| 93 |
"""),
|
| 94 |
code(r"""
|
| 95 |
for nucleus, label in [("H", "Proton"), ("C", "Carbon")]:
|
| 96 |
-
corr = delta22.
|
| 97 |
nuc_label = "1H" if nucleus == "H" else "13C"
|
| 98 |
nuc_title = "$^{1}$H" if nucleus == "H" else "$^{13}$C"
|
| 99 |
composite_plots.plot_feature_correlation_heatmap(
|
| 100 |
corr["r"], vmin=-1, vmax=1, cmap="RdBu",
|
| 101 |
title=f"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}",
|
| 102 |
-
save_path=figure_path(f"
|
| 103 |
plt.show()
|
| 104 |
"""),
|
| 105 |
code(r"""
|
|
@@ -107,7 +107,7 @@ pcm_desmond_corr = delta22.pcm_desmond_correlation_by_solvent(query_df_dft)
|
|
| 107 |
print("PCM vs. Desmond Pearson R by nucleus and solvent:")
|
| 108 |
display(pcm_desmond_corr.round(3))
|
| 109 |
composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,
|
| 110 |
-
save_path=figure_path("
|
| 111 |
plt.show()
|
| 112 |
"""),
|
| 113 |
]
|
|
|
|
| 54 |
|
| 55 |
si_composite_models_ablations = [
|
| 56 |
md(r"""
|
| 57 |
+
# Composite-formula ablation workbook
|
| 58 |
|
| 59 |
+
Rebuilds the composite-formula ablation grid: ~25 composite-formula variants (stationary geometry
|
| 60 |
+
plus some combination of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across
|
| 61 |
+
all 12 delta-22 solvents, at two reference levels: DSD-PBEP86/pcSseg-3 (geometry PBE0/tz) and the
|
| 62 |
+
MagNET-Zero training reference (WP04/pcSseg-2 for ¹H, ωB97X-D/pcSseg-2 for ¹³C). Also reports the
|
| 63 |
+
solvent-averaged correlations between the composite-model features.
|
| 64 |
"""),
|
| 65 |
code(_BOOTSTRAP),
|
| 66 |
code(_IMPORTS),
|
|
|
|
| 93 |
"""),
|
| 94 |
code(r"""
|
| 95 |
for nucleus, label in [("H", "Proton"), ("C", "Carbon")]:
|
| 96 |
+
corr = delta22.ablations_feature_correlations(query_df_dft, nucleus)
|
| 97 |
nuc_label = "1H" if nucleus == "H" else "13C"
|
| 98 |
nuc_title = "$^{1}$H" if nucleus == "H" else "$^{13}$C"
|
| 99 |
composite_plots.plot_feature_correlation_heatmap(
|
| 100 |
corr["r"], vmin=-1, vmax=1, cmap="RdBu",
|
| 101 |
title=f"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}",
|
| 102 |
+
save_path=figure_path(f"ablations_feature_corr_r_{nuc_label}.png"))
|
| 103 |
plt.show()
|
| 104 |
"""),
|
| 105 |
code(r"""
|
|
|
|
| 107 |
print("PCM vs. Desmond Pearson R by nucleus and solvent:")
|
| 108 |
display(pcm_desmond_corr.round(3))
|
| 109 |
composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,
|
| 110 |
+
save_path=figure_path("ablations_pcm_desmond_corr_table.png"))
|
| 111 |
plt.show()
|
| 112 |
"""),
|
| 113 |
]
|
analysis/code/build_nb_leveling.py
CHANGED
|
@@ -29,7 +29,7 @@ Two independent NMR shielding datasets, analyzed separately (never pooled):
|
|
| 29 |
| Dataset | Source | Methods | Nuclei | Reference |
|
| 30 |
|---|---|---|---|---|
|
| 31 |
| **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |
|
| 32 |
-
| **delta22** | in-house
|
| 33 |
"""
|
| 34 |
|
| 35 |
_CONFIG_MD = "## 1. Configuration"
|
|
@@ -69,8 +69,11 @@ def figure_path(name):
|
|
| 69 |
return os.path.join("figures", name)
|
| 70 |
|
| 71 |
# self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any
|
| 72 |
-
# previously written figures before regenerating
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
| 74 |
os.remove(_stale)
|
| 75 |
|
| 76 |
pd.set_option('display.width', 150)
|
|
@@ -120,14 +123,14 @@ _NS372_DEF_MD = r"""
|
|
| 120 |
|
| 121 |
Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a
|
| 122 |
CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference
|
| 123 |
-
outliers) are applied. Input:
|
| 124 |
"""
|
| 125 |
|
| 126 |
_DELTA22_DEF_MD = r"""
|
| 127 |
## 3. delta22 - definitions
|
| 128 |
|
| 129 |
-
Gas-phase conventional GIAO shieldings from
|
| 130 |
-
largest available basis (pcSseg-3; plain
|
| 131 |
geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no
|
| 132 |
CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it
|
| 133 |
is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)
|
|
@@ -138,7 +141,7 @@ _LOAD_MD = r"""
|
|
| 138 |
## 4. Load datasets + global colour scale
|
| 139 |
|
| 140 |
Run the loaders, analyse every nucleus, and compute the figure-wide
|
| 141 |
-
|
| 142 |
below, for NS372 and delta22 alike.
|
| 143 |
"""
|
| 144 |
|
|
@@ -190,8 +193,9 @@ sum_ns372.round(5)
|
|
| 190 |
md(r"""
|
| 191 |
### 5.2 Per-method scaled RMSE vs CCSD(T)
|
| 192 |
|
| 193 |
-
|
| 194 |
-
|
|
|
|
| 195 |
"""),
|
| 196 |
code(r"""
|
| 197 |
rmse_ns372 = pd.DataFrame({nuc: res_ns372[nuc]['scaled_rmse'] for nuc in res_ns372})
|
|
|
|
| 29 |
| Dataset | Source | Methods | Nuclei | Reference |
|
| 30 |
|---|---|---|---|---|
|
| 31 |
| **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |
|
| 32 |
+
| **delta22** | in-house delta-22 set | 18 gas-phase functionals, largest basis (pcSseg-3; mp2 → pcSseg-2), PBE0/cc-pVTZ geometry | ¹H ¹³C | DSD-PBEP86 (highest-rung in-set method, stands in for CCSD(T)) |
|
| 33 |
"""
|
| 34 |
|
| 35 |
_CONFIG_MD = "## 1. Configuration"
|
|
|
|
| 69 |
return os.path.join("figures", name)
|
| 70 |
|
| 71 |
# self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any
|
| 72 |
+
# previously written figures before regenerating. Scope the delete to THIS figure's own prefix:
|
| 73 |
+
# the figures/ folder is shared with the other notebooks in this directory, and a full reproduce
|
| 74 |
+
# run executes them in name order, so a broad "figures/*.png" wipe would destroy the panels written
|
| 75 |
+
# by notebooks that sort before this one. (glob on a missing folder returns [])
|
| 76 |
+
for _stale in glob.glob(os.path.join("figures", "si_figure_s03_*.png")):
|
| 77 |
os.remove(_stale)
|
| 78 |
|
| 79 |
pd.set_option('display.width', 150)
|
|
|
|
| 123 |
|
| 124 |
Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a
|
| 125 |
CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference
|
| 126 |
+
outliers) are applied. Input: the Kaupp NS372 supporting-information spreadsheet.
|
| 127 |
"""
|
| 128 |
|
| 129 |
_DELTA22_DEF_MD = r"""
|
| 130 |
## 3. delta22 - definitions
|
| 131 |
|
| 132 |
+
Gas-phase conventional GIAO shieldings from the delta-22 set: 18 functionals at their
|
| 133 |
+
largest available basis (pcSseg-3; plain MP2 only to pcSseg-2), at the PBE0/cc-pVTZ
|
| 134 |
geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no
|
| 135 |
CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it
|
| 136 |
is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)
|
|
|
|
| 141 |
## 4. Load datasets + global colour scale
|
| 142 |
|
| 143 |
Run the loaders, analyse every nucleus, and compute the figure-wide
|
| 144 |
+
-log10(1-|r|) maximum used as the colour scale on every correlation matrix
|
| 145 |
below, for NS372 and delta22 alike.
|
| 146 |
"""
|
| 147 |
|
|
|
|
| 193 |
md(r"""
|
| 194 |
### 5.2 Per-method scaled RMSE vs CCSD(T)
|
| 195 |
|
| 196 |
+
Scaled RMSE = RMSE of residuals after a per-method linear fit of each method's shieldings
|
| 197 |
+
against the CCSD(T) reference (slope and intercept) - the error that survives empirical
|
| 198 |
+
linear scaling.
|
| 199 |
"""),
|
| 200 |
code(r"""
|
| 201 |
rmse_ns372 = pd.DataFrame({nuc: res_ns372[nuc]['scaled_rmse'] for nuc in res_ns372})
|
analysis/code/build_nb_scaling.py
CHANGED
|
@@ -92,9 +92,9 @@ print("Symmetrized deployment Table S11 (13C):"); display(symmetrized["C"])
|
|
| 92 |
md(r"""
|
| 93 |
## Composite-model coefficients (Figure 5C/5D, SI S15)
|
| 94 |
|
| 95 |
-
The natural-products figures use a larger family of per-solvent coefficients fit on delta-22
|
| 96 |
-
|
| 97 |
-
|
| 98 |
"""),
|
| 99 |
code(r"""
|
| 100 |
reader = Applications(paths.dataset_file("applications", root=REPO))
|
|
|
|
| 92 |
md(r"""
|
| 93 |
## Composite-model coefficients (Figure 5C/5D, SI S15)
|
| 94 |
|
| 95 |
+
The natural-products figures use a larger family of per-solvent coefficients fit on delta-22: OLS
|
| 96 |
+
fits, 1000-seed bootstrap resamples, their RMSE distributions, and the PCM conversion factors. These
|
| 97 |
+
regenerate from released inputs alone.
|
| 98 |
"""),
|
| 99 |
code(r"""
|
| 100 |
reader = Applications(paths.dataset_file("applications", root=REPO))
|
analysis/code/composite_models.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""Reproduces the delta-22 composite-formula ablation study
|
| 2 |
|
| 3 |
Rebuilds `ablations.xlsx`: ~25 composite-formula variants (stationary geometry plus some combination
|
| 4 |
of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across all 12 delta-22 solvents,
|
|
@@ -110,8 +110,8 @@ CARBON_FORMULA_CONFIG = {
|
|
| 110 |
_FORMULA_CONFIG = {"H": PROTON_FORMULA_CONFIG, "C": CARBON_FORMULA_CONFIG}
|
| 111 |
|
| 112 |
# This ablation study fits plain OLS composite formulas, not an ML model. The canonical SI text
|
| 113 |
-
# for
|
| 114 |
-
# were considered, including nitromethane" -- other, ML-related analyses (e.g. scaling_factors.py's
|
| 115 |
# EXCLUDE_SOLUTES) drop nitromethane as an ML-training outlier; this module's default excludes nothing.
|
| 116 |
EXCLUDE_SOLUTES = ()
|
| 117 |
|
|
|
|
| 1 |
+
"""Reproduces the delta-22 composite-formula ablation study (the ablations.xlsx workbook).
|
| 2 |
|
| 3 |
Rebuilds `ablations.xlsx`: ~25 composite-formula variants (stationary geometry plus some combination
|
| 4 |
of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across all 12 delta-22 solvents,
|
|
|
|
| 110 |
_FORMULA_CONFIG = {"H": PROTON_FORMULA_CONFIG, "C": CARBON_FORMULA_CONFIG}
|
| 111 |
|
| 112 |
# This ablation study fits plain OLS composite formulas, not an ML model. The canonical SI text
|
| 113 |
+
# for this ablation analysis states that "since no ML was used in this particular analysis, all 22
|
| 114 |
+
# molecules were considered, including nitromethane" -- other, ML-related analyses (e.g. scaling_factors.py's
|
| 115 |
# EXCLUDE_SOLUTES) drop nitromethane as an ML-training outlier; this module's default excludes nothing.
|
| 116 |
EXCLUDE_SOLUTES = ()
|
| 117 |
|
analysis/code/composite_plots.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
-
"""Correlation-heatmap panels for the composite-
|
| 2 |
|
| 3 |
-
Two seaborn heatmaps behind
|
| 4 |
correlation matrix among the five composite-model features (stationary shielding, PCM, Desmond, its
|
| 5 |
vibrational analogue, QCD), and a PCM-vs-Desmond Pearson r table by nucleus and solvent. The notebook
|
| 6 |
supplies the correlation table(s) computed by delta22.py and calls these functions directly.
|
|
|
|
| 1 |
+
"""Correlation-heatmap panels for the composite-formula ablation workbook.
|
| 2 |
|
| 3 |
+
Two seaborn heatmaps behind the ablation workbook's feature-correlation panels: a per-nucleus lower-triangle
|
| 4 |
correlation matrix among the five composite-model features (stationary shielding, PCM, Desmond, its
|
| 5 |
vibrational analogue, QCD), and a PCM-vs-Desmond Pearson r table by nucleus and solvent. The notebook
|
| 6 |
supplies the correlation table(s) computed by delta22.py and calls these functions directly.
|
analysis/code/delta22.py
CHANGED
|
@@ -721,15 +721,15 @@ def correlation_matrix(query_df, value_col, sites_index, column_var):
|
|
| 721 |
return pivot.corr()
|
| 722 |
|
| 723 |
|
| 724 |
-
#
|
| 725 |
-
# one nucleus and solvent.
|
| 726 |
FEATURE_CORRELATION_COLUMNS = ("stationary", "pcm", "desmond", "desmond_vib", "qcd")
|
| 727 |
|
| 728 |
|
| 729 |
def feature_correlation_matrix(query_df, nucleus, solvent, cols=FEATURE_CORRELATION_COLUMNS, squared=False):
|
| 730 |
"""Pearson correlation matrix (R^2 if squared=True) between the feature columns, within one
|
| 731 |
nucleus and solvent, over sites where both features have a value. query_df must be filtered to a
|
| 732 |
-
single (sap_nmr_method, sap_basis, sap_geometry_type);
|
| 733 |
sub = query_df[(query_df["nucleus"] == nucleus) & (query_df["solvent"] == solvent)][list(cols)]
|
| 734 |
sub = sub.dropna(how="all")
|
| 735 |
if len(sub) < 2:
|
|
@@ -740,7 +740,7 @@ def feature_correlation_matrix(query_df, nucleus, solvent, cols=FEATURE_CORRELAT
|
|
| 740 |
|
| 741 |
def average_feature_correlation_matrix(query_df, nucleus, solvents, cols=FEATURE_CORRELATION_COLUMNS,
|
| 742 |
squared=False):
|
| 743 |
-
"""Cell-by-cell nanmean of feature_correlation_matrix over the solvents (
|
| 744 |
solvent-averaged matrix). nanmean so a solvent with too few sites for one feature pair drops out
|
| 745 |
of that cell alone, not the whole average. Same single-level-of-theory requirement."""
|
| 746 |
mats = [feature_correlation_matrix(query_df, nucleus, s, cols, squared) for s in solvents]
|
|
@@ -750,7 +750,7 @@ def average_feature_correlation_matrix(query_df, nucleus, solvents, cols=FEATURE
|
|
| 750 |
return pd.DataFrame(avg, index=mats[0].index, columns=mats[0].columns)
|
| 751 |
|
| 752 |
|
| 753 |
-
def
|
| 754 |
"""Solvent-averaged feature correlations for one nucleus from the unfiltered
|
| 755 |
load_query_df_dft(...) output: filters to the reference level
|
| 756 |
(MAGNET_PCM_OUTPUT_METHODS[nucleus], pcSseg2, aimnet2), then returns {"r": Pearson R matrix,
|
|
@@ -765,9 +765,9 @@ def si_s15_feature_correlations(query_df_dft, nucleus, solvents=None):
|
|
| 765 |
|
| 766 |
|
| 767 |
def pcm_desmond_correlation_by_solvent(query_df_dft, solvents=None):
|
| 768 |
-
"""
|
| 769 |
between the PCM (implicit) and Desmond (explicit) corrections. Filters to the reference level
|
| 770 |
-
per nucleus itself (same as
|
| 771 |
nucleus, one column per solvent."""
|
| 772 |
solvents = list(solvents) if solvents is not None else sorted(query_df_dft["solvent"].unique())
|
| 773 |
rows = {}
|
|
|
|
| 721 |
return pivot.corr()
|
| 722 |
|
| 723 |
|
| 724 |
+
# Composite-formula ablation workbook, "Correlations Between Features": correlations among the
|
| 725 |
+
# composite features within one nucleus and solvent.
|
| 726 |
FEATURE_CORRELATION_COLUMNS = ("stationary", "pcm", "desmond", "desmond_vib", "qcd")
|
| 727 |
|
| 728 |
|
| 729 |
def feature_correlation_matrix(query_df, nucleus, solvent, cols=FEATURE_CORRELATION_COLUMNS, squared=False):
|
| 730 |
"""Pearson correlation matrix (R^2 if squared=True) between the feature columns, within one
|
| 731 |
nucleus and solvent, over sites where both features have a value. query_df must be filtered to a
|
| 732 |
+
single (sap_nmr_method, sap_basis, sap_geometry_type); ablations_feature_correlations does that."""
|
| 733 |
sub = query_df[(query_df["nucleus"] == nucleus) & (query_df["solvent"] == solvent)][list(cols)]
|
| 734 |
sub = sub.dropna(how="all")
|
| 735 |
if len(sub) < 2:
|
|
|
|
| 740 |
|
| 741 |
def average_feature_correlation_matrix(query_df, nucleus, solvents, cols=FEATURE_CORRELATION_COLUMNS,
|
| 742 |
squared=False):
|
| 743 |
+
"""Cell-by-cell nanmean of feature_correlation_matrix over the solvents (the ablation workbook's
|
| 744 |
solvent-averaged matrix). nanmean so a solvent with too few sites for one feature pair drops out
|
| 745 |
of that cell alone, not the whole average. Same single-level-of-theory requirement."""
|
| 746 |
mats = [feature_correlation_matrix(query_df, nucleus, s, cols, squared) for s in solvents]
|
|
|
|
| 750 |
return pd.DataFrame(avg, index=mats[0].index, columns=mats[0].columns)
|
| 751 |
|
| 752 |
|
| 753 |
+
def ablations_feature_correlations(query_df_dft, nucleus, solvents=None):
|
| 754 |
"""Solvent-averaged feature correlations for one nucleus from the unfiltered
|
| 755 |
load_query_df_dft(...) output: filters to the reference level
|
| 756 |
(MAGNET_PCM_OUTPUT_METHODS[nucleus], pcSseg2, aimnet2), then returns {"r": Pearson R matrix,
|
|
|
|
| 765 |
|
| 766 |
|
| 767 |
def pcm_desmond_correlation_by_solvent(query_df_dft, solvents=None):
|
| 768 |
+
"""The ablation workbook's PCM-vs-Desmond table: per nucleus and solvent, the Pearson correlation
|
| 769 |
between the PCM (implicit) and Desmond (explicit) corrections. Filters to the reference level
|
| 770 |
+
per nucleus itself (same as ablations_feature_correlations). Returns a DataFrame indexed by
|
| 771 |
nucleus, one column per solvent."""
|
| 772 |
solvents = list(solvents) if solvents is not None else sorted(query_df_dft["solvent"].unique())
|
| 773 |
rows = {}
|
analysis/code/leveling.py
CHANGED
|
@@ -274,43 +274,42 @@ DELTA22_REF = 'DSD-PBEP86'
|
|
| 274 |
|
| 275 |
def load_delta22(path):
|
| 276 |
"""Load the delta22 shielding matrix, sorted by family, for 1H and 13C sites."""
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
f.close()
|
| 314 |
out = {}
|
| 315 |
for nuc, z in (('1H', 1), ('13C', 6)):
|
| 316 |
M = np.array(rows[z])
|
|
|
|
| 274 |
|
| 275 |
def load_delta22(path):
|
| 276 |
"""Load the delta22 shielding matrix, sorted by family, for 1H and 13C sites."""
|
| 277 |
+
with h5py.File(path, 'r') as f:
|
| 278 |
+
mn = [m.decode() for m in f['conventional_nmr_method_names'][:]]
|
| 279 |
+
order = ['hf', 'blyp_d3bj', 'bp86_d3bj', 'b97d3_d3bj', 'tpsstpss_d3bj',
|
| 280 |
+
'b3lyp_d3bj', 'pbe0_d3bj', 'm062x_d3', 'wb97xd', 'wp04', 'wc04',
|
| 281 |
+
'B2PLYP', 'mPW2PLYP', 'B2GP_PLYP', 'dsd_pbep86', 'revdsd_pbep86',
|
| 282 |
+
'mp2', 'dlpno_mp2']
|
| 283 |
+
disp = {'hf': 'HF', 'blyp_d3bj': 'BLYP-D3', 'bp86_d3bj': 'BP86-D3',
|
| 284 |
+
'b97d3_d3bj': 'B97D3', 'tpsstpss_d3bj': 'TPSS-D3', 'b3lyp_d3bj': 'B3LYP-D3',
|
| 285 |
+
'pbe0_d3bj': 'PBE0-D3', 'm062x_d3': 'M06-2X-D3', 'wb97xd': 'ωB97X-D',
|
| 286 |
+
'wp04': 'WP04', 'wc04': 'WC04', 'B2PLYP': 'B2PLYP', 'mPW2PLYP': 'mPW2PLYP',
|
| 287 |
+
'B2GP_PLYP': 'B2GP-PLYP', 'dsd_pbep86': 'DSD-PBEP86',
|
| 288 |
+
'revdsd_pbep86': 'revDSD-PBEP86', 'mp2': 'MP2', 'dlpno_mp2': 'DLPNO-MP2'}
|
| 289 |
+
fam = {'hf': 'WFT', 'blyp_d3bj': 'GGA', 'bp86_d3bj': 'GGA', 'b97d3_d3bj': 'GGA',
|
| 290 |
+
'tpsstpss_d3bj': 'mGGA', 'b3lyp_d3bj': 'GH', 'pbe0_d3bj': 'GH',
|
| 291 |
+
'm062x_d3': 'GH', 'wb97xd': 'RSH', 'wp04': 'GH', 'wc04': 'GH',
|
| 292 |
+
'B2PLYP': 'DH', 'mPW2PLYP': 'DH', 'B2GP_PLYP': 'DH', 'dsd_pbep86': 'DH',
|
| 293 |
+
'revdsd_pbep86': 'DH', 'mp2': 'WFT', 'dlpno_mp2': 'WFT'}
|
| 294 |
+
gas = {}
|
| 295 |
+
for i, m in enumerate(mn):
|
| 296 |
+
func, basis, smodel, _ = m.split(',')
|
| 297 |
+
if smodel == 'gas':
|
| 298 |
+
gas.setdefault(func, {})[basis] = i
|
| 299 |
+
sel, labels, families = [], [], []
|
| 300 |
+
for func in order:
|
| 301 |
+
b = gas[func]
|
| 302 |
+
big = 'pcSseg3' if 'pcSseg3' in b else ('pcSseg2' if 'pcSseg2' in b else 'pcSseg1')
|
| 303 |
+
sel.append(b[big]); labels.append(disp[func]); families.append(fam[func])
|
| 304 |
+
GEOM = 1 # geometries axis: 0 = AIMNet2, 1 = PBE0/cc-pVTZ
|
| 305 |
+
rows = {1: [], 6: []}
|
| 306 |
+
for s in f['solutes']:
|
| 307 |
+
an = f['solutes'][s]['atomic_numbers'][:]
|
| 308 |
+
cs = _decode_fixed_point(
|
| 309 |
+
f['solutes'][s]['stationary_and_pcm']['conventional_shieldings'][:, GEOM, :])
|
| 310 |
+
for ai, z in enumerate(an):
|
| 311 |
+
if z in (1, 6):
|
| 312 |
+
rows[int(z)].append(cs[sel, ai])
|
|
|
|
| 313 |
out = {}
|
| 314 |
for nuc, z in (('1H', 1), ('13C', 6)):
|
| 315 |
M = np.array(rows[z])
|
analysis/code/test_delta22_analysis.py
CHANGED
|
@@ -790,7 +790,7 @@ def test_fig2a_reproduces_published_magnet_point():
|
|
| 790 |
assert got == pytest.approx(expected, abs=1e-4), f"{nucleus}: {got} vs {expected}"
|
| 791 |
|
| 792 |
|
| 793 |
-
# ---
|
| 794 |
|
| 795 |
def test_feature_correlation_matrix_synthetic():
|
| 796 |
# a and b perfectly correlated, c independent noise uncorrelated with either
|
|
@@ -841,9 +841,10 @@ def test_average_feature_correlation_matrix_ignores_a_solvent_with_too_few_sites
|
|
| 841 |
@pytest.mark.skipif(
|
| 842 |
not (os.path.exists(REAL_H5) and os.path.exists(REAL_XLSX)),
|
| 843 |
reason="real delta22.hdf5 / experimental xlsx not present")
|
| 844 |
-
def
|
| 845 |
"""Every entry of both nuclei's published Pearson R and R^2 "Correlations Between Features"
|
| 846 |
-
matrices (
|
|
|
|
| 847 |
pooling all DFT methods/bases/geometries together instead of filtering to the MagNET-Zero
|
| 848 |
reference level, which throws entries off by up to 0.03."""
|
| 849 |
dft = D.load_query_df_dft(REAL_H5, REAL_XLSX, verbose=False)
|
|
@@ -866,7 +867,7 @@ def test_si_s15_feature_correlations_reproduces_published_matrices():
|
|
| 866 |
("qcd", "stationary"): 0.380, ("qcd", "pcm"): 0.404, ("qcd", "desmond"): 0.189, ("qcd", "desmond_vib"): 0.021},
|
| 867 |
}
|
| 868 |
for nucleus in ("H", "C"):
|
| 869 |
-
corr = D.
|
| 870 |
for (a, b), expected in published_r[nucleus].items():
|
| 871 |
assert corr["r"].loc[a, b] == pytest.approx(expected, abs=5e-4), f"{nucleus} r[{a},{b}]"
|
| 872 |
for (a, b), expected in published_r2[nucleus].items():
|
|
|
|
| 790 |
assert got == pytest.approx(expected, abs=1e-4), f"{nucleus}: {got} vs {expected}"
|
| 791 |
|
| 792 |
|
| 793 |
+
# --- Composite-formula ablation workbook's "Correlations Between Features" -----------------------
|
| 794 |
|
| 795 |
def test_feature_correlation_matrix_synthetic():
|
| 796 |
# a and b perfectly correlated, c independent noise uncorrelated with either
|
|
|
|
| 841 |
@pytest.mark.skipif(
|
| 842 |
not (os.path.exists(REAL_H5) and os.path.exists(REAL_XLSX)),
|
| 843 |
reason="real delta22.hdf5 / experimental xlsx not present")
|
| 844 |
+
def test_ablations_feature_correlations_reproduces_published_matrices():
|
| 845 |
"""Every entry of both nuclei's published Pearson R and R^2 "Correlations Between Features"
|
| 846 |
+
matrices (the composite-formula ablation workbook), reproduced from the released data to 3
|
| 847 |
+
decimal places. Guards against
|
| 848 |
pooling all DFT methods/bases/geometries together instead of filtering to the MagNET-Zero
|
| 849 |
reference level, which throws entries off by up to 0.03."""
|
| 850 |
dft = D.load_query_df_dft(REAL_H5, REAL_XLSX, verbose=False)
|
|
|
|
| 867 |
("qcd", "stationary"): 0.380, ("qcd", "pcm"): 0.404, ("qcd", "desmond"): 0.189, ("qcd", "desmond_vib"): 0.021},
|
| 868 |
}
|
| 869 |
for nucleus in ("H", "C"):
|
| 870 |
+
corr = D.ablations_feature_correlations(dft, nucleus)
|
| 871 |
for (a, b), expected in published_r[nucleus].items():
|
| 872 |
assert corr["r"].loc[a, b] == pytest.approx(expected, abs=5e-4), f"{nucleus} r[{a},{b}]"
|
| 873 |
for (a, b), expected in published_r2[nucleus].items():
|
analysis/manuscript_figures/fig5b_dft8k.ipynb
CHANGED
|
@@ -144,13 +144,7 @@
|
|
| 144 |
"cell_type": "markdown",
|
| 145 |
"id": "5f4276e7",
|
| 146 |
"metadata": {},
|
| 147 |
-
"source":
|
| 148 |
-
"## Functional-group error breakdown\n",
|
| 149 |
-
"\n",
|
| 150 |
-
"Mean absolute residual for six functional groups (Carbonyls, Amines, Sulfonyl, Pyridines, Furans,\n",
|
| 151 |
-
"Nitroso), via RDKit SMARTS matching over every molecule's SMILES\n",
|
| 152 |
-
"(`dft8k_residuals.FUNCTIONAL_GROUP_SMARTS`)."
|
| 153 |
-
]
|
| 154 |
},
|
| 155 |
{
|
| 156 |
"cell_type": "code",
|
|
|
|
| 144 |
"cell_type": "markdown",
|
| 145 |
"id": "5f4276e7",
|
| 146 |
"metadata": {},
|
| 147 |
+
"source": "## Functional-group error breakdown\n\nMean absolute residual for six functional groups (Carbonyls, Amines, Sulfonyl, Pyridines, Furans,\nNitroso), via RDKit SMARTS matching over every molecule's SMILES."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
},
|
| 149 |
{
|
| 150 |
"cell_type": "code",
|
analysis/si_figures/si_composite_models_ablations.ipynb
CHANGED
|
@@ -2,22 +2,22 @@
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
-
"id": "
|
| 6 |
"metadata": {},
|
| 7 |
"source": [
|
| 8 |
-
"#
|
| 9 |
"\n",
|
| 10 |
-
"Rebuilds the
|
| 11 |
-
"
|
| 12 |
-
"
|
| 13 |
-
"
|
| 14 |
-
"
|
| 15 |
]
|
| 16 |
},
|
| 17 |
{
|
| 18 |
"cell_type": "code",
|
| 19 |
"execution_count": null,
|
| 20 |
-
"id": "
|
| 21 |
"metadata": {},
|
| 22 |
"outputs": [],
|
| 23 |
"source": [
|
|
@@ -32,7 +32,7 @@
|
|
| 32 |
{
|
| 33 |
"cell_type": "code",
|
| 34 |
"execution_count": null,
|
| 35 |
-
"id": "
|
| 36 |
"metadata": {},
|
| 37 |
"outputs": [],
|
| 38 |
"source": [
|
|
@@ -47,7 +47,7 @@
|
|
| 47 |
{
|
| 48 |
"cell_type": "code",
|
| 49 |
"execution_count": null,
|
| 50 |
-
"id": "
|
| 51 |
"metadata": {},
|
| 52 |
"outputs": [],
|
| 53 |
"source": [
|
|
@@ -69,7 +69,7 @@
|
|
| 69 |
{
|
| 70 |
"cell_type": "code",
|
| 71 |
"execution_count": null,
|
| 72 |
-
"id": "
|
| 73 |
"metadata": {},
|
| 74 |
"outputs": [],
|
| 75 |
"source": [
|
|
@@ -80,7 +80,7 @@
|
|
| 80 |
},
|
| 81 |
{
|
| 82 |
"cell_type": "markdown",
|
| 83 |
-
"id": "
|
| 84 |
"metadata": {},
|
| 85 |
"source": [
|
| 86 |
"## Preview: one formula's mean test RMSE at the DSD-PBEP86 reference level"
|
|
@@ -89,7 +89,7 @@
|
|
| 89 |
{
|
| 90 |
"cell_type": "code",
|
| 91 |
"execution_count": null,
|
| 92 |
-
"id": "
|
| 93 |
"metadata": {},
|
| 94 |
"outputs": [],
|
| 95 |
"source": [
|
|
@@ -103,7 +103,7 @@
|
|
| 103 |
},
|
| 104 |
{
|
| 105 |
"cell_type": "markdown",
|
| 106 |
-
"id": "
|
| 107 |
"metadata": {},
|
| 108 |
"source": [
|
| 109 |
"## Build the full ablations workbook (both reference levels, both nuclei)"
|
|
@@ -112,7 +112,7 @@
|
|
| 112 |
{
|
| 113 |
"cell_type": "code",
|
| 114 |
"execution_count": null,
|
| 115 |
-
"id": "
|
| 116 |
"metadata": {},
|
| 117 |
"outputs": [],
|
| 118 |
"source": [
|
|
@@ -122,7 +122,7 @@
|
|
| 122 |
},
|
| 123 |
{
|
| 124 |
"cell_type": "markdown",
|
| 125 |
-
"id": "
|
| 126 |
"metadata": {},
|
| 127 |
"source": [
|
| 128 |
"## Correlations Between Features\n",
|
|
@@ -135,25 +135,25 @@
|
|
| 135 |
{
|
| 136 |
"cell_type": "code",
|
| 137 |
"execution_count": null,
|
| 138 |
-
"id": "
|
| 139 |
"metadata": {},
|
| 140 |
"outputs": [],
|
| 141 |
"source": [
|
| 142 |
"for nucleus, label in [(\"H\", \"Proton\"), (\"C\", \"Carbon\")]:\n",
|
| 143 |
-
" corr = delta22.
|
| 144 |
" nuc_label = \"1H\" if nucleus == \"H\" else \"13C\"\n",
|
| 145 |
" nuc_title = \"$^{1}$H\" if nucleus == \"H\" else \"$^{13}$C\"\n",
|
| 146 |
" composite_plots.plot_feature_correlation_heatmap(\n",
|
| 147 |
" corr[\"r\"], vmin=-1, vmax=1, cmap=\"RdBu\",\n",
|
| 148 |
" title=f\"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}\",\n",
|
| 149 |
-
" save_path=figure_path(f\"
|
| 150 |
"plt.show()"
|
| 151 |
]
|
| 152 |
},
|
| 153 |
{
|
| 154 |
"cell_type": "code",
|
| 155 |
"execution_count": null,
|
| 156 |
-
"id": "
|
| 157 |
"metadata": {},
|
| 158 |
"outputs": [],
|
| 159 |
"source": [
|
|
@@ -161,7 +161,7 @@
|
|
| 161 |
"print(\"PCM vs. Desmond Pearson R by nucleus and solvent:\")\n",
|
| 162 |
"display(pcm_desmond_corr.round(3))\n",
|
| 163 |
"composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,\n",
|
| 164 |
-
" save_path=figure_path(\"
|
| 165 |
"plt.show()"
|
| 166 |
]
|
| 167 |
}
|
|
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
+
"id": "77f6c813",
|
| 6 |
"metadata": {},
|
| 7 |
"source": [
|
| 8 |
+
"# Composite-formula ablation workbook\n",
|
| 9 |
"\n",
|
| 10 |
+
"Rebuilds the composite-formula ablation grid: ~25 composite-formula variants (stationary geometry\n",
|
| 11 |
+
"plus some combination of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across\n",
|
| 12 |
+
"all 12 delta-22 solvents, at two reference levels: DSD-PBEP86/pcSseg-3 (geometry PBE0/tz) and the\n",
|
| 13 |
+
"MagNET-Zero training reference (WP04/pcSseg-2 for ¹H, ωB97X-D/pcSseg-2 for ¹³C). Also reports the\n",
|
| 14 |
+
"solvent-averaged correlations between the composite-model features."
|
| 15 |
]
|
| 16 |
},
|
| 17 |
{
|
| 18 |
"cell_type": "code",
|
| 19 |
"execution_count": null,
|
| 20 |
+
"id": "e00f8b39",
|
| 21 |
"metadata": {},
|
| 22 |
"outputs": [],
|
| 23 |
"source": [
|
|
|
|
| 32 |
{
|
| 33 |
"cell_type": "code",
|
| 34 |
"execution_count": null,
|
| 35 |
+
"id": "b3acc33b",
|
| 36 |
"metadata": {},
|
| 37 |
"outputs": [],
|
| 38 |
"source": [
|
|
|
|
| 47 |
{
|
| 48 |
"cell_type": "code",
|
| 49 |
"execution_count": null,
|
| 50 |
+
"id": "a909ba74",
|
| 51 |
"metadata": {},
|
| 52 |
"outputs": [],
|
| 53 |
"source": [
|
|
|
|
| 69 |
{
|
| 70 |
"cell_type": "code",
|
| 71 |
"execution_count": null,
|
| 72 |
+
"id": "5df608e9",
|
| 73 |
"metadata": {},
|
| 74 |
"outputs": [],
|
| 75 |
"source": [
|
|
|
|
| 80 |
},
|
| 81 |
{
|
| 82 |
"cell_type": "markdown",
|
| 83 |
+
"id": "b7bc63a7",
|
| 84 |
"metadata": {},
|
| 85 |
"source": [
|
| 86 |
"## Preview: one formula's mean test RMSE at the DSD-PBEP86 reference level"
|
|
|
|
| 89 |
{
|
| 90 |
"cell_type": "code",
|
| 91 |
"execution_count": null,
|
| 92 |
+
"id": "ad79f75e",
|
| 93 |
"metadata": {},
|
| 94 |
"outputs": [],
|
| 95 |
"source": [
|
|
|
|
| 103 |
},
|
| 104 |
{
|
| 105 |
"cell_type": "markdown",
|
| 106 |
+
"id": "7ae7d282",
|
| 107 |
"metadata": {},
|
| 108 |
"source": [
|
| 109 |
"## Build the full ablations workbook (both reference levels, both nuclei)"
|
|
|
|
| 112 |
{
|
| 113 |
"cell_type": "code",
|
| 114 |
"execution_count": null,
|
| 115 |
+
"id": "f9acdb9e",
|
| 116 |
"metadata": {},
|
| 117 |
"outputs": [],
|
| 118 |
"source": [
|
|
|
|
| 122 |
},
|
| 123 |
{
|
| 124 |
"cell_type": "markdown",
|
| 125 |
+
"id": "66102dfb",
|
| 126 |
"metadata": {},
|
| 127 |
"source": [
|
| 128 |
"## Correlations Between Features\n",
|
|
|
|
| 135 |
{
|
| 136 |
"cell_type": "code",
|
| 137 |
"execution_count": null,
|
| 138 |
+
"id": "208d343b",
|
| 139 |
"metadata": {},
|
| 140 |
"outputs": [],
|
| 141 |
"source": [
|
| 142 |
"for nucleus, label in [(\"H\", \"Proton\"), (\"C\", \"Carbon\")]:\n",
|
| 143 |
+
" corr = delta22.ablations_feature_correlations(query_df_dft, nucleus)\n",
|
| 144 |
" nuc_label = \"1H\" if nucleus == \"H\" else \"13C\"\n",
|
| 145 |
" nuc_title = \"$^{1}$H\" if nucleus == \"H\" else \"$^{13}$C\"\n",
|
| 146 |
" composite_plots.plot_feature_correlation_heatmap(\n",
|
| 147 |
" corr[\"r\"], vmin=-1, vmax=1, cmap=\"RdBu\",\n",
|
| 148 |
" title=f\"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}\",\n",
|
| 149 |
+
" save_path=figure_path(f\"ablations_feature_corr_r_{nuc_label}.png\"))\n",
|
| 150 |
"plt.show()"
|
| 151 |
]
|
| 152 |
},
|
| 153 |
{
|
| 154 |
"cell_type": "code",
|
| 155 |
"execution_count": null,
|
| 156 |
+
"id": "4e3701cd",
|
| 157 |
"metadata": {},
|
| 158 |
"outputs": [],
|
| 159 |
"source": [
|
|
|
|
| 161 |
"print(\"PCM vs. Desmond Pearson R by nucleus and solvent:\")\n",
|
| 162 |
"display(pcm_desmond_corr.round(3))\n",
|
| 163 |
"composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,\n",
|
| 164 |
+
" save_path=figure_path(\"ablations_pcm_desmond_corr_table.png\"))\n",
|
| 165 |
"plt.show()"
|
| 166 |
]
|
| 167 |
}
|
analysis/si_figures/si_figure_s03_leveling.ipynb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
-
"id": "
|
| 6 |
"metadata": {},
|
| 7 |
"source": [
|
| 8 |
"# SI Figure S3: inter-method correlation and PCA of NS372 and delta-22 shieldings\n",
|
|
@@ -13,7 +13,7 @@
|
|
| 13 |
},
|
| 14 |
{
|
| 15 |
"cell_type": "markdown",
|
| 16 |
-
"id": "
|
| 17 |
"metadata": {},
|
| 18 |
"source": [
|
| 19 |
"Two independent NMR shielding datasets, analyzed separately (never pooled):\n",
|
|
@@ -21,12 +21,12 @@
|
|
| 21 |
"| Dataset | Source | Methods | Nuclei | Reference |\n",
|
| 22 |
"|---|---|---|---|---|\n",
|
| 23 |
"| **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |\n",
|
| 24 |
-
"| **delta22** | in-house
|
| 25 |
]
|
| 26 |
},
|
| 27 |
{
|
| 28 |
"cell_type": "markdown",
|
| 29 |
-
"id": "
|
| 30 |
"metadata": {},
|
| 31 |
"source": [
|
| 32 |
"## 1. Configuration"
|
|
@@ -35,7 +35,7 @@
|
|
| 35 |
{
|
| 36 |
"cell_type": "code",
|
| 37 |
"execution_count": null,
|
| 38 |
-
"id": "
|
| 39 |
"metadata": {},
|
| 40 |
"outputs": [],
|
| 41 |
"source": [
|
|
@@ -49,7 +49,7 @@
|
|
| 49 |
{
|
| 50 |
"cell_type": "code",
|
| 51 |
"execution_count": null,
|
| 52 |
-
"id": "
|
| 53 |
"metadata": {},
|
| 54 |
"outputs": [],
|
| 55 |
"source": [
|
|
@@ -65,7 +65,7 @@
|
|
| 65 |
{
|
| 66 |
"cell_type": "code",
|
| 67 |
"execution_count": null,
|
| 68 |
-
"id": "
|
| 69 |
"metadata": {},
|
| 70 |
"outputs": [],
|
| 71 |
"source": [
|
|
@@ -85,8 +85,11 @@
|
|
| 85 |
" return os.path.join(\"figures\", name)\n",
|
| 86 |
"\n",
|
| 87 |
"# self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any\n",
|
| 88 |
-
"# previously written figures before regenerating
|
| 89 |
-
"
|
|
|
|
|
|
|
|
|
|
| 90 |
" os.remove(_stale)\n",
|
| 91 |
"\n",
|
| 92 |
"pd.set_option('display.width', 150)\n",
|
|
@@ -96,7 +99,7 @@
|
|
| 96 |
{
|
| 97 |
"cell_type": "code",
|
| 98 |
"execution_count": null,
|
| 99 |
-
"id": "
|
| 100 |
"metadata": {},
|
| 101 |
"outputs": [],
|
| 102 |
"source": [
|
|
@@ -139,25 +142,25 @@
|
|
| 139 |
},
|
| 140 |
{
|
| 141 |
"cell_type": "markdown",
|
| 142 |
-
"id": "
|
| 143 |
"metadata": {},
|
| 144 |
"source": [
|
| 145 |
"## 2. NS372 (Kaupp) - definitions\n",
|
| 146 |
"\n",
|
| 147 |
"Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a\n",
|
| 148 |
"CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference\n",
|
| 149 |
-
"outliers) are applied. Input:
|
| 150 |
]
|
| 151 |
},
|
| 152 |
{
|
| 153 |
"cell_type": "markdown",
|
| 154 |
-
"id": "
|
| 155 |
"metadata": {},
|
| 156 |
"source": [
|
| 157 |
"## 3. delta22 - definitions\n",
|
| 158 |
"\n",
|
| 159 |
-
"Gas-phase conventional GIAO shieldings from
|
| 160 |
-
"largest available basis (pcSseg-3; plain
|
| 161 |
"geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no\n",
|
| 162 |
"CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it\n",
|
| 163 |
"is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)\n",
|
|
@@ -166,20 +169,20 @@
|
|
| 166 |
},
|
| 167 |
{
|
| 168 |
"cell_type": "markdown",
|
| 169 |
-
"id": "
|
| 170 |
"metadata": {},
|
| 171 |
"source": [
|
| 172 |
"## 4. Load datasets + global colour scale\n",
|
| 173 |
"\n",
|
| 174 |
"Run the loaders, analyse every nucleus, and compute the figure-wide\n",
|
| 175 |
-
"
|
| 176 |
"below, for NS372 and delta22 alike."
|
| 177 |
]
|
| 178 |
},
|
| 179 |
{
|
| 180 |
"cell_type": "code",
|
| 181 |
"execution_count": null,
|
| 182 |
-
"id": "
|
| 183 |
"metadata": {},
|
| 184 |
"outputs": [],
|
| 185 |
"source": [
|
|
@@ -203,7 +206,7 @@
|
|
| 203 |
},
|
| 204 |
{
|
| 205 |
"cell_type": "markdown",
|
| 206 |
-
"id": "
|
| 207 |
"metadata": {},
|
| 208 |
"source": [
|
| 209 |
"## 5. NS372 results"
|
|
@@ -211,7 +214,7 @@
|
|
| 211 |
},
|
| 212 |
{
|
| 213 |
"cell_type": "markdown",
|
| 214 |
-
"id": "
|
| 215 |
"metadata": {},
|
| 216 |
"source": [
|
| 217 |
"### 5.1 Leveling diagnostics - NS372"
|
|
@@ -220,7 +223,7 @@
|
|
| 220 |
{
|
| 221 |
"cell_type": "code",
|
| 222 |
"execution_count": null,
|
| 223 |
-
"id": "
|
| 224 |
"metadata": {},
|
| 225 |
"outputs": [],
|
| 226 |
"source": [
|
|
@@ -231,19 +234,20 @@
|
|
| 231 |
},
|
| 232 |
{
|
| 233 |
"cell_type": "markdown",
|
| 234 |
-
"id": "
|
| 235 |
"metadata": {},
|
| 236 |
"source": [
|
| 237 |
"### 5.2 Per-method scaled RMSE vs CCSD(T)\n",
|
| 238 |
"\n",
|
| 239 |
-
"
|
| 240 |
-
"
|
|
|
|
| 241 |
]
|
| 242 |
},
|
| 243 |
{
|
| 244 |
"cell_type": "code",
|
| 245 |
"execution_count": null,
|
| 246 |
-
"id": "
|
| 247 |
"metadata": {},
|
| 248 |
"outputs": [],
|
| 249 |
"source": [
|
|
@@ -256,7 +260,7 @@
|
|
| 256 |
},
|
| 257 |
{
|
| 258 |
"cell_type": "markdown",
|
| 259 |
-
"id": "
|
| 260 |
"metadata": {},
|
| 261 |
"source": [
|
| 262 |
"### 5.3 Inter-method correlation matrices - NS372 (one nucleus per file)"
|
|
@@ -265,7 +269,7 @@
|
|
| 265 |
{
|
| 266 |
"cell_type": "code",
|
| 267 |
"execution_count": null,
|
| 268 |
-
"id": "
|
| 269 |
"metadata": {},
|
| 270 |
"outputs": [],
|
| 271 |
"source": [
|
|
@@ -282,7 +286,7 @@
|
|
| 282 |
},
|
| 283 |
{
|
| 284 |
"cell_type": "markdown",
|
| 285 |
-
"id": "
|
| 286 |
"metadata": {},
|
| 287 |
"source": [
|
| 288 |
"### 5.4 PC1/PC2 structure - NS372 (one nucleus per file)"
|
|
@@ -291,7 +295,7 @@
|
|
| 291 |
{
|
| 292 |
"cell_type": "code",
|
| 293 |
"execution_count": null,
|
| 294 |
-
"id": "
|
| 295 |
"metadata": {},
|
| 296 |
"outputs": [],
|
| 297 |
"source": [
|
|
@@ -307,7 +311,7 @@
|
|
| 307 |
},
|
| 308 |
{
|
| 309 |
"cell_type": "markdown",
|
| 310 |
-
"id": "
|
| 311 |
"metadata": {},
|
| 312 |
"source": [
|
| 313 |
"### 5.5 delta22 panels (lead the published figure: corr ¹H/¹³C + PCA)\n",
|
|
@@ -319,7 +323,7 @@
|
|
| 319 |
{
|
| 320 |
"cell_type": "code",
|
| 321 |
"execution_count": null,
|
| 322 |
-
"id": "
|
| 323 |
"metadata": {},
|
| 324 |
"outputs": [],
|
| 325 |
"source": [
|
|
@@ -345,7 +349,7 @@
|
|
| 345 |
},
|
| 346 |
{
|
| 347 |
"cell_type": "markdown",
|
| 348 |
-
"id": "
|
| 349 |
"metadata": {},
|
| 350 |
"source": [
|
| 351 |
"## 6. Combined summary (both datasets)"
|
|
@@ -354,7 +358,7 @@
|
|
| 354 |
{
|
| 355 |
"cell_type": "code",
|
| 356 |
"execution_count": null,
|
| 357 |
-
"id": "
|
| 358 |
"metadata": {},
|
| 359 |
"outputs": [],
|
| 360 |
"source": [
|
|
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
+
"id": "3a2b8d51",
|
| 6 |
"metadata": {},
|
| 7 |
"source": [
|
| 8 |
"# SI Figure S3: inter-method correlation and PCA of NS372 and delta-22 shieldings\n",
|
|
|
|
| 13 |
},
|
| 14 |
{
|
| 15 |
"cell_type": "markdown",
|
| 16 |
+
"id": "0fb6f798",
|
| 17 |
"metadata": {},
|
| 18 |
"source": [
|
| 19 |
"Two independent NMR shielding datasets, analyzed separately (never pooled):\n",
|
|
|
|
| 21 |
"| Dataset | Source | Methods | Nuclei | Reference |\n",
|
| 22 |
"|---|---|---|---|---|\n",
|
| 23 |
"| **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |\n",
|
| 24 |
+
"| **delta22** | in-house delta-22 set | 18 gas-phase functionals, largest basis (pcSseg-3; mp2 → pcSseg-2), PBE0/cc-pVTZ geometry | ¹H ¹³C | DSD-PBEP86 (highest-rung in-set method, stands in for CCSD(T)) |"
|
| 25 |
]
|
| 26 |
},
|
| 27 |
{
|
| 28 |
"cell_type": "markdown",
|
| 29 |
+
"id": "3ff927f7",
|
| 30 |
"metadata": {},
|
| 31 |
"source": [
|
| 32 |
"## 1. Configuration"
|
|
|
|
| 35 |
{
|
| 36 |
"cell_type": "code",
|
| 37 |
"execution_count": null,
|
| 38 |
+
"id": "9e5a25b8",
|
| 39 |
"metadata": {},
|
| 40 |
"outputs": [],
|
| 41 |
"source": [
|
|
|
|
| 49 |
{
|
| 50 |
"cell_type": "code",
|
| 51 |
"execution_count": null,
|
| 52 |
+
"id": "06e0333d",
|
| 53 |
"metadata": {},
|
| 54 |
"outputs": [],
|
| 55 |
"source": [
|
|
|
|
| 65 |
{
|
| 66 |
"cell_type": "code",
|
| 67 |
"execution_count": null,
|
| 68 |
+
"id": "fbd3d68f",
|
| 69 |
"metadata": {},
|
| 70 |
"outputs": [],
|
| 71 |
"source": [
|
|
|
|
| 85 |
" return os.path.join(\"figures\", name)\n",
|
| 86 |
"\n",
|
| 87 |
"# self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any\n",
|
| 88 |
+
"# previously written figures before regenerating. Scope the delete to THIS figure's own prefix:\n",
|
| 89 |
+
"# the figures/ folder is shared with the other notebooks in this directory, and a full reproduce\n",
|
| 90 |
+
"# run executes them in name order, so a broad \"figures/*.png\" wipe would destroy the panels written\n",
|
| 91 |
+
"# by notebooks that sort before this one. (glob on a missing folder returns [])\n",
|
| 92 |
+
"for _stale in glob.glob(os.path.join(\"figures\", \"si_figure_s03_*.png\")):\n",
|
| 93 |
" os.remove(_stale)\n",
|
| 94 |
"\n",
|
| 95 |
"pd.set_option('display.width', 150)\n",
|
|
|
|
| 99 |
{
|
| 100 |
"cell_type": "code",
|
| 101 |
"execution_count": null,
|
| 102 |
+
"id": "84ddcb3f",
|
| 103 |
"metadata": {},
|
| 104 |
"outputs": [],
|
| 105 |
"source": [
|
|
|
|
| 142 |
},
|
| 143 |
{
|
| 144 |
"cell_type": "markdown",
|
| 145 |
+
"id": "4fbeeddb",
|
| 146 |
"metadata": {},
|
| 147 |
"source": [
|
| 148 |
"## 2. NS372 (Kaupp) - definitions\n",
|
| 149 |
"\n",
|
| 150 |
"Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a\n",
|
| 151 |
"CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference\n",
|
| 152 |
+
"outliers) are applied. Input: the Kaupp NS372 supporting-information spreadsheet."
|
| 153 |
]
|
| 154 |
},
|
| 155 |
{
|
| 156 |
"cell_type": "markdown",
|
| 157 |
+
"id": "34326933",
|
| 158 |
"metadata": {},
|
| 159 |
"source": [
|
| 160 |
"## 3. delta22 - definitions\n",
|
| 161 |
"\n",
|
| 162 |
+
"Gas-phase conventional GIAO shieldings from the delta-22 set: 18 functionals at their\n",
|
| 163 |
+
"largest available basis (pcSseg-3; plain MP2 only to pcSseg-2), at the PBE0/cc-pVTZ\n",
|
| 164 |
"geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no\n",
|
| 165 |
"CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it\n",
|
| 166 |
"is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)\n",
|
|
|
|
| 169 |
},
|
| 170 |
{
|
| 171 |
"cell_type": "markdown",
|
| 172 |
+
"id": "eedf9b3d",
|
| 173 |
"metadata": {},
|
| 174 |
"source": [
|
| 175 |
"## 4. Load datasets + global colour scale\n",
|
| 176 |
"\n",
|
| 177 |
"Run the loaders, analyse every nucleus, and compute the figure-wide\n",
|
| 178 |
+
"-log10(1-|r|) maximum used as the colour scale on every correlation matrix\n",
|
| 179 |
"below, for NS372 and delta22 alike."
|
| 180 |
]
|
| 181 |
},
|
| 182 |
{
|
| 183 |
"cell_type": "code",
|
| 184 |
"execution_count": null,
|
| 185 |
+
"id": "4bec0cf4",
|
| 186 |
"metadata": {},
|
| 187 |
"outputs": [],
|
| 188 |
"source": [
|
|
|
|
| 206 |
},
|
| 207 |
{
|
| 208 |
"cell_type": "markdown",
|
| 209 |
+
"id": "667fef3c",
|
| 210 |
"metadata": {},
|
| 211 |
"source": [
|
| 212 |
"## 5. NS372 results"
|
|
|
|
| 214 |
},
|
| 215 |
{
|
| 216 |
"cell_type": "markdown",
|
| 217 |
+
"id": "c5f2cbda",
|
| 218 |
"metadata": {},
|
| 219 |
"source": [
|
| 220 |
"### 5.1 Leveling diagnostics - NS372"
|
|
|
|
| 223 |
{
|
| 224 |
"cell_type": "code",
|
| 225 |
"execution_count": null,
|
| 226 |
+
"id": "8b9b9449",
|
| 227 |
"metadata": {},
|
| 228 |
"outputs": [],
|
| 229 |
"source": [
|
|
|
|
| 234 |
},
|
| 235 |
{
|
| 236 |
"cell_type": "markdown",
|
| 237 |
+
"id": "2b666d04",
|
| 238 |
"metadata": {},
|
| 239 |
"source": [
|
| 240 |
"### 5.2 Per-method scaled RMSE vs CCSD(T)\n",
|
| 241 |
"\n",
|
| 242 |
+
"Scaled RMSE = RMSE of residuals after a per-method linear fit of each method's shieldings\n",
|
| 243 |
+
"against the CCSD(T) reference (slope and intercept) - the error that survives empirical\n",
|
| 244 |
+
"linear scaling."
|
| 245 |
]
|
| 246 |
},
|
| 247 |
{
|
| 248 |
"cell_type": "code",
|
| 249 |
"execution_count": null,
|
| 250 |
+
"id": "4020359a",
|
| 251 |
"metadata": {},
|
| 252 |
"outputs": [],
|
| 253 |
"source": [
|
|
|
|
| 260 |
},
|
| 261 |
{
|
| 262 |
"cell_type": "markdown",
|
| 263 |
+
"id": "ba763232",
|
| 264 |
"metadata": {},
|
| 265 |
"source": [
|
| 266 |
"### 5.3 Inter-method correlation matrices - NS372 (one nucleus per file)"
|
|
|
|
| 269 |
{
|
| 270 |
"cell_type": "code",
|
| 271 |
"execution_count": null,
|
| 272 |
+
"id": "133a5053",
|
| 273 |
"metadata": {},
|
| 274 |
"outputs": [],
|
| 275 |
"source": [
|
|
|
|
| 286 |
},
|
| 287 |
{
|
| 288 |
"cell_type": "markdown",
|
| 289 |
+
"id": "0fa55480",
|
| 290 |
"metadata": {},
|
| 291 |
"source": [
|
| 292 |
"### 5.4 PC1/PC2 structure - NS372 (one nucleus per file)"
|
|
|
|
| 295 |
{
|
| 296 |
"cell_type": "code",
|
| 297 |
"execution_count": null,
|
| 298 |
+
"id": "a37b38c6",
|
| 299 |
"metadata": {},
|
| 300 |
"outputs": [],
|
| 301 |
"source": [
|
|
|
|
| 311 |
},
|
| 312 |
{
|
| 313 |
"cell_type": "markdown",
|
| 314 |
+
"id": "bc460701",
|
| 315 |
"metadata": {},
|
| 316 |
"source": [
|
| 317 |
"### 5.5 delta22 panels (lead the published figure: corr ¹H/¹³C + PCA)\n",
|
|
|
|
| 323 |
{
|
| 324 |
"cell_type": "code",
|
| 325 |
"execution_count": null,
|
| 326 |
+
"id": "74e02974",
|
| 327 |
"metadata": {},
|
| 328 |
"outputs": [],
|
| 329 |
"source": [
|
|
|
|
| 349 |
},
|
| 350 |
{
|
| 351 |
"cell_type": "markdown",
|
| 352 |
+
"id": "49825011",
|
| 353 |
"metadata": {},
|
| 354 |
"source": [
|
| 355 |
"## 6. Combined summary (both datasets)"
|
|
|
|
| 358 |
{
|
| 359 |
"cell_type": "code",
|
| 360 |
"execution_count": null,
|
| 361 |
+
"id": "bffe5ff3",
|
| 362 |
"metadata": {},
|
| 363 |
"outputs": [],
|
| 364 |
"source": [
|
analysis/si_tables/si_table_s10_s11_scaling.ipynb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
-
"id": "
|
| 6 |
"metadata": {},
|
| 7 |
"source": [
|
| 8 |
"# Tables S10 and S11: MagNET-Zero/MagNET-PCM scaling factors\n",
|
|
@@ -14,7 +14,7 @@
|
|
| 14 |
{
|
| 15 |
"cell_type": "code",
|
| 16 |
"execution_count": null,
|
| 17 |
-
"id": "
|
| 18 |
"metadata": {},
|
| 19 |
"outputs": [],
|
| 20 |
"source": [
|
|
@@ -30,7 +30,7 @@
|
|
| 30 |
{
|
| 31 |
"cell_type": "code",
|
| 32 |
"execution_count": null,
|
| 33 |
-
"id": "
|
| 34 |
"metadata": {},
|
| 35 |
"outputs": [],
|
| 36 |
"source": [
|
|
@@ -46,7 +46,7 @@
|
|
| 46 |
{
|
| 47 |
"cell_type": "code",
|
| 48 |
"execution_count": null,
|
| 49 |
-
"id": "
|
| 50 |
"metadata": {},
|
| 51 |
"outputs": [],
|
| 52 |
"source": [
|
|
@@ -61,7 +61,7 @@
|
|
| 61 |
{
|
| 62 |
"cell_type": "code",
|
| 63 |
"execution_count": null,
|
| 64 |
-
"id": "
|
| 65 |
"metadata": {},
|
| 66 |
"outputs": [],
|
| 67 |
"source": [
|
|
@@ -78,7 +78,7 @@
|
|
| 78 |
},
|
| 79 |
{
|
| 80 |
"cell_type": "markdown",
|
| 81 |
-
"id": "
|
| 82 |
"metadata": {},
|
| 83 |
"source": [
|
| 84 |
"## Reproducibility check: re-derive from delta-22"
|
|
@@ -87,7 +87,7 @@
|
|
| 87 |
{
|
| 88 |
"cell_type": "code",
|
| 89 |
"execution_count": null,
|
| 90 |
-
"id": "
|
| 91 |
"metadata": {},
|
| 92 |
"outputs": [],
|
| 93 |
"source": [
|
|
@@ -103,7 +103,7 @@
|
|
| 103 |
},
|
| 104 |
{
|
| 105 |
"cell_type": "markdown",
|
| 106 |
-
"id": "
|
| 107 |
"metadata": {},
|
| 108 |
"source": [
|
| 109 |
"## Deployment tables with reflection symmetrization\n",
|
|
@@ -116,7 +116,7 @@
|
|
| 116 |
{
|
| 117 |
"cell_type": "code",
|
| 118 |
"execution_count": null,
|
| 119 |
-
"id": "
|
| 120 |
"metadata": {},
|
| 121 |
"outputs": [],
|
| 122 |
"source": [
|
|
@@ -127,20 +127,20 @@
|
|
| 127 |
},
|
| 128 |
{
|
| 129 |
"cell_type": "markdown",
|
| 130 |
-
"id": "
|
| 131 |
"metadata": {},
|
| 132 |
"source": [
|
| 133 |
"## Composite-model coefficients (Figure 5C/5D, SI S15)\n",
|
| 134 |
"\n",
|
| 135 |
-
"The natural-products figures use a larger family of per-solvent coefficients fit on delta-22
|
| 136 |
-
"
|
| 137 |
-
"
|
| 138 |
]
|
| 139 |
},
|
| 140 |
{
|
| 141 |
"cell_type": "code",
|
| 142 |
"execution_count": null,
|
| 143 |
-
"id": "
|
| 144 |
"metadata": {},
|
| 145 |
"outputs": [],
|
| 146 |
"source": [
|
|
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
| 5 |
+
"id": "41675f1b",
|
| 6 |
"metadata": {},
|
| 7 |
"source": [
|
| 8 |
"# Tables S10 and S11: MagNET-Zero/MagNET-PCM scaling factors\n",
|
|
|
|
| 14 |
{
|
| 15 |
"cell_type": "code",
|
| 16 |
"execution_count": null,
|
| 17 |
+
"id": "fb163d74",
|
| 18 |
"metadata": {},
|
| 19 |
"outputs": [],
|
| 20 |
"source": [
|
|
|
|
| 30 |
{
|
| 31 |
"cell_type": "code",
|
| 32 |
"execution_count": null,
|
| 33 |
+
"id": "177bdc88",
|
| 34 |
"metadata": {},
|
| 35 |
"outputs": [],
|
| 36 |
"source": [
|
|
|
|
| 46 |
{
|
| 47 |
"cell_type": "code",
|
| 48 |
"execution_count": null,
|
| 49 |
+
"id": "7f56b61d",
|
| 50 |
"metadata": {},
|
| 51 |
"outputs": [],
|
| 52 |
"source": [
|
|
|
|
| 61 |
{
|
| 62 |
"cell_type": "code",
|
| 63 |
"execution_count": null,
|
| 64 |
+
"id": "44a384c9",
|
| 65 |
"metadata": {},
|
| 66 |
"outputs": [],
|
| 67 |
"source": [
|
|
|
|
| 78 |
},
|
| 79 |
{
|
| 80 |
"cell_type": "markdown",
|
| 81 |
+
"id": "219c400a",
|
| 82 |
"metadata": {},
|
| 83 |
"source": [
|
| 84 |
"## Reproducibility check: re-derive from delta-22"
|
|
|
|
| 87 |
{
|
| 88 |
"cell_type": "code",
|
| 89 |
"execution_count": null,
|
| 90 |
+
"id": "b525b479",
|
| 91 |
"metadata": {},
|
| 92 |
"outputs": [],
|
| 93 |
"source": [
|
|
|
|
| 103 |
},
|
| 104 |
{
|
| 105 |
"cell_type": "markdown",
|
| 106 |
+
"id": "14d0ed95",
|
| 107 |
"metadata": {},
|
| 108 |
"source": [
|
| 109 |
"## Deployment tables with reflection symmetrization\n",
|
|
|
|
| 116 |
{
|
| 117 |
"cell_type": "code",
|
| 118 |
"execution_count": null,
|
| 119 |
+
"id": "111de099",
|
| 120 |
"metadata": {},
|
| 121 |
"outputs": [],
|
| 122 |
"source": [
|
|
|
|
| 127 |
},
|
| 128 |
{
|
| 129 |
"cell_type": "markdown",
|
| 130 |
+
"id": "31089f6c",
|
| 131 |
"metadata": {},
|
| 132 |
"source": [
|
| 133 |
"## Composite-model coefficients (Figure 5C/5D, SI S15)\n",
|
| 134 |
"\n",
|
| 135 |
+
"The natural-products figures use a larger family of per-solvent coefficients fit on delta-22: OLS\n",
|
| 136 |
+
"fits, 1000-seed bootstrap resamples, their RMSE distributions, and the PCM conversion factors. These\n",
|
| 137 |
+
"regenerate from released inputs alone."
|
| 138 |
]
|
| 139 |
},
|
| 140 |
{
|
| 141 |
"cell_type": "code",
|
| 142 |
"execution_count": null,
|
| 143 |
+
"id": "d2d9f7b4",
|
| 144 |
"metadata": {},
|
| 145 |
"outputs": [],
|
| 146 |
"source": [
|
data/applications/applications_reader.py
CHANGED
|
@@ -28,7 +28,7 @@ SHELL_SIZES = [50, 150, 250, 350, 450, 550, 650]
|
|
| 28 |
def _decode(values):
|
| 29 |
"""Turn stored whole numbers back into real values; blanks where data is missing."""
|
| 30 |
values = np.asarray(values)
|
| 31 |
-
if values.dtype
|
| 32 |
out = values.astype(np.float64) / _SCALE
|
| 33 |
out[values == _MISSING_MARKER] = np.nan
|
| 34 |
return out
|
|
|
|
| 28 |
def _decode(values):
|
| 29 |
"""Turn stored whole numbers back into real values; blanks where data is missing."""
|
| 30 |
values = np.asarray(values)
|
| 31 |
+
if np.issubdtype(values.dtype, np.integer):
|
| 32 |
out = values.astype(np.float64) / _SCALE
|
| 33 |
out[values == _MISSING_MARKER] = np.nan
|
| 34 |
return out
|
data/applications/test_applications.py
CHANGED
|
@@ -102,6 +102,17 @@ def test_decode_roundtrip_and_missing():
|
|
| 102 |
assert np.isnan(_decode(enc_with_miss)[-1])
|
| 103 |
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
def test_solutes_and_solvents_group_excluded(fake_release):
|
| 106 |
assert set(fake_release.solutes()) == {"vomicine", "isomer_1E"}
|
| 107 |
|
|
|
|
| 102 |
assert np.isnan(_decode(enc_with_miss)[-1])
|
| 103 |
|
| 104 |
|
| 105 |
+
def test_decode_int64_is_still_scaled():
|
| 106 |
+
# a rebuild that stored the columns as int64 (any integer width) must decode the same way as
|
| 107 |
+
# int32; the old exact `dtype == np.int32` check fell through and returned raw scaled integers.
|
| 108 |
+
vals = np.array([1.2345, -50.0, 0.0], dtype=np.float64)
|
| 109 |
+
enc = _enc(vals).astype(np.int64)
|
| 110 |
+
out = _decode(enc)
|
| 111 |
+
assert np.allclose(out, vals, atol=5e-5)
|
| 112 |
+
enc_with_miss = np.append(enc, np.int64(-2147483648))
|
| 113 |
+
assert np.isnan(_decode(enc_with_miss)[-1])
|
| 114 |
+
|
| 115 |
+
|
| 116 |
def test_solutes_and_solvents_group_excluded(fake_release):
|
| 117 |
assert set(fake_release.solutes()) == {"vomicine", "isomer_1E"}
|
| 118 |
|
data/delta22/delta22_reader.py
CHANGED
|
@@ -39,7 +39,7 @@ _MISSING_MARKER = -2147483648
|
|
| 39 |
|
| 40 |
def _decode_fixed_point(values):
|
| 41 |
values = np.asarray(values)
|
| 42 |
-
if values.dtype
|
| 43 |
out = values.astype(np.float64) / _FIXED_POINT_SCALE
|
| 44 |
out[values == _MISSING_MARKER] = np.nan
|
| 45 |
return out
|
|
@@ -74,7 +74,7 @@ def _sort_and_parse_raw_shieldings(shield_df):
|
|
| 74 |
value_list = [v for v in value_list if v]
|
| 75 |
try:
|
| 76 |
return [float(i) for i in value_list]
|
| 77 |
-
except:
|
| 78 |
return []
|
| 79 |
|
| 80 |
def _to_float_list(x):
|
|
|
|
| 39 |
|
| 40 |
def _decode_fixed_point(values):
|
| 41 |
values = np.asarray(values)
|
| 42 |
+
if np.issubdtype(values.dtype, np.integer):
|
| 43 |
out = values.astype(np.float64) / _FIXED_POINT_SCALE
|
| 44 |
out[values == _MISSING_MARKER] = np.nan
|
| 45 |
return out
|
|
|
|
| 74 |
value_list = [v for v in value_list if v]
|
| 75 |
try:
|
| 76 |
return [float(i) for i in value_list]
|
| 77 |
+
except (ValueError, TypeError):
|
| 78 |
return []
|
| 79 |
|
| 80 |
def _to_float_list(x):
|
data/delta22/test_delta22.py
CHANGED
|
@@ -144,6 +144,17 @@ def test_decode_passes_floats_through():
|
|
| 144 |
np.testing.assert_array_equal(_decode_fixed_point(f), f)
|
| 145 |
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
# ---- the full reader against the synthetic file ----
|
| 148 |
EXPECTED_COLUMNS = ["experimental", "stationary", "qcd", "pcm",
|
| 149 |
"desmond", "openMM", "desmond_vib", "openMM_vib"]
|
|
|
|
| 144 |
np.testing.assert_array_equal(_decode_fixed_point(f), f)
|
| 145 |
|
| 146 |
|
| 147 |
+
def test_decode_int64_is_still_scaled():
|
| 148 |
+
# a rebuild that stored the fixed-point columns as int64 (any integer width) must decode the
|
| 149 |
+
# same way as int32; the old exact `dtype == np.int32` check fell through and returned the raw
|
| 150 |
+
# scaled integers as if they were ppm.
|
| 151 |
+
enc = np.array([2070700, _MISSING_MARKER, -150000], np.int64)
|
| 152 |
+
out = _decode_fixed_point(enc)
|
| 153 |
+
assert out[0] == pytest.approx(207.07)
|
| 154 |
+
assert np.isnan(out[1])
|
| 155 |
+
assert out[2] == pytest.approx(-15.0)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
# ---- the full reader against the synthetic file ----
|
| 159 |
EXPECTED_COLUMNS = ["experimental", "stationary", "qcd", "pcm",
|
| 160 |
"desmond", "openMM", "desmond_vib", "openMM_vib"]
|
data/delta50/delta50.hdf5
CHANGED
|
Binary files a/data/delta50/delta50.hdf5 and b/data/delta50/delta50.hdf5 differ
|
|
|
magnet/eqV2/edge_rot_mat.py
CHANGED
|
@@ -5,15 +5,17 @@ def init_edge_rot_mat(edge_distance_vec):
|
|
| 5 |
edge_vec_0 = edge_distance_vec
|
| 6 |
edge_vec_0_distance = torch.sqrt(torch.sum(edge_vec_0**2, dim=1))
|
| 7 |
|
| 8 |
-
#
|
| 9 |
-
#
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
| 15 |
)
|
| 16 |
-
|
| 17 |
norm_x = edge_vec_0 / (edge_vec_0_distance.view(-1, 1))
|
| 18 |
|
| 19 |
edge_vec_2 = torch.rand_like(edge_vec_0) - 0.5
|
|
|
|
| 5 |
edge_vec_0 = edge_distance_vec
|
| 6 |
edge_vec_0_distance = torch.sqrt(torch.sum(edge_vec_0**2, dim=1))
|
| 7 |
|
| 8 |
+
# Two atoms at (nearly) the same position cannot define a local frame: the normalization below
|
| 9 |
+
# would divide by ~zero and silently produce NaN or a garbage frame that propagates through the
|
| 10 |
+
# whole forward pass. Refuse the input instead. This is reachable from raw geometries, e.g.
|
| 11 |
+
# overlapping atoms in an unrelaxed explicit-solvent snapshot.
|
| 12 |
+
min_distance = torch.min(edge_vec_0_distance)
|
| 13 |
+
if min_distance < 0.0001:
|
| 14 |
+
raise ValueError(
|
| 15 |
+
f"overlapping atoms: smallest interatomic distance {float(min_distance):.2e} is too "
|
| 16 |
+
"small to define a local frame; check the input geometry for coincident atoms"
|
| 17 |
)
|
| 18 |
+
|
| 19 |
norm_x = edge_vec_0 / (edge_vec_0_distance.view(-1, 1))
|
| 20 |
|
| 21 |
edge_vec_2 = torch.rand_like(edge_vec_0) - 0.5
|
magnet/eqV2/equiformer_v2_NMR.py
CHANGED
|
@@ -4,7 +4,6 @@ import math
|
|
| 4 |
import numpy as np
|
| 5 |
import torch
|
| 6 |
import torch.nn as nn
|
| 7 |
-
from pyexpat.model import XML_CQUANT_OPT
|
| 8 |
|
| 9 |
from .ocpmodels.common.registry import registry
|
| 10 |
from .ocpmodels.common.utils import conditional_grad
|
|
@@ -422,8 +421,7 @@ class EquiformerV2_NMR(BaseModel):
|
|
| 422 |
edge_index = edge_index[:, ~remove_solvent_edge]
|
| 423 |
edge_distance = edge_distance[~remove_solvent_edge]
|
| 424 |
edge_distance_vec = edge_distance_vec[~remove_solvent_edge]
|
| 425 |
-
|
| 426 |
-
|
| 427 |
#print(edge_index.shape)
|
| 428 |
|
| 429 |
|
|
|
|
| 4 |
import numpy as np
|
| 5 |
import torch
|
| 6 |
import torch.nn as nn
|
|
|
|
| 7 |
|
| 8 |
from .ocpmodels.common.registry import registry
|
| 9 |
from .ocpmodels.common.utils import conditional_grad
|
|
|
|
| 421 |
edge_index = edge_index[:, ~remove_solvent_edge]
|
| 422 |
edge_distance = edge_distance[~remove_solvent_edge]
|
| 423 |
edge_distance_vec = edge_distance_vec[~remove_solvent_edge]
|
| 424 |
+
|
|
|
|
| 425 |
#print(edge_index.shape)
|
| 426 |
|
| 427 |
|
magnet/test_api.py
CHANGED
|
@@ -14,8 +14,11 @@ import pytest
|
|
| 14 |
|
| 15 |
warnings.filterwarnings("ignore")
|
| 16 |
|
|
|
|
|
|
|
| 17 |
import magnet.api as api
|
| 18 |
from magnet import scaling
|
|
|
|
| 19 |
|
| 20 |
_HERE = os.path.dirname(os.path.abspath(__file__))
|
| 21 |
_REPO = os.path.dirname(_HERE) # magnet/ -> repo root
|
|
@@ -461,3 +464,22 @@ def test_real_atoms_beyond_cutoff_crash():
|
|
| 461 |
# two atoms 50 A apart: no edges within cutoff -> same zero-edge crash as one atom.
|
| 462 |
api.predict_shieldings(np.array([6, 1]), np.array([[0., 0., 0.], [0., 0., 50.]]),
|
| 463 |
n_passes=1, symmetrize=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
warnings.filterwarnings("ignore")
|
| 16 |
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
import magnet.api as api
|
| 20 |
from magnet import scaling
|
| 21 |
+
from magnet.eqV2.edge_rot_mat import init_edge_rot_mat
|
| 22 |
|
| 23 |
_HERE = os.path.dirname(os.path.abspath(__file__))
|
| 24 |
_REPO = os.path.dirname(_HERE) # magnet/ -> repo root
|
|
|
|
| 464 |
# two atoms 50 A apart: no edges within cutoff -> same zero-edge crash as one atom.
|
| 465 |
api.predict_shieldings(np.array([6, 1]), np.array([[0., 0., 0.], [0., 0., 50.]]),
|
| 466 |
n_passes=1, symmetrize=False)
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
# ---- degenerate-geometry guard in the local-frame construction (no weights needed) ----
|
| 470 |
+
def test_edge_rot_mat_rejects_coincident_atoms():
|
| 471 |
+
# an edge between two atoms at the same position has ~zero length; the old code printed a
|
| 472 |
+
# warning and then divided by ~zero, silently producing NaN frames that poison the whole
|
| 473 |
+
# forward pass. It must raise instead.
|
| 474 |
+
edge_vecs = torch.tensor([[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]])
|
| 475 |
+
with pytest.raises(ValueError, match="overlapping atoms"):
|
| 476 |
+
init_edge_rot_mat(edge_vecs)
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
def test_edge_rot_mat_accepts_separated_atoms():
|
| 480 |
+
edge_vecs = torch.tensor([[1.0, 0.0, 0.0], [0.0, 1.5, 0.0], [0.0, 0.0, 2.0]])
|
| 481 |
+
rot = init_edge_rot_mat(edge_vecs)
|
| 482 |
+
assert rot.shape == (3, 3, 3)
|
| 483 |
+
# each frame is a proper rotation: R @ R^T == I
|
| 484 |
+
identity = torch.eye(3).expand(3, 3, 3)
|
| 485 |
+
assert torch.allclose(torch.bmm(rot, rot.transpose(1, 2)), identity, atol=1e-4)
|