Skip to content

Commit

Permalink
Merge pull request #141 from MolecularAI/release-4.0.0
Browse files Browse the repository at this point in the history
Release 4.0.0
  • Loading branch information
Lakshidaa committed Dec 5, 2023
2 parents 82f19c4 + 6e9e809 commit 1667b7f
Show file tree
Hide file tree
Showing 103 changed files with 5,970 additions and 5,913 deletions.
38 changes: 37 additions & 1 deletion CHANGELOG.md
@@ -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
@@ -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

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
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
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
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
Expand Up @@ -9,7 +9,6 @@
)
from aizynthfinder.chem.reaction import (
FixedRetroReaction,
Reaction,
RetroReaction,
SmilesBasedRetroReaction,
TemplatedRetroReaction,
Expand Down

0 comments on commit 1667b7f

Please sign in to comment.