Skip to content

Reaction tree plain text visualization #53

Description

@tim-hsu

I'm not sure if this should be a PR since it's not an essential feature, so I'm documenting the feature here as an issue.

Example visualizations

A reaction tree is the tree representation of a chemical synthesis route, with the root node being the target molecule, which is always printed on the first line.

Tamoxifen

CC/C(=C(\c1ccccc1)c1ccc(OCCN(C)C)cc1)c1ccccc1
├── CC/C(=C\c1ccccc1)c1ccccc1
│   └── CCC(O)(Cc1ccccc1)c1ccccc1
│       ├── CCC(=O)Cc1ccccc1
│       └── Brc1ccccc1
└── CN(C)CCOc1ccc(I)cc1
    ├── CN(C)CCCl
    └── Oc1ccc(I)cc1

Ritonavir

CC(C)c1nc(CN(C)C(=O)N[C@H](C(=O)N[C@@H](Cc2ccccc2)C[C@H](O)[C@H](Cc2ccccc2)NC(=O)OCc2cncs2)C(C)C)cs1
├── CC(C)c1nc(CN(C)C(=O)N[C@H](C(=O)Cl)C(C)C)cs1
│   ├── O=C(Cl)C(=O)Cl
│   └── CC(C)c1nc(CN(C)C(=O)N[C@H](C(=O)O)C(C)C)cs1
│       └── COC(=O)[C@@H](NC(=O)N(C)Cc1csc(C(C)C)n1)C(C)C
│           ├── CNCc1csc(C(C)C)n1
│           └── COC(=O)[C@@H](NC(=O)Oc1ccc([N+](=O)[O-])cc1)C(C)C
│               ├── COC(=O)[C@@H](N)C(C)C
│               └── O=C(Cl)Oc1ccc([N+](=O)[O-])cc1
└── N[C@@H](Cc1ccccc1)C[C@H](O)[C@H](Cc1ccccc1)NC(=O)OCc1cncs1
    └── [N-]=[N+]=N[C@@H](Cc1ccccc1)C[C@H](O)[C@H](Cc1ccccc1)NC(=O)OCc1cncs1
        └── CC(C)(C)[Si](C)(C)O[C@@H](C[C@H](Cc1ccccc1)N=[N+]=[N-])[C@H](Cc1ccccc1)NC(=O)OCc1cncs1
            ├── O=C(OCc1cncs1)Oc1ccc([N+](=O)[O-])cc1
            │   ├── OCc1cncs1
            │   └── O=C(Cl)Oc1ccc([N+](=O)[O-])cc1
            └── CC(C)(C)[Si](C)(C)O[C@@H](C[C@H](Cc1ccccc1)N=[N+]=[N-])[C@@H](N)Cc1ccccc1

In-house implementation

The in-house code is fairly simple, as shown below.

How to reproduce

Add this function to the ReactionPath class:

def show_tree(self, stdout: bool = True) -> str | None:
    # Outer, nonlocal string to be modified during depth-first search
    # In this string, the first line is always the root node
    string = self.root.smiles + '\n'

    # Depth-first search
    def dfs(curr: Node, prefix: str):
        nonlocal string
        for i in range(len(curr.children)):
            node_id = curr.children[i]
            if i == len(curr.children) - 1:
                string += prefix + '└── ' + self.nodes[node_id].smiles + '\n'
                dfs(self.nodes[node_id], prefix + '    ')
            else:
                string += prefix + '├── ' + self.nodes[node_id].smiles + '\n'
                dfs(self.nodes[node_id], prefix + '│   ')
    
    dfs(self.root, '')
    
    # Whether to print to console or return the string object
    if stdout:
        print(string)
    else:
        return string

Then, run the following:

from charge.servers.AiZynthTools import find_synthesis_routes

routes = find_synthesis_routes('CN(C)CCOc1ccc(cc1)/C(c2ccccc2)=C(/CC)c3ccccc3')  # tamoxifen
path = ReactionPath(routes[0])
path.show_tree()

treelib implementation

If we want more features for reaction tree analysis and visualization, I recommend using the treelib package, which is very light-weight and only requires the six dependency.

How to reproduce

from treelib import Tree
from treelib.node import Node

def route2tree(route: dict) -> Tree:
    tree = Tree()
    def dfs(curr: dict, parent: Node | None):
        if curr['type'] == 'reaction':
            for child in curr['children']:
                dfs(child, parent)
        else:
            # You can add custom data (such as `in_stock`) with the `data` keyword
            node = tree.create_node(tag=curr['smiles'], parent=parent, data={'in_stock': curr['in_stock']})
            if 'children' in curr:
                for child in curr['children']:
                    dfs(child, node)
    dfs(route, None)
    return tree

routes = find_synthesis_routes('CN(C)CCOc1ccc(cc1)/C(c2ccccc2)=C(/CC)c3ccccc3')  # tamoxifen
tree = route2tree(routes[0])
tree.show()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions