Queimo commited on
Commit
c2c8b08
·
verified ·
1 Parent(s): 31dee3b

Pin exact evaluated starting checkpoint, objective equivalence tests, source and joint training protocol

Browse files
preparation_complete.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "completed": true,
3
+ "time": 1793210639.778262,
4
+ "initialization": {
5
+ "initial_checkpoint_repository": "Queimo/rem3di-pcqm-epoch2-cosmo",
6
+ "initial_checkpoint_path": "epoch_10",
7
+ "initial_checkpoint_revision": "9266a41fbe3b36afe0615e0c3ba232ca74bdeef7",
8
+ "initial_hashes": {
9
+ "encoder.pth": "97e616a16e8317717784929495ba3a030bfcd821038be6e05324624b17c409c0",
10
+ "atomic_preprocessor.pth": "9683883267a902773b4f6777eedb366befd016f4f7083bed62a1bd35ae113e3d",
11
+ "geometric_preprocessor.pth": "4da0843646d06fcd2ff5b13e1fd4fb4b22c8c0456d0507c59df01e665c4d6d7b",
12
+ "cosmo_head.pth": "1a43270e60f00c9428099e0e37ab62f77a2c219ea611d4f05194bc1abf69b0a7"
13
+ },
14
+ "frozen_foundation_sha256": "fab8b8713c832f31a2a853aaa22fd638be8a369cbf5095e6b3e982a18d10e93a",
15
+ "verified_against_bigsoldb_protocol": true,
16
+ "bigsoldb_protocol_sha256": "dcd893dc297eef5d50b225367f295cdded69a6a9cd9b53c8f8f63c241f7e371b",
17
+ "denoising_decoder": {
18
+ "repository": "Queimo/rem3di-pcqm500k-corrected",
19
+ "revision": "2f5c3b40b36259a13adc8412689254054bb93288",
20
+ "path": "runs/corrected_20261027/evaluation/paper_audit/author_pcqm500k_5ep/epoch_02/decoder.pth",
21
+ "sha256": "bffdd09bae9bcb2d863165546dea090a10be06d8a52b89e4f651e0f1c80ae7c4"
22
+ },
23
+ "explanation": "All encoder, atomic/geometric preprocessor and COSMO head weights come from the exact evaluated COSMO10 checkpoint. Only its omitted denoising decoder is restored from PCQM epoch2."
24
+ },
25
+ "data": {
26
+ "repository": "Queimo/combisolv-qm-rem3di-multitask",
27
+ "revision": "de87254deae3bbdc031e3c2185df7c3ad63945ac",
28
+ "verified": true,
29
+ "files": 23
30
+ }
31
+ }
prepared_data_hub.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "repository": "Queimo/combisolv-qm-rem3di-multitask",
3
+ "revision": "de87254deae3bbdc031e3c2185df7c3ad63945ac",
4
+ "verified": true,
5
+ "files": 23
6
+ }
smoke_validation.json ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "passed": true,
3
+ "all_head_gradients": {
4
+ "encoder.pth": true,
5
+ "atomic_preprocessor.pth": true,
6
+ "geometric_preprocessor.pth": true,
7
+ "cosmo_head.pth": true,
8
+ "denoising_decoder.pth": true,
9
+ "combisolv_head.pth": true
10
+ },
11
+ "encoder_updated": true,
12
+ "task_weights_updated": true,
13
+ "optimizer_and_rng_resume_next_step_exact": true,
14
+ "normalization_buffers_unchanged": true,
15
+ "initial_bigsoldb_embedding_match": {
16
+ "molecules": 1334,
17
+ "max_absolute": 4.184246063232422e-05,
18
+ "mean_absolute": 1.621990719513633e-07
19
+ },
20
+ "real_batch_loss": {
21
+ "denoising": 0.0400693416595459,
22
+ "denoising_raw": 43.59544372558594,
23
+ "cosmo": 0.020166773349046707,
24
+ "cov_cosmo": 1.5453895684913732e-05,
25
+ "descriptor_std_cosmo": 0.11399243026971817,
26
+ "cosmo_sigma": 0.05140712857246399,
27
+ "cosmo_area": 0.0041954368352890015,
28
+ "cosmo_volume": 0.004897752311080694,
29
+ "combisolv": 1.3595600128173828,
30
+ "cov_combisolv": 0.0001902484946185723,
31
+ "descriptor_std_combisolv": 0.18535533547401428,
32
+ "total": 0.4212467670440674,
33
+ "gradient_norm": 2.3515171745204047,
34
+ "combisolv_rmse_kcal_mol": 5.220246867294003,
35
+ "log_variance_rem3di_denoising": -0.5009999871253967,
36
+ "precision_rem3di_denoising": 0.8251854181289673,
37
+ "log_variance_cosmo": -0.5009999871253967,
38
+ "precision_cosmo": 0.8251854181289673,
39
+ "log_variance_combisolv": -0.49900001287460327,
40
+ "precision_combisolv": 0.8235366940498352
41
+ },
42
+ "seconds_per_joint_step": 2.9659399400006805,
43
+ "steady_seconds_per_joint_step": 0.5084904360010114,
44
+ "smoke_cosmo_molecules": 64,
45
+ "smoke_qm_pairs": 256,
46
+ "peak_gpu_GB": 13.077587456,
47
+ "config": {
48
+ "epochs": 5,
49
+ "seed": 42026,
50
+ "learning_rate_backbone": 1e-05,
51
+ "learning_rate_cosmo": 1e-05,
52
+ "deterministic_training": true,
53
+ "learning_rate_denoiser": 1e-05,
54
+ "learning_rate_combisolv": 0.0003,
55
+ "learning_rate_uncertainty": 0.001,
56
+ "weight_decay": 0.0001,
57
+ "gradient_clip": 1.0,
58
+ "cosmo_batch_size": 96,
59
+ "cosmo_atom_cap": 4000,
60
+ "qm_pair_batch_size": 256,
61
+ "qm_graph_batch_size": 64,
62
+ "qm_atom_cap": 3600,
63
+ "noise_sigma": 0.3,
64
+ "denoising_reduction": "Original atom_denoising_loss / 1088 embedding channels; raw loss also logged",
65
+ "task_order": [
66
+ "rem3di_denoising",
67
+ "cosmo",
68
+ "combisolv"
69
+ ],
70
+ "uncertainty_init": -0.5,
71
+ "uncertainty_objective": "sum(L_t/(2*exp(s_t)) + s_t/2); s is learned log variance; no weight normalization in objective",
72
+ "decorrelation_weight": 8.0,
73
+ "decorrelation": "Monroe off_diag_cov_loss: sum(offdiag(sample covariance)^2)/(D*(D-1)); average COSMO and unique CombiSolv molecular batches; outside UW",
74
+ "monroe_revision": "de075eabe98e4adbbedbc86ca9e877f4bfd9b629",
75
+ "temperature_input": "Temperature_K/298",
76
+ "qm_head": "concat(solute640, solvent640, T/298) -> Linear1281x256 -> SiLU -> Linear256x1",
77
+ "epoch_definition": "Maximum of full COSMO and full CombiSolv training-loader lengths. Cycle shorter loader. Every epoch covers every retained training row from each corpus.",
78
+ "initial_checkpoint": "Exact PCQMep2COSMOep10 used in existing BigSolDB evaluation",
79
+ "denoiser_initialization": "PCQM epoch2 decoder only; encoder/preprocessors/COSMO head all from COSMO10",
80
+ "frozen": "MACE-POLAR foundation only; full float32 raw features cached; learned atomic/geometric preprocessors trainable",
81
+ "evaluation": "Identical BigSolDB2.0 author test IDs, shared baseline geometries, concatenated solute+solvent+T, Kumo Large four estimators seed42 full75731-row context and all1281 columns; evaluate every epoch without test-based selection"
82
+ }
83
+ }
source/multitask_data.py CHANGED
@@ -194,7 +194,8 @@ def prepare_tables():
194
 
195
  def prepare_geometries():
196
  path=DATA/'combisolv_geometries.parquet'
197
- if path.exists():return
 
198
  molecules=pd.read_parquet(DATA/'combisolv_molecules.parquet')
199
  status('generating_combisolv_3d',total=len(molecules))
200
  result=[]
@@ -206,8 +207,18 @@ def prepare_geometries():
206
  assert not failures,failures[:10]
207
  assert [r['smiles'] for r in result]==molecules.smiles.tolist()
208
  pd.DataFrame(result).to_parquet(path,index=False,compression='zstd')
209
- write(DATA/'geometry_manifest.json',dict(rows=len(result),sha256=sha(path),seed=42026,
210
- algorithm='RDKit ETKDGv3 explicit H; deterministic retries with random coordinates; MMFF94s or UFF optimization',
 
 
 
 
 
 
 
 
 
 
211
  no_2d_fallback=True,missing=0))
212
 
213
 
@@ -252,7 +263,10 @@ def cache_mace(name,frame):
252
  mace=build_author_mace_runtime().cuda().eval().requires_grad_(False)
253
  captures=[]
254
  hook=mace.preprocessor.atomic_preprocessor.register_forward_pre_hook(lambda m,a:captures.append(a[0].detach()))
255
- started=time.time();plan=list(batches(np.arange(len(frame)),lengths,max_size=64,atom_cap=3600))
 
 
 
