diff --git a/AUTHORS.md b/AUTHORS.md index 9c1f556..545e8cf 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,6 +1,12 @@ # Authors -All contributing authors to statocaster are listed below. +All contributing authors to stratocaster are listed below. + +2026 +- Alyssa Travitz + +2025 +- David L. Dotson 2024 - Ian Kenney diff --git a/README.md b/README.md index b30fddc..25b36c5 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,39 @@ Find the documentation for stratocaster on [Read the Docs](https://stratocaster. ## Installation +Install stratocaster via conda-forge: + +```bash +conda install -c conda-forge stratocaster +``` + +Or install the latest development version via pip: + +```bash +pip install git+https://github.com/OpenFreeEnergy/stratocaster.git@main +``` + ## Usage +Import a strategy and call `propose()` with your `AlchemicalNetwork` and existing results: + +```python +from stratocaster.strategies import RadialGrowthStrategy + +strategy = RadialGrowthStrategy(RadialGrowthStrategy.default_settings()) + +previous_results = {} +result = strategy.propose(alchemical_network, previous_results) +normalized_weights = result.resolve() +``` + +For more details, visit the [stratocaster documentation](https://stratocaster.readthedocs.io/en/latest/). + ## Contributing +Please report bugs or request features via the [issue tracker](https://github.com/OpenFreeEnergy/stratocaster/issues). +Pull requests are encouraged for bug fixes, new strategies, and documentation improvements. + ## License This project is released under the [MIT license](./LICENSE). diff --git a/docs/code/iterative.py b/docs/code/iterative.py index 4aac9d4..6a7c9c5 100644 --- a/docs/code/iterative.py +++ b/docs/code/iterative.py @@ -1,4 +1,3 @@ -import random from stratocaster.strategies import ConnectivityStrategy settings = ConnectivityStrategy.default_settings() @@ -7,10 +6,10 @@ previous_results = {} # a loop that will eventually end while True: - strategy_result = strategy.propose() + strategy_result = strategy.propose(alchem_network, previous_results) normalized_weights = strategy_result.resolve() # check if there are any weights - if not any(weights.values()): + if not any(normalized_weights.values()): break # Pick a transformation from the weights, run it, update previous_results. diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 212d0f1..da359d6 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -15,7 +15,7 @@ Verify the installation was successful in a Python interpreter .. code:: python - import statocaster + import stratocaster print(stratocaster.__version__) 3. Quick-start example diff --git a/docs/installation.rst b/docs/installation.rst index 24bfdca..91dbec9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,7 +3,7 @@ Installation ============ -The only requirement for installing statocaster is a working installation of gufe with a version 1.2.0 or higher. +The only requirement for installing stratocaster is a working installation of gufe with a version 1.2.0 or higher. For general use, we recommend installing from the conda-forge channel, which will also install gufe in the process. conda-forge channel @@ -13,7 +13,7 @@ If you use conda, stratocaster can be installed through the conda-forge channel. .. code:: - conda create -n statocaster-env + conda create -n stratocaster-env conda activate stratocaster-env conda install -c conda-forge stratocaster diff --git a/src/stratocaster/strategies/connectivity.py b/src/stratocaster/strategies/connectivity.py index d6a310b..b12817a 100644 --- a/src/stratocaster/strategies/connectivity.py +++ b/src/stratocaster/strategies/connectivity.py @@ -10,8 +10,6 @@ field_validator, ) -import pydantic - class ConnectivityStrategySettings(StrategySettings): """Specific settings required for the ConnectivityStrategy.""" @@ -25,7 +23,7 @@ class ConnectivityStrategySettings(StrategySettings): ) max_runs: int | None = Field( default=None, - description="the upper limit of protocol DAG results needed before a transformation is no longer weighed", + description="the upper limit of protocol DAG results needed before a transformation is no longer weighted", ) @field_validator("cutoff", mode="before") @@ -63,8 +61,9 @@ class ConnectivityStrategy(Strategy): """A Strategy that suggests Transformations which lead to nodes with higher degrees of connectivity. - The Transformation for a given Transformation is calculated as the - average number of connections both of its end states has. + The weight for a given Transformation is calculated as the average + number of connections both of its end states has. + """ _settings_cls = ConnectivityStrategySettings @@ -128,13 +127,13 @@ def _propose( match (protocol_results.get(transformation_key)): case None: - transformation_n_protcol_dag_results = 0 + transformation_n_protocol_dag_results = 0 case pr: assert isinstance(pr, ProtocolResult) - transformation_n_protcol_dag_results = pr.n_protocol_dag_results + transformation_n_protocol_dag_results = pr.n_protocol_dag_results scaling_factor = self._exponential_decay_scaling( - transformation_n_protcol_dag_results, settings.decay_rate + transformation_n_protocol_dag_results, settings.decay_rate ) weight = scaling_factor * (num_neighbors_a + num_neighbors_b) / 2 @@ -143,12 +142,12 @@ def _propose( if weight < cutoff: weight = None case (max_runs, None) if max_runs is not None: - if transformation_n_protcol_dag_results >= max_runs: + if transformation_n_protocol_dag_results >= max_runs: weight = None case (max_runs, cutoff) if max_runs is not None and cutoff is not None: if ( weight < cutoff - or transformation_n_protcol_dag_results >= max_runs + or transformation_n_protocol_dag_results >= max_runs ): weight = None diff --git a/src/stratocaster/strategies/radialgrowth.py b/src/stratocaster/strategies/radialgrowth.py index 27f0cc8..16ad108 100644 --- a/src/stratocaster/strategies/radialgrowth.py +++ b/src/stratocaster/strategies/radialgrowth.py @@ -17,7 +17,7 @@ class RadialGrowthStrategySettings(StrategySettings): max_runs: int = Field( default=3, - description="the upper limit of ProtocolDAG results needed before a Transformation is no longer weighed", + description="the upper limit of ProtocolDAG results needed before a Transformation is no longer weighted", ) candidacy_max_distance: int = Field( @@ -42,10 +42,10 @@ def validate_max_runs(cls, value): return value @field_validator("candidacy_max_distance", mode="before") - def validate_candidate_max_distance(cls, value): + def validate_candidacy_max_distance(cls, value): if not value >= 1: raise ValueError( - "`candidtate_max_distance` must be greater than or equal to 1" + "`candidacy_max_distance` must be greater than or equal to 1" ) return value @@ -68,7 +68,7 @@ class RadialGrowthStrategy(Strategy): The weight assigned to each Transformation depends on its highest ChemicalSystem distance, as measured and labeled by the vertex - eccentricty, from the lowest completed tier of distances. In the + eccentricity, from the lowest completed tier of distances. In the graph below, if at least one ``ProtocolDAGResult`` exists for both edges in 3-2-3, the lowest completed distance is 3. If neither edge or only one edge has a result, the lowest completed distance @@ -125,13 +125,13 @@ def _propose( alchemical_network_mdg = alchemical_network.graph weights: dict[GufeKey, float | None] = {} - # calculate all node eccentricies + # calculate all node eccentricities e = nx.eccentricity(alchemical_network_mdg.to_undirected()) # start with the maximum value, this will be decremented as we # see evidence the value should be lower lowest_complete_eccentricity = max(e.values()) - # hold on to the eccentricies of the transformations instead + # hold on to the eccentricities of the transformations instead # of the distances since we don't know the lowest complete # eccentricity until we process the full graph, distances can # be calculated after @@ -139,7 +139,7 @@ def _propose( for state_a, state_b in alchemical_network_mdg.edges(): edge = e[state_a], e[state_b] - # find the range of eccentricies + # find the range of eccentricities lower, upper = min(edge), max(edge) transformation_key = alchemical_network_mdg.get_edge_data(state_a, state_b)[ @@ -149,7 +149,7 @@ def _propose( factor_repeats = 1 match (protocol_results.get(transformation_key)): case None: - transformation_n_protcol_dag_results = 0 + transformation_n_protocol_dag_results = 0 # since we have no results for this # transformation, we know the lowest complete # eccentricity must be lower than the upper @@ -157,16 +157,16 @@ def _propose( if upper < lowest_complete_eccentricity: lowest_complete_eccentricity = lower case pr: - transformation_n_protcol_dag_results = pr.n_protocol_dag_results + transformation_n_protocol_dag_results = pr.n_protocol_dag_results # scale the repeat factor to discourage reruns as # specified by the user's decay_repeat_rate factor_repeats *= ( self.settings.decay_repeat_rate - ** transformation_n_protcol_dag_results + ** transformation_n_protocol_dag_results ) # stop condition given max runs - if self.settings.max_runs <= transformation_n_protcol_dag_results: + if self.settings.max_runs <= transformation_n_protocol_dag_results: weights[transformation_key] = None continue