Skip to content

Commit

Permalink
Merge pull request #96 from MolecularAI/release-3.6.0
Browse files Browse the repository at this point in the history
Release 3.6.0
  • Loading branch information
SGenheden committed Nov 28, 2022
2 parents cfb7ffa + 867d1c2 commit 9e44989
Show file tree
Hide file tree
Showing 65 changed files with 9,496 additions and 3,236 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/docs.yml
Expand Up @@ -8,13 +8,13 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: build
run: |
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh |
bash -s -- --batch
conda env create -f env-dev.yml
conda run --name aizynth-dev poetry install
conda run --name aizynth-dev poetry install -E all
conda run --name aizynth-dev inv build-docs
- name: deploy
uses: peaceiris/actions-gh-pages@v3
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Expand Up @@ -10,13 +10,13 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Run
run: |
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh |
bash -s -- --batch
conda env create -f env-dev.yml
conda run --name aizynth-dev poetry install
conda run --name aizynth-dev poetry install -E all
conda run --name aizynth-dev inv full-tests
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
Expand Down
6 changes: 3 additions & 3 deletions .gitignore
Expand Up @@ -70,9 +70,9 @@ instance/

# Sphinx documentation
docs/build/
docs/source/aizynth*
docs/source/modules.rst
docs/source/cli_help.txt
docs/aizynth*
docs/modules.rst
docs/cli_help.txt

# PyBuilder
target/
Expand Down
35 changes: 35 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,40 @@
# CHANGELOG

## Version 3.6.0 2022-11-28 (2022-11-25)

### Features

- aizynthfinder can now be installed as a pure-python package
- ReactionTree now store at what iteration the route was created
- ReactionTree now supports a metadata property
- MCTS nodes populate a property that stores at what iteration the route was created
- aizynthcli now outputs stock information and route metadata and scores
- aizynthcli now has better error handling of invalid SMILES input
- Graphviz dependency for route drawing is removed, pure python implementation is used instead
- Dependencies reworked so that a minimal package can be installed
- Extra dependencies are related to route distances, clustering, training, MongoDB and external models
- Downloaded files are now the latest USPTO and Ringbreaker models

### Trivial changes

- RDKit and route-distances package now installed from pypi
- Documentation updated and extended

## Version 3.5.0 2022-11-28 (2022-07-21)

### Features

- Atom-mapping is tracked from product to reactant
- Support loading of template library from (gzipped) CSV file
- Support of saving aizynthcli output to (gzipped) JSON file
- AiZynthExpander now tracks non-applicable templates

### Bug-fixes
- Fixed failing test case

### Trival changes
- Silent progress bar when utilizing local Keras model

## Version 3.4.0 2022-04-28

### Features
Expand Down
43 changes: 10 additions & 33 deletions README.md
Expand Up @@ -18,7 +18,7 @@ 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.6 - 3.9
* You have installed [anaconda](https://www.anaconda.com/) or [miniconda](https://docs.conda.io/en/latest/miniconda.html) with python 3.8 - 3.9

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

Expand All @@ -29,15 +29,16 @@ The tool has been developed on a Linux platform, but the software has been teste

First time, execute the following command in a console or an Anaconda prompt

conda env create -f https://raw.githubusercontent.com/MolecularAI/aizynthfinder/master/env-users.yml
conda create "python>=3.8,<3.10" -n aizynth-env

And if you want to update the environment

conda env update -n aizynth-env -f https://raw.githubusercontent.com/MolecularAI/aizynthfinder/master/env-users.yml

The package is now installed in a new conda environment, that you need to activate each time you want to use it
To install, activate the environment and install the package using pypi

conda activate aizynth-env
python -m pip install aizynthfinder[all]

for a smaller package, without all the functionality, you can also type

python -m pip install aizynthfinder

### For developers

Expand All @@ -47,35 +48,10 @@ Then execute the following commands in the root of the repository

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

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

### Troubleshooting

If the above simple instructions does not work, here are the more detailed instructions. You might have to modify conda channels or similar if the dependencies fails to install on your OS.

First, install these conda packages

conda install -c conda-forge "rdkit=>2019.09.1" -y
conda install graphviz -y

Secondly, install the ``aizynthfinder`` package

python -m pip install https://github.com/MolecularAI/aizynthfinder/archive/v3.4.0.tar.gz


if you want to install the latest version

or, if you have cloned this repository

conda install poetry
python poetry


> Note on the graphviz installation: this package does not depend on any third-party python interfaces to graphviz but instead calls the `dot` executable directly. If the executable is not in the `$PATH` environmental variable, the generation of route images will not work. If unable to install it properly with the default conda channel, try using `-c anaconda`.

## Usage

The tool will install the ``aizynthcli`` and ``aizynthapp`` tools
Expand Down Expand Up @@ -148,6 +124,7 @@ Please use ``black`` package for formatting, and follow ``pep8`` style guide.
* [@SGenheden](https://www.github.com/SGenheden)
* [@EBjerrum](https://www.github.com/EBjerrum)
* [@A-Thakkar](https://www.github.com/A-Thakkar)
* [@benteb](https://www.github.com/benteb)

The contributors have limited time for support questions, but please do not hesitate to submit an issue (see above).

Expand Down
21 changes: 21 additions & 0 deletions aizynthfinder/aizynthfinder.py
Expand Up @@ -163,6 +163,24 @@ def prepare_tree(self) -> None:
self.analysis = None
self.routes = RouteCollection([])

def stock_info(self) -> StrDict:
"""
Return the stock availability for all leaf nodes in all collected reaction trees
The key of the return dictionary will be the SMILES string of the leaves,
and the value will be the stock availability
:return: the collected stock information.
"""
if not self.analysis:
return {}
_stock_info = {}
for tree in self.routes.reaction_trees:
for leaf in tree.leafs():
if leaf.smiles not in _stock_info:
_stock_info[leaf.smiles] = self.stock.availability_list(leaf)
return _stock_info

def tree_search(self, show_progress: bool = False) -> float:
"""
Perform the actual tree search
Expand Down Expand Up @@ -254,6 +272,7 @@ def __init__(self, configfile: str = None, configdict: StrDict = None) -> None:

self.expansion_policy = self.config.expansion_policy
self.filter_policy = self.config.filter_policy
self.stats: StrDict = {}

def do_expansion(
self,
Expand All @@ -280,13 +299,15 @@ def do_expansion(
:param filter_func: an additional filter function
:return: the grouped reactions
"""
self.stats = {"non-applicable": 0}

mol = TreeMolecule(parent=None, smiles=smiles)
actions, _ = self.expansion_policy.get_actions([mol])
results: Dict[Tuple[str, ...], List[FixedRetroReaction]] = defaultdict(list)
for action in actions:
reactants = action.reactants
if not reactants:
self.stats["non-applicable"] += 1
continue
if filter_func and not filter_func(action):
continue
Expand Down
51 changes: 43 additions & 8 deletions aizynthfinder/analysis/routes.py
Expand Up @@ -4,14 +4,18 @@
from typing import TYPE_CHECKING

import numpy as np
from route_distances.clustering import ClusteringHelper
from route_distances.route_distances import route_distances_calculator

try:
from route_distances.clustering import ClusteringHelper
from route_distances.route_distances import route_distances_calculator
except ImportError:
pass

from aizynthfinder.analysis.utils import (
CombinedReactionTrees,
RouteSelectionArguments,
)
from aizynthfinder.reactiontree import ReactionTree
from aizynthfinder.reactiontree import SUPPORT_DISTANCES, ReactionTree
from aizynthfinder.search.mcts import MctsSearchTree, MctsNode
from aizynthfinder.analysis import TreeAnalysis

Expand Down Expand Up @@ -49,6 +53,7 @@ class RouteCollection:
:ivar scores: initial scores of top-ranked nodes or routes
:ivar reaction_trees: the reaction trees created from the top-ranked nodes
:ivar clusters: the created clusters from the collection
:ivar route_metadata: the metadata of the reaction trees
:param reaction_trees: the trees to base the collection on
"""
Expand All @@ -57,6 +62,8 @@ def __init__(self, reaction_trees: Sequence[ReactionTree], **kwargs) -> None:
self._routes: Sequence[StrDict] = [{} for _ in range(len(reaction_trees))]
self.reaction_trees = reaction_trees
self._update_route_dict(reaction_trees, "reaction_tree")
self.route_metadata = [rt.metadata for rt in reaction_trees]
self._update_route_dict(self.route_metadata, "route_metadata")

self.nodes = self._unpack_kwarg_with_default("nodes", None, **kwargs)
self.scores = self._unpack_kwarg_with_default("scores", np.nan, **kwargs)
Expand Down Expand Up @@ -152,6 +159,12 @@ def cluster(
:param distances_model: can be ted or lstm and determines how the route distances are computed
:return: the cluster labels
"""
if not SUPPORT_DISTANCES:
raise ValueError(
"Clustering is not supported by this installation."
" Please install aizynthfinder with extras dependencies."
)

if len(self.reaction_trees) < 3:
return np.asarray([])
dist_kwargs = {
Expand Down Expand Up @@ -199,19 +212,35 @@ def compute_scores(self, *scorers: Scorer) -> None:
self.all_scores[idx][repr(scorer)] = score
self._update_route_dict(self.all_scores, "all_score")

def dict_with_scores(self) -> Sequence[StrDict]:
def dict_with_extra(
self, include_scores=False, include_metadata=False
) -> Sequence[StrDict]:
"""
Return the routes as dictionaries with all scores added
to the root (target) node.
Return the routes as dictionaries with optionally all scores and
all metadata added to the root (target) node.
:return: the routes as dictionaries
"""
dicts = []
for dict_, scores in zip(self.dicts, self.all_scores):
for dict_, scores, metadata in zip(
self.dicts, self.all_scores, self.route_metadata
):
dicts.append(dict(dict_))
dicts[-1]["scores"] = dict(scores)
if include_scores:
dicts[-1]["scores"] = dict(scores)
if include_metadata:
dicts[-1]["metadata"] = dict(metadata)
return dicts

def dict_with_scores(self) -> Sequence[StrDict]:
"""
Return the routes as dictionaries with all scores added
to the root (target) node.
:return: the routes as dictionaries
"""
return self.dict_with_extra(include_scores=True)

def distance_matrix(
self, recreate: bool = False, model: str = "ted", **kwargs: Any
) -> np.ndarray:
Expand All @@ -229,6 +258,12 @@ def distance_matrix(
:param model: the type of model to use "ted" or "lstm"
:return: the square distance matrix
"""
if not SUPPORT_DISTANCES:
raise ValueError(
"Distance calculations are not supported by this installation."
" Please install aizynthfinder with extras dependencies."
)

if model == "lstm" and not kwargs.get("model_path"):
raise KeyError(
"Need to provide 'model_path' argument when using LSTM model for computing distances"
Expand Down

0 comments on commit 9e44989

Please sign in to comment.