256
  status('caching_frozen_mace',corpus=name,total=len(frame),atoms=int(ptr[-1]),bytes=int(ptr[-1])*4096*4,batches=len(plan))
257
  with torch.inference_mode():
258
  for bi,ids in enumerate(plan,1):
@@ -276,9 +290,55 @@ def cache_mace(name,frame):
276
  def publish_data():
277
  marker=OUT/'prepared_data_hub.json'
278
  if marker.exists():return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  files=[p for p in DATA.iterdir() if p.is_file()]
280
  commit=HfApi().create_commit(repo_id=DATA_REPO,repo_type='dataset',
281
- operations=[CommitOperationAdd(path_in_repo='prepared/'+p.name,path_or_fileobj=str(p)) for p in files],
 
282
  commit_message='Add reproducible joint splits, all geometries, label transforms and benchmark exclusions')
283
  remote={p.rfilename:p for p in HfApi().dataset_info(DATA_REPO,revision=commit.oid,files_metadata=True).siblings}
284
  for p in files:
@@ -300,14 +360,11 @@ def main():
300
  graphs=pd.read_parquet(WORK/'bigsoldb_evaluation/features/shared_graphs.parquet')
301
  vocab=NODE_FEAT_LIST_ONE_HOT['atomic_number'];col=list(NODE_FEAT_LIST_ONE_HOT).index('atomic_number')
302
  ptable=Chem.GetPeriodicTable()
303
- graph_atoms=pd.DataFrame(dict(smiles=graphs.smiles,positions=graphs.pos_rdkit,
304
  atomic_numbers=[[ptable.GetAtomicNumber(vocab[int(i)]) for i in np.stack(codes)[:,col]] for codes in graphs.node_codes],
305
  charge=[int(round(float(np.stack(v)[:,5].sum()))) for v in graphs.node_float]))
306
  graph_atoms.to_parquet(DATA/'bigsoldb_fixed_geometries.parquet',index=False,compression='zstd')
307
  cache_mace('bigsoldb',graph_atoms)
308
- manifest=json.loads((DATA/'manifest.json').read_text())
309
- manifest['files']={p.name:sha(p) for p in DATA.iterdir() if p.is_file() and p.name!='manifest.json'}
310
- write(DATA/'manifest.json',manifest)
311
  publish_data()
312
  write(OUT/'preparation_complete.json',dict(completed=True,time=time.time(),initialization=json.loads((OUT/'initialization.json').read_text()),data=json.loads((OUT/'prepared_data_hub.json').read_text())))
