Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 4.0.0 #141

Merged
merged 10 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
# CHANGELOG

## Version 4.0.0 2023-11-30

### Features

- `Configuration` now supports a `rescale_prior` property which rescales the priors in `TemplateBasedExpansionStrategy`.
- Functionality of `ScorerCollection` has been extended.
- Pricing is now supported in `InMemoryInchiKeyQuery`.
- Reward scorer has been added to Configuration scorers as `search_reward` item.
- `MaxTransformScorerer` and `FractionInStockScorer` have been created to separate scores in `StateScorer`.
- Reaction routes are scored with all reward scorers after the search is complete.
- `StockAvailabilityScorer` and `ReactionClassMembershipScorer` have been added to the scorers.
- Atom mapping existing in target molecule can be inherited.
- A caching feature has been add to the expansion strategies.
- Degenerate states can now be grouped in the MCTS algorithm.
- `ChemformerBasedExpansionStrategy` and `ModelZooExpansionStrategy` can be found under plugins and used as additional expansion strategies.
- Support for the stereocenter model has been added with to support chiral fingerprints for molecules and reactions.
- The `Configuration` format has been entirely revamped to a more easy-to-use format.
- A `MolbloomFilterQuery` has been created as a stock query class.

### Trivial changes

- Python version requirements have been updated to versions 3.9 - 3.11.
- `MoleculeCost` has been moved from aizynthfinder.context.cost.collection to the Retro* package.

### Deprecations

- Graphviz has been removed from aizynthfinder.utils.image.
- `Reaction` class has been removed from aizynthfinder.chem.reaction.
- aizynthfinder.context.cost package has been removed.

### Bug-fixes

- Fixed an issue with `max_transforms` to ensure only the given number of maximum depth is considered.
- Pinned Jupyter notebook version to ^6.5.3 to avoid errors when displaying widgets.
- Rollout child has been removed from the MCTS search algorithm.

## Version 3.7.0 2023-06-01 (2023-04-11)

### Features
Expand Down Expand Up @@ -289,4 +325,4 @@

## Version 1.0.0 - 2020-06-11

- First public version
- First public version
36 changes: 17 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AiZynthFinder
# AiZynthFinder

