code wiki / (root) / nx_chem_valence_test.nx

nx_chem_valence_test.nx source

↩ module page · 361 lines · 15449 B

1// nx_chem_valence_test.nx -- C2.3a KAT for implicit-H + molecular weight. 2// 3// Tests verify implicit-H inference matches Daylight 1988 organic-subset 4// rules; molecular weights are checked against NIST WebBook standard 5// values (atomic weights from IUPAC 2021 abridged). 6// 7// expect_exit: 0 8// 9// license_tier: ORIGINAL 10 11import "nx_chem.nx" 12import "nx_chem_molecule.nx" 13import "nx_chem_smiles.nx" 14import "nx_chem_periodic.nx" 15import "nx_chem_valence.nx" 16 17// ================================================================= 18// A -- default valence lookup matches Daylight organic-subset. 19// ================================================================= 20func a_default_valence() -> nx_int { 21 if nx_chem_default_valence(1) != 1 { return 11 } // H 22 if nx_chem_default_valence(5) != 3 { return 12 } // B 23 if nx_chem_default_valence(6) != 4 { return 13 } // C 24 if nx_chem_default_valence(7) != 3 { return 14 } // N 25 if nx_chem_default_valence(8) != 2 { return 15 } // O 26 if nx_chem_default_valence(9) != 1 { return 16 } // F 27 if nx_chem_default_valence(15) != 3 { return 17 } // P 28 if nx_chem_default_valence(16) != 2 { return 18 } // S 29 if nx_chem_default_valence(17) != 1 { return 19 } // Cl 30 if nx_chem_default_valence(35) != 1 { return 20 } // Br 31 if nx_chem_default_valence(53) != 1 { return 21 } // I 32 // outside organic subset returns -1 33 if nx_chem_default_valence(26) != -1 { return 22 } // Fe 34 if nx_chem_default_valence(80) != -1 { return 23 } // Hg 35 if nx_chem_default_valence(82) != -1 { return 24 } // Pb 36 return 0 37} 38 39// ================================================================= 40// B -- methane "C" -- single C, 0 bonds, h_count = 4. 41// ================================================================= 42func b_methane_h() -> nx_int { 43 let s: *u8 = sys_mmap(8); s[0] = 0x43 44 let m: *MolGraph = nx_chem_parse_smiles(s, 1) 45 if m.is_valid != 1 { return 31 } 46 let _f: nx_int = nx_chem_compute_implicit_h(m) 47 let a: *Atom = nx_chem_mol_atom(m, 0) 48 if a.h_count != 4 { return 32 } 49 if nx_chem_mol_h_count_total(m) != 4 { return 33 } 50 return 0 51} 52 53// ================================================================= 54// C -- water "O" -- single O, 0 bonds, h_count = 2. 55// ================================================================= 56func c_water_h() -> nx_int { 57 let s: *u8 = sys_mmap(8); s[0] = 0x4F 58 let m: *MolGraph = nx_chem_parse_smiles(s, 1) 59 if m.is_valid != 1 { return 41 } 60 let _f: nx_int = nx_chem_compute_implicit_h(m) 61 let a: *Atom = nx_chem_mol_atom(m, 0) 62 if a.h_count != 2 { return 42 } 63 return 0 64} 65 66// ================================================================= 67// D -- ethanol "CCO" -- C(3H) - C(2H) - O(1H). 68// ================================================================= 69func d_ethanol_h() -> nx_int { 70 let s: *u8 = sys_mmap(8) 71 s[0] = 0x43; s[1] = 0x43; s[2] = 0x4F 72 let m: *MolGraph = nx_chem_parse_smiles(s, 3) 73 if m.is_valid != 1 { return 51 } 74 let _f: nx_int = nx_chem_compute_implicit_h(m) 75 let c0: *Atom = nx_chem_mol_atom(m, 0) 76 if c0.h_count != 3 { return 52 } 77 let c1: *Atom = nx_chem_mol_atom(m, 1) 78 if c1.h_count != 2 { return 53 } 79 let o: *Atom = nx_chem_mol_atom(m, 2) 80 if o.h_count != 1 { return 54 } 81 if nx_chem_mol_h_count_total(m) != 6 { return 55 } 82 return 0 83} 84 85// ================================================================= 86// E -- ethene "C=C" -- each C has 1 double bond -> 4-2 = 2 H. 87// ================================================================= 88func e_ethene_h() -> nx_int { 89 let s: *u8 = sys_mmap(8) 90 s[0] = 0x43; s[1] = 0x3D; s[2] = 0x43 91 let m: *MolGraph = nx_chem_parse_smiles(s, 3) 92 let _f: nx_int = nx_chem_compute_implicit_h(m) 93 let c0: *Atom = nx_chem_mol_atom(m, 0) 94 if c0.h_count != 2 { return 61 } 95 let c1: *Atom = nx_chem_mol_atom(m, 1) 96 if c1.h_count != 2 { return 62 } 97 return 0 98} 99 100// ================================================================= 101// F -- ethyne "C#C" -- each C has 1 triple bond -> 4-3 = 1 H. 102// ================================================================= 103func f_ethyne_h() -> nx_int { 104 let s: *u8 = sys_mmap(8) 105 s[0] = 0x43; s[1] = 0x23; s[2] = 0x43 106 let m: *MolGraph = nx_chem_parse_smiles(s, 3) 107 let _f: nx_int = nx_chem_compute_implicit_h(m) 108 let c0: *Atom = nx_chem_mol_atom(m, 0) 109 if c0.h_count != 1 { return 71 } 110 let c1: *Atom = nx_chem_mol_atom(m, 1) 111 if c1.h_count != 1 { return 72 } 112 return 0 113} 114 115// ================================================================= 116// G -- benzene "c1ccccc1" -- each c has 2 aromatic bonds; 4 - 2 - 1 = 1 H. 117// ================================================================= 118func g_benzene_h() -> nx_int { 119 let s: *u8 = sys_mmap(16) 120 s[0] = 0x63; s[1] = 0x31 121 s[2] = 0x63; s[3] = 0x63 122 s[4] = 0x63; s[5] = 0x63 123 s[6] = 0x63; s[7] = 0x31 124 let m: *MolGraph = nx_chem_parse_smiles(s, 8) 125 if m.is_valid != 1 { return 81 } 126 let _f: nx_int = nx_chem_compute_implicit_h(m) 127 var i: nx_int = 0 128 while i < 6 { 129 let a: *Atom = nx_chem_mol_atom(m, i) 130 if a.h_count != 1 { return 90 + i } 131 i = i + 1 132 } 133 if nx_chem_mol_h_count_total(m) != 6 { return 99 } 134 return 0 135} 136 137// ================================================================= 138// H -- bracket-explicit H is preserved (NOT overwritten). 139// "[CH4]" -> Atom.h_count stays 4 (from bracket). 140// ================================================================= 141func h_bracket_preserved() -> nx_int { 142 let s: *u8 = sys_mmap(8) 143 s[0] = 0x5B; s[1] = 0x43; s[2] = 0x48; s[3] = 0x34; s[4] = 0x5D 144 let m: *MolGraph = nx_chem_parse_smiles(s, 5) 145 let _f: nx_int = nx_chem_compute_implicit_h(m) 146 let a: *Atom = nx_chem_mol_atom(m, 0) 147 if a.h_count != 4 { return 101 } 148 return 0 149} 150 151// ================================================================= 152// I -- methane molecular weight = 12.011 + 4 * 1.008 = 16.043 153// In Q3 milli-AMU: 12011 + 4032 = 16043. 154// ================================================================= 155func i_methane_mw() -> nx_int { 156 let table: *Element = nx_chem_periodic_table_118() 157 let s: *u8 = sys_mmap(8); s[0] = 0x43 158 let m: *MolGraph = nx_chem_parse_smiles(s, 1) 159 let _f: nx_int = nx_chem_compute_implicit_h(m) 160 let mw: nx_int = nx_chem_mol_weight_q3(m, table) 161 if mw != 16043 { return 111 } 162 return 0 163} 164 165// ================================================================= 166// J -- water molecular weight = 15.999 + 2 * 1.008 = 18.015 167// In Q3 milli-AMU: 15999 + 2016 = 18015. 168// ================================================================= 169func j_water_mw() -> nx_int { 170 let table: *Element = nx_chem_periodic_table_118() 171 let s: *u8 = sys_mmap(8); s[0] = 0x4F 172 let m: *MolGraph = nx_chem_parse_smiles(s, 1) 173 let _f: nx_int = nx_chem_compute_implicit_h(m) 174 let mw: nx_int = nx_chem_mol_weight_q3(m, table) 175 if mw != 18015 { return 121 } 176 return 0 177} 178 179// ================================================================= 180// K -- ethanol molecular weight = 2*12.011 + 15.999 + 6*1.008 = 46.069 181// In Q3 milli-AMU: 24022 + 15999 + 6048 = 46069. 182// NIST WebBook: ethanol 46.068. 183// ================================================================= 184func k_ethanol_mw() -> nx_int { 185 let table: *Element = nx_chem_periodic_table_118() 186 let s: *u8 = sys_mmap(8) 187 s[0] = 0x43; s[1] = 0x43; s[2] = 0x4F 188 let m: *MolGraph = nx_chem_parse_smiles(s, 3) 189 let _f: nx_int = nx_chem_compute_implicit_h(m) 190 let mw: nx_int = nx_chem_mol_weight_q3(m, table) 191 if mw != 46069 { return 131 } 192 return 0 193} 194 195// ================================================================= 196// L -- benzene molecular weight = 6*12.011 + 6*1.008 = 78.114 197// In Q3 milli-AMU: 72066 + 6048 = 78114. 198// NIST WebBook: benzene 78.114. 199// ================================================================= 200func l_benzene_mw() -> nx_int { 201 let table: *Element = nx_chem_periodic_table_118() 202 let s: *u8 = sys_mmap(16) 203 s[0] = 0x63; s[1] = 0x31 204 s[2] = 0x63; s[3] = 0x63 205 s[4] = 0x63; s[5] = 0x63 206 s[6] = 0x63; s[7] = 0x31 207 let m: *MolGraph = nx_chem_parse_smiles(s, 8) 208 let _f: nx_int = nx_chem_compute_implicit_h(m) 209 let mw: nx_int = nx_chem_mol_weight_q3(m, table) 210 if mw != 78114 { return 141 } 211 return 0 212} 213 214// ================================================================= 215// M -- vitamin D3 backbone test: cholecalciferol simplified SMILES 216// "C(C(C)C)C(=C)C" -- 2,3-dimethyl-1-butene-class. Just verify 217// the parser + H-count + MW pipeline works on a small branched 218// olefin. 219// 6 C atoms; 1 C=C; rest single. 220// MW = 6*12.011 + 12*1.008 = 84.162 (the 2,3-dimethyl-1-butene) 221// = 72066 + 12096 = 84162 milli-AMU 222// NIST: 2,3-dimethyl-1-butene = 84.16. 223// ================================================================= 224func m_branched_olefin_mw() -> nx_int { 225 let table: *Element = nx_chem_periodic_table_118() 226 // 2,3-dimethyl-1-butene: "C=C(C)C(C)C" = CH2=C(CH3)CH(CH3)CH3 227 // 6 C atoms; 5 bonds (1 double + 4 single); 12 H implicit 228 let s: *u8 = sys_mmap(16) 229 s[0] = 0x43 // C 230 s[1] = 0x3D // = 231 s[2] = 0x43 // C 232 s[3] = 0x28 // ( 233 s[4] = 0x43 // C 234 s[5] = 0x29 // ) 235 s[6] = 0x43 // C 236 s[7] = 0x28 // ( 237 s[8] = 0x43 // C 238 s[9] = 0x29 // ) 239 s[10] = 0x43 // C 240 let m: *MolGraph = nx_chem_parse_smiles(s, 11) 241 if m.is_valid != 1 { return 151 } 242 if m.n_atoms != 6 { return 152 } 243 let _f: nx_int = nx_chem_compute_implicit_h(m) 244 if nx_chem_mol_heavy_atom_count(m) != 6 { return 153 } 245 let mw: nx_int = nx_chem_mol_weight_q3(m, table) 246 if mw != 84162 { return 154 } 247 return 0 248} 249 250// ================================================================= 251// N -- supplement adulterant: chloride salt of sodium "[Na+].[Cl-]" 252// MW = Na (22.990) + Cl (35.453) = 58.443 253// In Q3: 22990 + 35453 = 58443 254// (charge doesn't add to atomic weight here; no Hs) 255// ================================================================= 256func n_nacl_mw() -> nx_int { 257 let table: *Element = nx_chem_periodic_table_118() 258 let s: *u8 = sys_mmap(16) 259 s[0] = 0x5B; s[1] = 0x4E; s[2] = 0x61; s[3] = 0x2B; s[4] = 0x5D 260 s[5] = 0x2E 261 s[6] = 0x5B; s[7] = 0x43; s[8] = 0x6C; s[9] = 0x2D; s[10] = 0x5D 262 let m: *MolGraph = nx_chem_parse_smiles(s, 11) 263 if m.is_valid != 1 { return 161 } 264 if m.n_atoms != 2 { return 162 } 265 let _f: nx_int = nx_chem_compute_implicit_h(m) 266 let mw: nx_int = nx_chem_mol_weight_q3(m, table) 267 if mw != 58443 { return 163 } 268 return 0 269} 270 271// ================================================================= 272// O -- heavy-metal adulterant: lead acetate Pb(OAc)2 simplified 273// as just "[Pb+2]" alone -- MW = Pb (207.200) = 207200 milli-AMU. 274// ================================================================= 275func o_lead_mw() -> nx_int { 276 let table: *Element = nx_chem_periodic_table_118() 277 let s: *u8 = sys_mmap(8) 278 s[0] = 0x5B; s[1] = 0x50; s[2] = 0x62; s[3] = 0x2B; s[4] = 0x32; s[5] = 0x5D 279 let m: *MolGraph = nx_chem_parse_smiles(s, 6) 280 if m.is_valid != 1 { return 171 } 281 let _f: nx_int = nx_chem_compute_implicit_h(m) 282 let mw: nx_int = nx_chem_mol_weight_q3(m, table) 283 if mw != 207200 { return 172 } 284 return 0 285} 286 287func main() -> nx_exit { 288 println("=== nx_chem_valence -- C2.3a KAT: implicit-H + molecular weight ===" as *u8) 289 290 let ra: nx_int = a_default_valence() 291 if ra != 0 { println("A default_valence FAIL" as *u8); return ra } 292 println("A default_valence PASS Daylight 1988 organic subset valences; non-subset returns -1" as *u8) 293 294 let rb: nx_int = b_methane_h() 295 if rb != 0 { println("B methane_h FAIL" as *u8); return rb } 296 println("B methane_h PASS C with 0 bonds -> 4 H implicit" as *u8) 297 298 let rc: nx_int = c_water_h() 299 if rc != 0 { println("C water_h FAIL" as *u8); return rc } 300 println("C water_h PASS O with 0 bonds -> 2 H implicit" as *u8) 301 302 let rd: nx_int = d_ethanol_h() 303 if rd != 0 { println("D ethanol_h FAIL" as *u8); return rd } 304 println("D ethanol_h PASS CCO -> C(3H) + C(2H) + O(1H) = 6 H total" as *u8) 305 306 let re: nx_int = e_ethene_h() 307 if re != 0 { println("E ethene_h FAIL" as *u8); return re } 308 println("E ethene_h PASS C=C -> 2 H each (4 - 2)" as *u8) 309 310 let rf: nx_int = f_ethyne_h() 311 if rf != 0 { println("F ethyne_h FAIL" as *u8); return rf } 312 println("F ethyne_h PASS C#C -> 1 H each (4 - 3)" as *u8) 313 314 let rg: nx_int = g_benzene_h() 315 if rg != 0 { println("G benzene_h FAIL" as *u8); return rg } 316 println("G benzene_h PASS c1ccccc1 -> 1 H per aromatic C (4 - 2 - 1)" as *u8) 317 318 let rh: nx_int = h_bracket_preserved() 319 if rh != 0 { println("H bracket_preserved FAIL" as *u8); return rh } 320 println("H bracket_preserved PASS [CH4] -> h_count stays 4 (NOT overwritten by inference)" as *u8) 321 322 let ri: nx_int = i_methane_mw() 323 if ri != 0 { println("I methane_mw FAIL" as *u8); return ri } 324 println("I methane_mw PASS C -> MW 16.043 (matches NIST WebBook)" as *u8) 325 326 let rj: nx_int = j_water_mw() 327 if rj != 0 { println("J water_mw FAIL" as *u8); return rj } 328 println("J water_mw PASS O -> MW 18.015 (matches NIST WebBook)" as *u8) 329 330 let rk: nx_int = k_ethanol_mw() 331 if rk != 0 { println("K ethanol_mw FAIL" as *u8); return rk } 332 println("K ethanol_mw PASS CCO -> MW 46.069 (matches NIST WebBook)" as *u8) 333 334 let rl: nx_int = l_benzene_mw() 335 if rl != 0 { println("L benzene_mw FAIL" as *u8); return rl } 336 println("L benzene_mw PASS c1ccccc1 -> MW 78.114 (matches NIST WebBook)" as *u8) 337 338 let rm: nx_int = m_branched_olefin_mw() 339 if rm != 0 { println("M branched_olefin_mw FAIL" as *u8); return rm } 340 println("M branched_olefin_mw PASS C(C(C)C)C(=C)C -> MW 84.162 (2,3-dimethyl-1-butene NIST)" as *u8) 341 342 let rn: nx_int = n_nacl_mw() 343 if rn != 0 { println("N nacl_mw FAIL" as *u8); return rn } 344 println("N nacl_mw PASS [Na+].[Cl-] -> MW 58.443 (sodium chloride)" as *u8) 345 346 let ro: nx_int = o_lead_mw() 347 if ro != 0 { println("O lead_mw FAIL" as *u8); return ro } 348 println("O lead_mw PASS [Pb+2] -> MW 207.200 (supplement-arc heavy-metal adulterant)" as *u8) 349 350 println("" as *u8) 351 println("=== C2.3a substrate milestone PASS ===" as *u8) 352 println(" implicit-H : Daylight 1988 organic-subset rules; aromatic-C handled;" as *u8) 353 println(" aromatic non-C honestly deferred (pyrrole-vs-pyridine = C2.5)" as *u8) 354 println(" molecular weight : Q3 milli-AMU from IUPAC 2021 atomic weights + implicit Hs;" as *u8) 355 println(" verified against NIST WebBook for water, methane, ethanol," as *u8) 356 println(" benzene, 2,3-dimethyl-1-butene, NaCl, Pb" as *u8) 357 println(" honest gaps : aromatic non-C h_count (C2.5); hypervalent N/P/S (bracket-required);" as *u8) 358 println(" exact-mass via monoisotope (use AtomicData.iso_mass_q4); canonical SMILES (C2.3b)" as *u8) 359 println(" next : C2.3b -- canonical SMILES emit + Morgan extended-connectivity" as *u8) 360 return 0 361}