313
  status('preparation_complete')
 
194
 
195
  def prepare_geometries():
196
  path=DATA/'combisolv_geometries.parquet'
197
+ if path.exists():
198
+ record_geometry_manifest(path);return
199
  molecules=pd.read_parquet(DATA/'combisolv_molecules.parquet')
200
  status('generating_combisolv_3d',total=len(molecules))
201
  result=[]
 
207
  assert not failures,failures[:10]
208
  assert [r['smiles'] for r in result]==molecules.smiles.tolist()
209
  pd.DataFrame(result).to_parquet(path,index=False,compression='zstd')
210
+ record_geometry_manifest(path)
211
+
212
+
213
+ def record_geometry_manifest(path):
214
+ from rdkit import rdBase
215
+ frame=pd.read_parquet(path,columns=['smiles','geometry_method'])
216
+ molecules=pd.read_parquet(DATA/'combisolv_molecules.parquet',columns=['smiles'])
217
+ assert frame.smiles.tolist()==molecules.smiles.tolist()
218
+ write(DATA/'geometry_manifest.json',dict(rows=len(frame),sha256=sha(path),seed=42026,
219
+ rdkit_version=rdBase.rdkitVersion,
220
+ algorithm='RDKit ETKDGv3 explicit H, deterministic random-coordinate retries, generic 3D distance geometry fallback; MMFF94s or UFF where parameterized',
221
+ methods={str(k):int(v) for k,v in frame.geometry_method.value_counts().items()},
222
  no_2d_fallback=True,missing=0))
223
 
224
 
 
263
  mace=build_author_mace_runtime().cuda().eval().requires_grad_(False)
264
  captures=[]
265
  hook=mace.preprocessor.atomic_preprocessor.register_forward_pre_hook(lambda m,a:captures.append(a[0].detach()))
266
+ started=time.time()
267
+ if name=='bigsoldb':
268
+ plan=list(common.batch_indices(np.arange(len(frame)),lengths,max_size=24,atom_cap=2400))
269
+ else:plan=list(batches(np.arange(len(frame)),lengths,max_size=64,atom_cap=3600))
270
  status('caching_frozen_mace',corpus=name,total=len(frame),atoms=int(ptr[-1]),bytes=int(ptr[-1])*4096*4,batches=len(plan))
271
  with torch.inference_mode():
272
  for bi,ids in enumerate(plan,1):
 
290
  def publish_data():
291
  marker=OUT/'prepared_data_hub.json'
292
  if marker.exists():return
