Skip to content

Commit 5c296cf

Browse files
Merge pull request #329 from macrocosm-os/staging
Staging
2 parents 3bdcc27 + a1b663d commit 5c296cf

File tree

6 files changed

+62
-29
lines changed

6 files changed

+62
-29
lines changed

folding/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .protocol import JobSubmissionSynapse
22
from .validators.protein import Protein
33

4-
__version__ = "1.4.3"
4+
__version__ = "1.4.4"
55
version_split = __version__.split(".")
66
__spec_version__ = (10000 * int(version_split[0])) + (100 * int(version_split[1])) + (1 * int(version_split[2]))
77

folding/base/simulation.py

+10-11
Original file line numberDiff line numberDiff line change
@@ -44,36 +44,35 @@ def create_simulation(
4444
This method takes in a seed, state, and checkpoint file path to recreate a simulation object.
4545
Args:
4646
seed (str): The seed for the random number generator.
47-
state (str): The state of the simulation.
48-
cpt_file (str): The path to the checkpoint file.
47+
system_config (dict): A dictionary containing the system configuration settings.
48+
pdb (app.PDBFile): The PDB file used to initialize the simulation
4949
5050
Returns:
51-
app.Simulation: The recreated simulation object.
52-
system_config: The potentially altered system configuration in SimulationConfig format.
51+
Tuple[app.Simulation, SimulationConfig]: A tuple containing the recreated simulation object and the potentially altered system configuration in SystemConfig format.
5352
"""
5453
start_time = time.time()
5554
forcefield = app.ForceField(system_config["ff"], system_config["water"])
56-
logger.warning(f"Creating ff took {time.time() - start_time:.4f} seconds")
55+
logger.debug(f"Creating ff took {time.time() - start_time:.4f} seconds")
5756

5857
modeller = app.Modeller(pdb.topology, pdb.positions)
5958

6059
start_time = time.time()
6160
modeller.deleteWater()
62-
logger.warning(f"Deleting water took {time.time() - start_time:.4f} seconds")
61+
logger.debug(f"Deleting water took {time.time() - start_time:.4f} seconds")
6362

6463
# modeller.addExtraParticles(forcefield)
6564

6665
start_time = time.time()
6766
modeller.addHydrogens(forcefield)
68-
logger.warning(f"Adding hydrogens took {time.time() - start_time:.4f} seconds")
67+
logger.debug(f"Adding hydrogens took {time.time() - start_time:.4f} seconds")
6968

7069
start_time = time.time()
7170
# modeller.addSolvent(
7271
# forcefield,
7372
# padding=system_config.box_padding * unit.nanometer,
7473
# boxShape=system_config.box,
7574
# )
76-
logger.warning(f"Adding solvent took {time.time() - start_time:.4f} seconds")
75+
logger.debug(f"Adding solvent took {time.time() - start_time:.4f} seconds")
7776

7877
# Create the system
7978
start_time = time.time()
@@ -85,7 +84,7 @@ def create_simulation(
8584
nonbondedCutoff = threshold * mm.unit.nanometers
8685
# set the attribute in the config for the pipeline.
8786
system_config["cutoff"] = threshold
88-
logger.warning(
87+
logger.debug(
8988
f"Nonbonded cutoff is greater than half the minimum box dimension. Setting nonbonded cutoff to {threshold} nm"
9089
)
9190
else:
@@ -133,14 +132,14 @@ def create_simulation(
133132
simulation = mm.app.Simulation(
134133
modeller.topology, system, integrator, platform, properties
135134
)
136-
logger.warning(
135+
logger.debug(
137136
f"Creating simulation took {time.time() - start_time:.4f} seconds"
138137
)
139138
# Set initial positions
140139

141140
start_time = time.time()
142141
simulation.context.setPositions(modeller.positions)
143-
logger.warning(f"Setting positions took {time.time() - start_time:.4f} seconds")
142+
logger.debug(f"Setting positions took {time.time() - start_time:.4f} seconds")
144143

145144
# Converting the system config into a Dict[str,str] and ensure all values in system_config are of the correct type
146145
for k, v in system_config.items():

folding/validators/protein.py

+39-15
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1-
import os
2-
import time
3-
import glob
1+
import asyncio
42
import base64
3+
import glob
4+
import os
55
import random
66
import shutil
7-
import asyncio
8-
import datetime
9-
from pathlib import Path
10-
from dataclasses import dataclass
7+
import time
118
from collections import defaultdict
12-
from typing import Dict, List, Literal, Any
9+
from dataclasses import dataclass
10+
from pathlib import Path
11+
from typing import Any, Dict, List, Literal
1312

1413
import numpy as np
1514
import pandas as pd
1615
from openmm import app, unit
1716
from pdbfixer import PDBFixer
1817

19-
from folding.utils.s3_utils import DigitalOceanS3Handler
2018
from folding.base.simulation import OpenMMSimulation
2119
from folding.store import Job
20+
from folding.utils.logger import logger
2221
from folding.utils.opemm_simulation_config import SimulationConfig
2322
from folding.utils.ops import (
2423
OpenMMException,
2524
ValidationError,
26-
write_pkl,
27-
load_pkl,
2825
check_and_download_pdbs,
2926
check_if_directory_exists,
27+
load_pkl,
3028
plot_miner_validator_curves,
29+
write_pkl,
3130
)
32-
33-
from folding.utils.logger import logger
31+
from folding.utils.s3_utils import DigitalOceanS3Handler
3432

3533
ROOT_DIR = Path(__file__).resolve().parents[2]
3634

@@ -537,6 +535,28 @@ def check_masses(self) -> bool:
537535
logger.error(f"Masses for atom {i} do not match. Validator: {v_mass}, Miner: {m_mass}")
538536
return False
539537
return True
538+
539+
def compare_state_to_cpt(self, state_energies: list, checkpoint_energies: list) -> bool:
540+
"""
541+
Check if the state file is the same as the checkpoint file by comparing the median of the first few energy values
542+
in the simulation created by the checkpoint and the state file respectively.
543+
"""
544+
545+
WINDOW = 50
546+
547+
state_energies = np.array(state_energies)
548+
checkpoint_energies = np.array(checkpoint_energies)
549+
550+
state_median = np.median(state_energies[:WINDOW])
551+
checkpoint_median = np.median(checkpoint_energies[:WINDOW])
552+
553+
percent_diff = abs((state_median - checkpoint_median) / checkpoint_median) * 100
554+
555+
if percent_diff > self.epsilon:
556+
return False
557+
return True
558+
559+
540560

541561
def is_run_valid(self):
542562
"""
@@ -575,8 +595,8 @@ def is_run_valid(self):
575595
)
576596
self.simulation.loadState(self.state_xml_path)
577597
state_energies = []
578-
for _ in range(100):
579-
self.simulation.step(100)
598+
for _ in range(steps_to_run // 10):
599+
self.simulation.step(10)
580600
energy = self.simulation.context.getState(getEnergy=True).getPotentialEnergy()._value
581601
state_energies.append(energy)
582602

@@ -622,6 +642,10 @@ def is_run_valid(self):
622642
if not self.check_gradient(check_energies=check_energies):
623643
logger.warning(f"hotkey {self.hotkey_alias} failed cpt-gradient check for {self.pdb_id}, ... Skipping!")
624644
return False, [], [], "cpt-gradient"
645+
646+
if not self.compare_state_to_cpt(state_energies=state_energies, checkpoint_energies=check_energies):
647+
logger.warning(f"hotkey {self.hotkey_alias} failed state-checkpoint comparison for {self.pdb_id}, ... Skipping!")
648+
return False, [], [], "state-checkpoint"
625649

626650
# calculating absolute percent difference per step
627651
percent_diff = abs(((check_energies - miner_energies) / miner_energies) * 100)

folding/validators/reward.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ def check_if_identical(event):
3232
logger.warning(f"Setting {len(flattened_list)} / {len(event['checked_energy'])} uids to 0 reward due to identical submissions.")
3333
for idx in flattened_list:
3434
event["is_valid"][idx] = False
35-
event["reason"][idx] = "Identical submission to another hotkey in the group"
35+
if event["reason"]== "":
36+
event["reason"][idx] = "Identical submission to another hotkey in the group"
3637

3738
return event
3839

@@ -58,6 +59,7 @@ def get_energies(protein: Protein, responses: List[JobSubmissionSynapse], uids:
5859
event["ns_computed"] = [0] * len(uids)
5960
event["reason"] = [""] * len(uids)
6061
event["best_cpt"] = [""] * len(uids)
62+
event["seed"] = []
6163

6264
energies = np.zeros(len(uids))
6365

@@ -71,6 +73,7 @@ def get_energies(protein: Protein, responses: List[JobSubmissionSynapse], uids:
7173
state=resp.miner_state,
7274
seed=resp.miner_seed,
7375
)
76+
event['seed'].append(resp.miner_seed)
7477
event["process_md_output_time"][i] = time.time() - start_time
7578
event["best_cpt"][i] = protein.checkpoint_path if hasattr(protein, "checkpoint_path") else ""
7679

neurons/validator.py

+7
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@ async def update_job(self, job: Job):
290290
energies = torch.Tensor(job.event["energies"])
291291
rewards = torch.zeros(len(energies)) # one-hot per update step
292292

293+
# If there is an exploit on the cpt file detected via the state-checkpoint reason, we will reset the score to 0.
294+
logger.info(f"event information: {job.event['reason']}, {job.event['uids']}")
295+
for uid, reason in zip(job.event["uids"], job.event["reason"]):
296+
if reason == "state-checkpoint":
297+
logger.warning(f"Setting uid {uid} score to zero, State-checkpoint check failed.")
298+
self.scores[uid] = 0
299+
293300
best_index = np.argmin(energies)
294301
best_loss = energies[best_index].item() # item because it's a torch.tensor
295302
best_hotkey = serving_hotkeys[best_index]

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "folding"
3-
version = "1.4.3"
3+
version = "1.4.4"
44
description = "Macrocosmos Subnet 25: Folding"
55
authors = ["Brian McCrindle <[email protected]>", "Sergio Champoux <[email protected]>", "Szymon Fonau <[email protected]>"]
66

0 commit comments

Comments
 (0)