[![License](https://img.shields.io/github/license/MolecularAI/aizynthfinder)](https://github.com/MolecularAI/aizynthfinder/blob/master/LICENSE)
[![Tests](https://github.com/MolecularAI/aizynthfinder/workflows/tests/badge.svg)](https://github.com/MolecularAI/aizynthfinder/actions?workflow=tests)
Expand All @@ -7,8 +7,8 @@
[![version](https://img.shields.io/github/v/release/MolecularAI/aizynthfinder)](https://github.com/MolecularAI/aizynthfinder/releases)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/MolecularAI/aizynthfinder/blob/master/contrib/notebook.ipynb)


AiZynthFinder is a tool for retrosynthetic planning. The algorithm is based on a Monte Carlo tree search that recursively breaks down a molecule to purchasable precursors. The tree search is guided by a policy that suggests possible precursors by utilizing a neural network trained on a library of known reaction templates.
AiZynthFinder is a tool for retrosynthetic planning. The default algorithm is based on a Monte Carlo tree search that recursively breaks down a molecule to purchasable precursors. The tree search is guided by a policy that suggests possible precursors by utilizing a neural network trained on a library of known reaction templates. This setup is completely customizable as the tool
supports multiple search algorithms and expansion policies.

An introduction video can be found here: [https://youtu.be/r9Dsxm-mcgA](https://youtu.be/r9Dsxm-mcgA)

Expand All @@ -18,19 +18,18 @@ Before you begin, ensure you have met the following requirements:

* Linux, Windows or macOS platforms are supported - as long as the dependencies are supported on these platforms.

* You have installed [anaconda](https://www.anaconda.com/) or [miniconda](https://docs.conda.io/en/latest/miniconda.html) with python 3.8 - 3.9
* You have installed [anaconda](https://www.anaconda.com/) or [miniconda](https://docs.conda.io/en/latest/miniconda.html) with python 3.9 - 3.11

The tool has been developed on a Linux platform, but the software has been tested on Windows 10 and macOS Catalina.


## Installation

### For end-users

Lakshidaa marked this conversation as resolved.
Show resolved Hide resolved
First time, execute the following command in a console or an Anaconda prompt

conda create "python>=3.8,<3.10" -n aizynth-env

To install, activate the environment and install the package using pypi

conda activate aizynth-env
Expand All @@ -48,28 +47,28 @@ Then execute the following commands in the root of the repository

conda env create -f env-dev.yml
conda activate aizynth-dev
poetry install -E all
poetry install --all-extras

the `aizynthfinder` package is now installed in editable mode.


## Usage

The tool will install the ``aizynthcli`` and ``aizynthapp`` tools
The tool will install the `aizynthcli` and `aizynthapp` tools
as interfaces to the algorithm:

```
aizynthcli --config config.yml --smiles smiles.txt
aizynthapp --config config.yml
```
aizynthcli --config config_local.yml --smiles smiles.txt
aizynthapp --config config_local.yml


Consult the documentation [here](https://molecularai.github.io/aizynthfinder/) for more information.

To use the tool you need

1. A stock file
2. A trained rollout policy network (including the Keras model and the list of unique templates)
3. A trained filer policy network (optional)

2. A trained expansion policy network
3. A trained filter policy network (optional)
Such files can be downloaded from [figshare](https://figshare.com/articles/AiZynthFinder_a_fast_robust_and_flexible_open-source_software_for_retrosynthetic_planning/12334577) and [here](https://figshare.com/articles/dataset/A_quick_policy_to_filter_reactions_based_on_feasibility_in_AI-guided_retrosynthetic_planning/13280507) or they can be downloaded automatically using

```
Expand Down Expand Up @@ -122,6 +121,8 @@ Please use ``black`` package for formatting, and follow ``pep8`` style guide.
## Contributors

* [@SGenheden](https://www.github.com/SGenheden)
* [@lakshidaa](https://github.com/Lakshidaa)
* [@helenlai](https://github.com/helenlai)
* [@EBjerrum](https://www.github.com/EBjerrum)
* [@A-Thakkar](https://www.github.com/A-Thakkar)
* [@benteb](https://www.github.com/benteb)
Expand All @@ -135,7 +136,4 @@ The software is licensed under the MIT license (see LICENSE file), and is free a
## References

1. Thakkar A, Kogej T, Reymond J-L, et al (2019) Datasets and their influence on the development of computer assisted synthesis planning tools in the pharmaceutical domain. Chem Sci. https://doi.org/10.1039/C9SC04944D
2. Genheden S, Thakkar A, Chadimova V, et al (2020) AiZynthFinder: a fast, robust and flexible open-source software for retrosynthetic planning. J. Cheminf. https://jcheminf.biomedcentral.com/articles/10.1186/s13321-020-00472-1
3. Genheden S, Engkvist O, Bjerrum E (2020) A Quick Policy to Filter Reactions Based on Feasibility in AI-Guided Retrosynthetic Planning. ChemRxiv. Preprint. https://doi.org/10.26434/chemrxiv.13280495.v1
4. Genheden S, Engkvist O, Bjerrum E (2021) Clustering of synthetic routes using tree edit distance. J. Chem. Inf. Model. 61:3899–3907 [https://doi.org/10.1021/acs.jcim.1c00232](https://doi.org/10.1021/acs.jcim.1c00232)
5. Genheden S, Engkvist O, Bjerrum E (2022) Fast prediction of distances between synthetic routes with deep learning. Mach. Learn. Sci. Technol. 3:015018 [https://doi.org/10.1088/2632-2153/ac4a91](https://doi.org/10.1088/2632-2153/ac4a91)
2. Genheden S, Thakkar A, Chadimova V, et al (2020) AiZynthFinder: a fast, robust and flexible open-source software for retrosynthetic planning. ChemRxiv. Preprint. https://doi.org/10.26434/chemrxiv.12465371.v1
41 changes: 30 additions & 11 deletions aizynthfinder/aizynthfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)
from aizynthfinder.chem import FixedRetroReaction, Molecule, TreeMolecule
from aizynthfinder.context.config import Configuration
from aizynthfinder.context.scoring import CombinedScorer
from aizynthfinder.reactiontree import ReactionTreeFromExpansion
from aizynthfinder.search.andor_trees import AndOrSearchTreeBase
from aizynthfinder.search.mcts import MctsSearchTree
Expand Down Expand Up @@ -60,7 +61,9 @@ class AiZynthFinder:
:param configdict: the config as a dictionary source, defaults to None
"""

def __init__(self, configfile: str = None, configdict: StrDict = None) -> None:
def __init__(
self, configfile: Optional[str] = None, configdict: Optional[StrDict] = None
) -> None:
self._logger = logger()

if configfile:
Expand Down Expand Up @@ -102,7 +105,9 @@ def target_mol(self, mol: Molecule) -> None:
self._target_mol = mol

def build_routes(
self, selection: RouteSelectionArguments = None, scorer: str = "state score"
self,
selection: Optional[RouteSelectionArguments] = None,
scorer: Optional[str] = None,
) -> None:
"""
Build reaction routes
Expand All @@ -114,10 +119,15 @@ def build_routes(
:param scorer: a reference to the object used to score the nodes
:raises ValueError: if the search tree not initialized
"""

scorer = scorer or self.config.post_processing.route_scorer

if not self.tree:
raise ValueError("Search tree not initialized")

self.analysis = TreeAnalysis(self.tree, scorer=self.scorers[scorer])
_scorer = self.scorers[scorer]

self.analysis = TreeAnalysis(self.tree, scorer=_scorer)
config_selection = RouteSelectionArguments(
nmin=self.config.post_processing.min_routes,
nmax=self.config.post_processing.max_routes,
Expand Down Expand Up @@ -157,13 +167,17 @@ def prepare_tree(self) -> None:
raise ValueError("Target molecule unsanitizable")

self.stock.reset_exclusion_list()
if self.config.exclude_target_from_stock and self.target_mol in self.stock:
if (
self.config.search.exclude_target_from_stock
and self.target_mol in self.stock
):
self.stock.exclude(self.target_mol)
self._logger.debug("Excluding the target compound from the stock")

self._setup_search_tree()
self.analysis = None
self.routes = RouteCollection([])
self.expansion_policy.reset_cache()

def stock_info(self) -> StrDict:
"""
Expand Down Expand Up @@ -202,9 +216,12 @@ def tree_search(self, show_progress: bool = False) -> float:
time_past = time.time() - time0

if show_progress:
pbar = tqdm(total=self.config.iteration_limit, leave=False)
pbar = tqdm(total=self.config.search.iteration_limit, leave=False)

while time_past < self.config.time_limit and i <= self.config.iteration_limit:
while (
time_past < self.config.search.time_limit
and i <= self.config.search.iteration_limit
):
if show_progress:
pbar.update(1)
self.search_stats["iterations"] += 1
Expand All @@ -218,7 +235,7 @@ def tree_search(self, show_progress: bool = False) -> float:
self.search_stats["first_solution_time"] = time.time() - time0
self.search_stats["first_solution_iteration"] = i

if self.config.return_first and is_solved:
if self.config.search.return_first and is_solved:
self._logger.debug("Found first solved route")
self.search_stats["returned_first"] = True
break
Expand All @@ -234,12 +251,12 @@ def tree_search(self, show_progress: bool = False) -> float:

def _setup_search_tree(self) -> None:
self._logger.debug("Defining tree root: %s" % self.target_smiles)
if self.config.search_algorithm.lower() == "mcts":
if self.config.search.algorithm.lower() == "mcts":
self.tree = MctsSearchTree(
root_smiles=self.target_smiles, config=self.config
)
else:
cls = load_dynamic_class(self.config.search_algorithm)
cls = load_dynamic_class(self.config.search.algorithm)
self.tree = cls(root_smiles=self.target_smiles, config=self.config)


Expand All @@ -260,7 +277,9 @@ class AiZynthExpander:
:param configdict: the config as a dictionary source, defaults to None
"""

def __init__(self, configfile: str = None, configdict: StrDict = None) -> None:
def __init__(
self, configfile: Optional[str] = None, configdict: Optional[StrDict] = None
) -> None:
self._logger = logger()

if configfile:
Expand All @@ -278,7 +297,7 @@ def do_expansion(
self,
smiles: str,
return_n: int = 5,
filter_func: Callable[[RetroReaction], bool] = None,
filter_func: Optional[Callable[[RetroReaction], bool]] = None,
) -> List[Tuple[FixedRetroReaction, ...]]:
"""
Do the expansion of the given molecule returning a list of
Expand Down
5 changes: 3 additions & 2 deletions aizynthfinder/analysis/tree_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Any,
Iterable,
List,
Optional,
Sequence,
StrDict,
Tuple,
Expand All @@ -41,7 +42,7 @@ class TreeAnalysis:
def __init__(
self,
search_tree: Union[MctsSearchTree, AndOrSearchTreeBase],
scorer: Scorer = None,
scorer: Optional[Scorer] = None,
) -> None:
self.search_tree = search_tree
if scorer is None:
Expand All @@ -65,7 +66,7 @@ def best(self) -> Union[MctsNode, ReactionTree]:
return sorted_routes[0]

def sort(
self, selection: RouteSelectionArguments = None
self, selection: Optional[RouteSelectionArguments] = None
) -> Tuple[Union[Sequence[MctsNode], Sequence[ReactionTree]], Sequence[float]]:
"""
Sort and select the nodes or routes in the search tree.
Expand Down
11 changes: 8 additions & 3 deletions aizynthfinder/analysis/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
from aizynthfinder.utils.image import make_visjs_page

if TYPE_CHECKING:
from aizynthfinder.utils.type_utils import FrameColors, Sequence, StrDict, Tuple
from aizynthfinder.utils.type_utils import (
FrameColors,
Optional,
Sequence,
StrDict,
Tuple,
)


@dataclass
Expand Down Expand Up @@ -69,7 +75,7 @@ def to_dict(self) -> StrDict:
def to_visjs_page(
self,
filename: str,
in_stock_colors: FrameColors = None,
in_stock_colors: Optional[FrameColors] = None,
) -> None:
"""
Create a visualization of the combined reaction tree using the vis.js network library.
Expand All @@ -93,7 +99,6 @@ def _add_reaction_trees_to_node(
base_node: UniqueMolecule,
rt_node_spec: Sequence[Tuple[UniqueMolecule, nx.DiGraph]],
) -> None:

reaction_groups = defaultdict(list)
# Group the reactions from the nodes at this level based on the reaction smiles
for node, graph in rt_node_spec:
Expand Down
1 change: 0 additions & 1 deletion aizynthfinder/chem/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
)
from aizynthfinder.chem.reaction import (
FixedRetroReaction,
Reaction,
RetroReaction,
SmilesBasedRetroReaction,
TemplatedRetroReaction,
Expand Down
Loading
Loading