293
+ pairs=pd.read_parquet(DATA/'combisolv_pairs.parquet')
294
+ for split,frame in pairs.groupby('split',sort=False):
295
+ frame.to_parquet(DATA/f'combisolv_{split}.parquet',index=False,compression='zstd')
296
+ manifest=json.loads((DATA/'manifest.json').read_text())
297
+ manifest['files']={p.name:sha(p) for p in DATA.iterdir() if p.is_file() and p.name!='manifest.json'}
298
+ write(DATA/'manifest.json',manifest)
299
+ header='''---
300
+ license: cc-by-4.0
301
+ task_categories:
302
+ - tabular-regression
303
+ configs:
304
+ - config_name: raw_combisolv_qm
305
+ default: true
306
+ data_files:
307
+ - split: records
308
+ path: raw/combisolv_qm.parquet
309
+ - config_name: prepared_pairs
310
+ data_files:
311
+ - split: train
312
+ path: prepared/combisolv_train.parquet
313
+ - split: validation
314
+ path: prepared/combisolv_validation.parquet
315
+ - split: benchmark_overlap
316
+ path: prepared/combisolv_benchmark_overlap.parquet
317
+ - split: auxiliary_holdout
318
+ path: prepared/combisolv_auxiliary_holdout.parquet
319
+ - config_name: combisolv_geometries
320
+ data_files:
321
+ - split: molecules
322
+ path: prepared/combisolv_geometries.parquet
323
+ - config_name: cosmo_pool
324
+ data_files:
325
+ - split: records
326
+ path: prepared/cosmo-*.parquet
327
+ ---
328
+ '''
329
+ card=header+'\n# CombiSolv-QM and COSMO multi-task continuation data\n\n'
330
+ card+='The unchanged original one-million-row CombiSolv-QM table is preserved under `raw/`, together with source archive/CSV/Parquet checksums. The target is signed solvation Gibbs free energy at 298 K, in kcal/mol, using 1 mol/L reference concentrations. It is distinct from experimental solubility.\n\n'
331
+ card+=f"Prepared CombiSolv memberships: `{json.dumps(manifest['combisolv_splits'],sort_keys=True)}`. All {manifest['unique_qm_molecules']:,} unique structures have explicit-H coordinates; supplied InChI defines identity, and original SMILES/InChI mappings are retained. Solute/solvent indices refer to row order in the geometry and molecule tables.\n\n"
332
+ card+='Geometries use seeded RDKit ETKDGv3, random-coordinate retries and generic 3D distance geometry for strained/unusual structures, followed by MMFF94s or UFF when parameterized. There is no 2D fallback. Geometry methods, software version, coverage and checksums are recorded.\n\n'
333
+ card+=f"The COSMO pool retains the source training/validation records and adds an explicit `joint_split` membership: `{json.dumps(manifest['cosmo_splits'],sort_keys=True)}`. Filter that column for training. The complete original COSMO dataset and its existing ESOL/FreeSolv quarantine remain available in [Queimo/pubchemqc-cosmo-pm6](https://huggingface.co/datasets/{COSMO_REPO}) at revision `{COSMO_REV}`.\n\n"
334
+ card+='BigSolDB2.0 held-out solute connectivity groups are excluded from all new objectives and both CombiSolv input roles. CombiSolv validation solutes are excluded across objectives. Existing COSMO validation groups are excluded from CombiSolv training. Raw rows remain available with explicit quarantine membership; earlier exposure in the starting checkpoint cannot be undone. ESOL/FreeSolv quarantine is preserved for COSMO, but is not claimed for CombiSolv.\n\n'
335
+ card+='CombiSolv target mean/std is fitted on its retained training rows only; signed energies are not log transformed. The existing COSMO log1p/ln transform and normalization are preserved so the initialized output head retains its meaning.\n\n'
336
+ card+='[SolProp v1.2 source](https://zenodo.org/records/5970538) · [Vermeire and Green (2021)](https://doi.org/10.1016/j.cej.2021.129307) · [Vermeire, Chung and Green (2022)](https://doi.org/10.1021/jacs.2c01768). Please cite the original data authors.\n'
337
+ card_path=OUT/'dataset_README.md';card_path.write_text(card)
338
  files=[p for p in DATA.iterdir() if p.is_file()]
339
  commit=HfApi().create_commit(repo_id=DATA_REPO,repo_type='dataset',
340
+ operations=[CommitOperationAdd(path_in_repo='prepared/'+p.name,path_or_fileobj=str(p)) for p in files]+[
341
+ CommitOperationAdd(path_in_repo='README.md',path_or_fileobj=str(card_path))],
342
  commit_message='Add reproducible joint splits, all geometries, label transforms and benchmark exclusions')
343
  remote={p.rfilename:p for p in HfApi().dataset_info(DATA_REPO,revision=commit.oid,files_metadata=True).siblings}
344
  for p in files:
 
360
  graphs=pd.read_parquet(WORK/'bigsoldb_evaluation/features/shared_graphs.parquet')
361
  vocab=NODE_FEAT_LIST_ONE_HOT['atomic_number'];col=list(NODE_FEAT_LIST_ONE_HOT).index('atomic_number')
362
  ptable=Chem.GetPeriodicTable()
363
+ graph_atoms=pd.DataFrame(dict(smiles=graphs.smiles,positions=[np.stack(p).astype(np.float32).tolist() for p in graphs.pos_rdkit],
364
  atomic_numbers=[[ptable.GetAtomicNumber(vocab[int(i)]) for i in np.stack(codes)[:,col]] for codes in graphs.node_codes],
365
  charge=[int(round(float(np.stack(v)[:,5].sum()))) for v in graphs.node_float]))
366
  graph_atoms.to_parquet(DATA/'bigsoldb_fixed_geometries.parquet',index=False,compression='zstd')
367
  cache_mace('bigsoldb',graph_atoms)
 
 
 
368
  publish_data()
369
  write(OUT/'preparation_complete.json',dict(completed=True,time=time.time(),initialization=json.loads((OUT/'initialization.json').read_text()),data=json.loads((OUT/'prepared_data_hub.json').read_text())))
370
  status('preparation_complete')
source/multitask_evaluate.py CHANGED
@@ -21,7 +21,7 @@ def embeddings(epoch):
21
  model=MultiTask(checkpoint);model.eval().requires_grad_(False)
22
  cache=RawCache('bigsoldb');X=np.empty((len(geometry),640),dtype=np.float32)
23
  with torch.inference_mode():
24
- for ids in batches(np.arange(len(geometry)),cache.lengths,max_size=24,atom_cap=2400):
25
  _,_,z=model.encode(cache.sample(ids));X[ids]=z.float().cpu().numpy()
26
  assert np.isfinite(X).all()
27
  np.savez_compressed(path,X=X,smiles=geometry.smiles.to_numpy(dtype=str),checkpoint_sha256=json.dumps(hashes))
 
21
  model=MultiTask(checkpoint);model.eval().requires_grad_(False)
22
  cache=RawCache('bigsoldb');X=np.empty((len(geometry),640),dtype=np.float32)
23
  with torch.inference_mode():
24
+ for ids in common.batch_indices(np.arange(len(geometry)),cache.lengths,max_size=24,atom_cap=2400):
25
  _,_,z=model.encode(cache.sample(ids));X[ids]=z.float().cpu().numpy()
26
  assert np.isfinite(X).all()
27
  np.savez_compressed(path,X=X,smiles=geometry.smiles.to_numpy(dtype=str),checkpoint_sha256=json.dumps(hashes))
source/multitask_train.py CHANGED
@@ -174,13 +174,15 @@ def smoke(data):
174
  else:check_monroe_losses()
175
  model=MultiTask();optimizer=optimizer_for(model)
176
  scheduler=torch.optim.lr_scheduler.LambdaLR(optimizer,lambda e:1.)
177
- model.eval();baseline=RawCache('bigsoldb');ids=list(range(24))
178
  with torch.no_grad():
179
- _,_,ours=model.encode(baseline.sample(ids))
180
- ref=np.load(WORK/'bigsoldb_evaluation/features/cosmo_epoch10.npz')['X'][ids]
181
- difference=np.abs(ours.cpu().numpy()-ref)
182
- assert np.allclose(ours.cpu().numpy(),ref,atol=2e-4,rtol=2e-4),(float(difference.max()),float(difference.mean()))
183
- initial_difference=dict(max_absolute=float(difference.max()),mean_absolute=float(difference.mean()))
 
 
184
  frozen={n:b.cpu().clone() for n,b in model.pre.atomic_preprocessor.invariant_normalization.named_buffers()}
185
  cp,qp,_=data.plans(1);ci=cp[10];qi=qp[10]
186
  initial=next(model.encoder.parameters()).detach().clone()
@@ -195,7 +197,9 @@ def smoke(data):
195
  expected_state={n:v.detach().cpu().clone() for n,v in model.state_dict().items()}
196
  model.load_parts(temp);state=torch.load(temp/'training_state.pt',map_location='cpu',weights_only=False)
197
  optimizer.load_state_dict(state['optimizer']);scheduler.load_state_dict(state['scheduler']);restore_rng(state)
 
198
  actual=update(model,optimizer,data,ci,qi)
 
199
  assert all(np.isclose(actual[k],expected[k],atol=1e-7,rtol=1e-7) for k in expected),(expected,actual)
200
  assert all(torch.equal(v.detach().cpu(),expected_state[n]) for n,v in model.state_dict().items()),'Resume changed next-step parameters'
201
  for n,b in model.pre.atomic_preprocessor.invariant_normalization.named_buffers():assert torch.equal(b.cpu(),frozen[n])
@@ -203,6 +207,7 @@ def smoke(data):
203
  result=dict(passed=True,all_head_gradients=gradients,encoder_updated=True,task_weights_updated=True,
204
  optimizer_and_rng_resume_next_step_exact=True,normalization_buffers_unchanged=True,
205
  initial_bigsoldb_embedding_match=initial_difference,real_batch_loss=first,seconds_per_joint_step=duration,
 
206
  smoke_cosmo_molecules=len(ci),smoke_qm_pairs=len(qi),peak_gpu_GB=peak,config=CONFIG)
207
  write(OUT/'smoke_validation.json',result);shutil.rmtree(temp)
208
  status('smoke_passed',seconds_per_joint_step=duration,peak_gpu_GB=peak,initial_embedding_difference=initial_difference)
@@ -219,7 +224,8 @@ def main():
219
  torch.manual_seed(CONFIG['seed']);torch.cuda.manual_seed_all(CONFIG['seed']);np.random.seed(CONFIG['seed']);random.seed(CONFIG['seed'])
220
  data=TrainingData()
221
  if args.smoke:smoke(data);return
222
- assert json.loads((OUT/'smoke_validation.json').read_text())['passed']
 
223
  checkpoints=sorted((OUT/'checkpoints').glob('epoch_[0-9][0-9]')) if (OUT/'checkpoints').exists() else []
224
  model=MultiTask(checkpoints[-1] if checkpoints else None);optimizer=optimizer_for(model)
225
  scheduler=torch.optim.lr_scheduler.LambdaLR(optimizer,lambda e:.1+.9*.5*(1+math.cos(math.pi*e/CONFIG['epochs'])))
 
174
  else:check_monroe_losses()
175
  model=MultiTask();optimizer=optimizer_for(model)
176
  scheduler=torch.optim.lr_scheduler.LambdaLR(optimizer,lambda e:1.)
177
+ model.eval();baseline=RawCache('bigsoldb')
178
  with torch.no_grad():
179
+ ref=np.load(WORK/'bigsoldb_evaluation/features/cosmo_epoch10.npz')['X']
180
+ ours=np.empty_like(ref)
181
+ for ids in common.batch_indices(np.arange(len(ref)),baseline.lengths,max_size=24,atom_cap=2400):
182
+ _,_,z=model.encode(baseline.sample(ids));ours[ids]=z.cpu().numpy()
183
+ difference=np.abs(ours-ref)
184
+ assert np.allclose(ours,ref,atol=2e-4,rtol=2e-4),(float(difference.max()),float(difference.mean()))
185
+ initial_difference=dict(molecules=len(ref),max_absolute=float(difference.max()),mean_absolute=float(difference.mean()))
186
  frozen={n:b.cpu().clone() for n,b in model.pre.atomic_preprocessor.invariant_normalization.named_buffers()}
187
  cp,qp,_=data.plans(1);ci=cp[10];qi=qp[10]
188
  initial=next(model.encoder.parameters()).detach().clone()
 
197
  expected_state={n:v.detach().cpu().clone() for n,v in model.state_dict().items()}
198
  model.load_parts(temp);state=torch.load(temp/'training_state.pt',map_location='cpu',weights_only=False)
199
  optimizer.load_state_dict(state['optimizer']);scheduler.load_state_dict(state['scheduler']);restore_rng(state)
200
+ replay_started=time.perf_counter()
201
  actual=update(model,optimizer,data,ci,qi)
202
+ steady_duration=time.perf_counter()-replay_started
203
  assert all(np.isclose(actual[k],expected[k],atol=1e-7,rtol=1e-7) for k in expected),(expected,actual)
204
  assert all(torch.equal(v.detach().cpu(),expected_state[n]) for n,v in model.state_dict().items()),'Resume changed next-step parameters'
205
  for n,b in model.pre.atomic_preprocessor.invariant_normalization.named_buffers():assert torch.equal(b.cpu(),frozen[n])
 
207
  result=dict(passed=True,all_head_gradients=gradients,encoder_updated=True,task_weights_updated=True,
208
  optimizer_and_rng_resume_next_step_exact=True,normalization_buffers_unchanged=True,
209
  initial_bigsoldb_embedding_match=initial_difference,real_batch_loss=first,seconds_per_joint_step=duration,
210
+ steady_seconds_per_joint_step=steady_duration,
211
  smoke_cosmo_molecules=len(ci),smoke_qm_pairs=len(qi),peak_gpu_GB=peak,config=CONFIG)
212
  write(OUT/'smoke_validation.json',result);shutil.rmtree(temp)
213
  status('smoke_passed',seconds_per_joint_step=duration,peak_gpu_GB=peak,initial_embedding_difference=initial_difference)
 
224
  torch.manual_seed(CONFIG['seed']);torch.cuda.manual_seed_all(CONFIG['seed']);np.random.seed(CONFIG['seed']);random.seed(CONFIG['seed'])
225
  data=TrainingData()
226
  if args.smoke:smoke(data);return
227
+ smoke_result=json.loads((OUT/'smoke_validation.json').read_text())
228
+ assert smoke_result['passed'] and smoke_result['config']==CONFIG
229
  checkpoints=sorted((OUT/'checkpoints').glob('epoch_[0-9][0-9]')) if (OUT/'checkpoints').exists() else []
230
  model=MultiTask(checkpoints[-1] if checkpoints else None);optimizer=optimizer_for(model)
231
  scheduler=torch.optim.lr_scheduler.LambdaLR(optimizer,lambda e:.1+.9*.5*(1+math.cos(math.pi*e/CONFIG['epochs'])))
source_hashes.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
- "multitask_data.py": "9e6a6926c427bba8b93f2225042a53e9d4a5cf0ee9bd2ee603e5d8acb284683c",
3
  "multitask_model.py": "1ffe262b355bc911e3ea35c9e6844d40ab5b6dafb5a11a90eba6565361083b05",
4
- "multitask_train.py": "9cfe0b327476e8cc4b076a30d211b428f77a51fd21139785174301b2503f43f9",
5
- "multitask_evaluate.py": "ab5352483e7d927755a8fed2fec150400ac71de804cbe93b1b34b039c1186ba1",
6
  "multitask_driver.py": "ade6f9106b3be1d488e39db711119d43e07457ebe9425196b460c87440205ad7",
7
  "eval_common.py": "e694f4e3dc4d62f2567b55a3e3b4da9855686c2977ba3b182094c86b1fb8eaa5",
8
  "train_cosmo.py": "a2ce3a8bfcd7dd8eafb108151cf7ccce78b3e576b07df83c2b436fde931dd866",
 
1
  {
2
+ "multitask_data.py": "7ba9accbbd9a0d9c23720946cb0ebd2e410853aa7951dc58c9198a4f2586c616",
3
  "multitask_model.py": "1ffe262b355bc911e3ea35c9e6844d40ab5b6dafb5a11a90eba6565361083b05",
4
+ "multitask_train.py": "00983d0060b26f148eb67b021899d9a73d84231aaf821406b2a4d9f1c36d568d",
5
+ "multitask_evaluate.py": "ea5132b5bd4efe97924009bbe94f6d1f7ff4504f993b37689c0ce7ef93dea9c6",
6
  "multitask_driver.py": "ade6f9106b3be1d488e39db711119d43e07457ebe9425196b460c87440205ad7",
7
  "eval_common.py": "e694f4e3dc4d62f2567b55a3e3b4da9855686c2977ba3b182094c86b1fb8eaa5",
8
  "train_cosmo.py": "a2ce3a8bfcd7dd8eafb108151cf7ccce78b3e576b07df83c2b436fde931dd866",