Skip to content
Open
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
19 changes: 11 additions & 8 deletions gauche/data_featuriser/featurisation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import numpy as np
from rdkit.Chem import MolFromSmiles, AllChem, Descriptors
import pandas as pd
from typing import List, Optional
from rxnfp.transformer_fingerprints import (
get_default_model_and_tokenizer,
RXNBERTFingerprintGenerator,
Expand All @@ -10,10 +11,12 @@
import selfies as sf
import graphein.molecule as gm
from rdkit.Chem import rdMolDescriptors
from graphein.molecule.config import MoleculeGraphConfig
import networkx as nx


# Reactions
def one_hot(df):
def one_hot(df: pd.DataFrame) -> np.ndarray:
"""
Builds reaction representation as a bit vector which indicates whether
a certain condition, reagent, reactant etc. is present in the reaction.
Expand All @@ -28,7 +31,7 @@ def one_hot(df):
return df_ohe.to_numpy(dtype=np.float64)


def rxnfp(reaction_smiles):
def rxnfp(reaction_smiles: List[str]) -> np.ndarray:
"""
https://rxn4chemistry.github.io/rxnfp/

Expand All @@ -44,7 +47,7 @@ def rxnfp(reaction_smiles):
return np.array(rxnfps, dtype=np.float64)


def drfp(reaction_smiles, nBits=2048):
def drfp(reaction_smiles: List[str], nBits: int = 2048) -> np.ndarray:
"""
https://github.com/reymond-group/drfp

Expand All @@ -59,7 +62,7 @@ def drfp(reaction_smiles, nBits=2048):


# Molecules
def fingerprints(smiles, bond_radius=3, nBits=2048):
def fingerprints(smiles: List[str], bond_radius: int = 3, nBits: int = 2048) -> np.ndarray:
rdkit_mols = [MolFromSmiles(smiles) for smiles in smiles]
fps = [
AllChem.GetMorganFingerprintAsBitVect(mol, bond_radius, nBits=nBits)
Expand All @@ -69,7 +72,7 @@ def fingerprints(smiles, bond_radius=3, nBits=2048):


# auxiliary function to calculate the fragment representation of a molecule
def fragments(smiles):
def fragments(smiles: List[str]) -> np.ndarray:
# descList[115:] contains fragment-based features only
# (https://www.rdkit.org/docs/source/rdkit.Chem.Fragments.html)
# Update: in the new RDKit version the indices are [124:]
Expand All @@ -87,7 +90,7 @@ def fragments(smiles):


# auxiliary function to calculate bag of character representation of a molecular string
def bag_of_characters(smiles, max_ngram=5, selfies=False):
def bag_of_characters(smiles: List[str], max_ngram: int = 5, selfies: bool = False) -> np.ndarray:
if selfies: # convert SMILES to SELFIES
strings = [sf.encoder(smiles[i]) for i in range(len(smiles))]
else: # otherwise stick with SMILES
Expand All @@ -100,13 +103,13 @@ def bag_of_characters(smiles, max_ngram=5, selfies=False):
return cv.fit_transform(strings).toarray()


def graphs(smiles, graphein_config=None):
def graphs(smiles: List[str], graphein_config: Optional[MoleculeGraphConfig] = None) -> List[nx.Graph]:
return [
gm.construct_graph(smiles=i, config=graphein_config) for i in smiles
]


def mqn_features(smiles):
def mqn_features(smiles: List[str]) -> np.ndarray:
"""
Builds molecular representation as a vector of Molecular Quantum Numbers.

Expand Down
29 changes: 22 additions & 7 deletions gauche/dataloader/data_utils.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
"""
Utility functions for molecular data
Utility functions for molecular data.
"""

import numpy as np
from typing import Optional Tuple
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler


def transform_data(
X_train, y_train, X_test, y_test, n_components=None, use_pca=False
):
X_train: np.ndarray,
y_train: np.ndarray,
X_test: np.ndarray,
y_test: np.ndarray,
n_components: Optional[int] = None,
use_pca: bool = False
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, StandardScaler]:
"""
Apply feature scaling, dimensionality reduction to the data. Return the standardised and low-dimensional train and
Apply feature scaling, dimensionality reduction to the data.

Returns the standardised and low-dimensional train and
test sets together with the scaler object for the target values.

:param X_train: input train data
:type X_train: np.ndarray
:param y_train: train labels
:type y_train: np.ndarray
:param X_test: input test data
:type X_test: np.ndarray
:param y_test: test labels
:param n_components: number of principal components to keep when use_pca = True
:param use_pca: Whether or not to use PCA
:type y_test: np.ndarray
:param n_components: number of principal components to keep when ``use_pca=True``
:type n_components: int, optional. Default is ``None``.
:param use_pca: Whether or not to use PCA.
:type use_pca: bool
:return: X_train_scaled, y_train_scaled, X_test_scaled, y_test_scaled, y_scaler
:rtype: Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, sklearn.preprocessing.StandardScaler]
"""

x_scaler = StandardScaler()
Expand Down
2 changes: 1 addition & 1 deletion gauche/dataloader/dataloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def featurize(self, representation):
raise NotImplementedError

def split_and_scale(
self, test_size=0.2, scale_labels=True, scale_features=False
self, test_size: float = 0.2, scale_labels: bool = True, scale_features: bool = False
):
"""Splits the data into training and testing sets.

Expand Down
18 changes: 10 additions & 8 deletions gauche/dataloader/mol_prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

from gauche.dataloader import DataLoader
from rdkit.Chem import MolFromSmiles

from graphein.molecule.config import MoleculeGraphConfig
from typing import Optional

class DataLoaderMP(DataLoader):
def __init__(self):
Expand All @@ -41,7 +42,7 @@ def labels(self):
def labels(self, value):
self._labels = value

def validate(self, drop=True):
def validate(self, drop: bool = True):
"""Checks if the features are valid SMILES strings and (potentially)
drops the entries that are not.

Expand All @@ -68,11 +69,11 @@ def validate(self, drop=True):

def featurize(
self,
representation,
bond_radius=3,
nBits=2048,
graphein_config=None,
max_ngram=5,
representation: str,
bond_radius: int = 3,
nBits: int = 2048,
graphein_config: Optional[MoleculeGraphConfig] = None,
max_ngram: int = 5,
):
"""Transforms SMILES into the specified molecular representation.

Expand Down Expand Up @@ -137,7 +138,7 @@ def featurize(
f"Choose between {valid_representations}."
)

def load_benchmark(self, benchmark, path):
def load_benchmark(self, benchmark: str, path: str):
"""Loads features and labels from one of the included benchmark datasets
and feeds them into the DataLoader.

Expand All @@ -146,6 +147,7 @@ def load_benchmark(self, benchmark, path):
:type benchmark: str
:param path: the path to the dataset in csv format
:type path: str
:raises ValueError: If an unsupported benchmark is provided.
"""

benchmarks = {
Expand Down
6 changes: 4 additions & 2 deletions gauche/dataloader/reaction_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ def labels(self, value):
def validate(self, drop=True):
invalid_idx = []

def featurize(self, representation, nBits=2048):
def featurize(self, representation: str, nBits: int = 2048):
"""Transforms reactions into the specified representation.

:param representation: the desired reaction representation, one of [ohe, rxnfp, drfp, bag_of_smiles]
:type representation: str
:param nBits: int giving the bit vector length for drfp representation. Default is 2048
:type nBits: int
:raises ValueError: If unsupported ``representation`` is provided.
"""

valid_representations = [
Expand Down Expand Up @@ -64,7 +65,7 @@ def featurize(self, representation, nBits=2048):
f"Choose between {valid_representations}."
)

def load_benchmark(self, benchmark, path):
def load_benchmark(self, benchmark: str, path: str):

"""Loads features and labels from one of the included benchmark datasets
and feeds them into the DataLoader.
Expand All @@ -75,6 +76,7 @@ def load_benchmark(self, benchmark, path):
:type benchmark: str
:param path: the path to the dataset in csv format
:type path: str
:raises ValueError: If unsupported ``benchmark`` is provided.
"""

benchmarks = {
Expand Down
14 changes: 7 additions & 7 deletions gauche/kernels/fingerprint_kernels/base_fingerprint_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, postprocess_script=default_postprocess_script):
super().__init__()
self._postprocess = postprocess_script

def _sim(self, x1, x2, postprocess, x1_eq_x2=False, metric="tanimoto"):
def _sim(self, x1: torch.Tensor, x2: torch.Tensor, postprocess: bool, x1_eq_x2: bool = False, metric: str = "tanimoto") -> torch.Tensor:
r"""
Computes the similarity between x1 and x2
Args:
Expand Down Expand Up @@ -82,18 +82,18 @@ def __init__(self, metric="", **kwargs):
super().__init__(**kwargs)
self.metric = metric

def forward(self, x1, x2, **params):
def forward(self, x1: torch.Tensor, x2: torch.Tensor, **params) -> torch.Tensor:
return self.covar_dist(x1, x2, **params)

def covar_dist(
self,
x1,
x2,
last_dim_is_batch=False,
x1: torch.Tensor,
x2: torch.Tensor,
last_dim_is_batch: bool = False,
dist_postprocess_func=default_postprocess_script,
postprocess=True,
postprocess: bool = True,
**params,
):
) -> torch.Tensor:
r"""
This is a helper method for computing the bit vector similarity between
all pairs of points in x1 and x2.
Expand Down
2 changes: 1 addition & 1 deletion gauche/kernels/fingerprint_kernels/tanimoto_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __init__(self, **kwargs):
super(TanimotoKernel, self).__init__(**kwargs)
self.metric = "tanimoto"

def forward(self, x1, x2, diag=False, **params):
def forward(self, x1: torch.Tensor, x2: torch.Tensor, diag: bool = False, **params) -> torch.Tensor:
if diag:
assert x1.size() == x2.size() and torch.equal(x1, x2)
return torch.ones(
Expand Down
22 changes: 11 additions & 11 deletions gauche/kernels/gnn_kernels/pretrained_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
masked_bond_token = 5 # bond type for masked edges


def mol_to_pyg(mol):
def mol_to_pyg(mol: Chem.Mol) -> Data:
"""
A featuriser that accepts an rdkit mol instance and
converts it to a PyTorch Geometric data object that
Expand Down Expand Up @@ -117,7 +117,7 @@ class GINConv(MessagePassing):
edge information by concatenating edge embeddings.
"""

def __init__(self, emb_dim, aggr="add"):
def __init__(self, emb_dim: int, aggr: str = "add"):
"""
Initialise GIN convolutional layer.
Args:
Expand All @@ -139,7 +139,7 @@ def __init__(self, emb_dim, aggr="add"):

self.aggr = aggr

def forward(self, x, edge_index, edge_attr):
def forward(self, x: torch.Tensor, edge_index: torch.Tensor, edge_attr: torch.Tensor) -> torch.Tensor:
"""
Message passing and aggregation function
of the adapted GIN convolutional layer.
Expand Down Expand Up @@ -169,10 +169,10 @@ def forward(self, x, edge_index, edge_attr):
) + self.edge_embedding2(edge_attr[:, 1])
return self.propagate(edge_index, x=x, edge_attr=edge_embeddings)

def message(self, x_j, edge_attr):
def message(self, x_j: torch.Tensor, edge_attr: torch.Tensor) -> torch.Tensor:
return x_j + edge_attr

def update(self, aggr_out):
def update(self, aggr_out: torch.Tensor) -> torch.Tensor:
return self.mlp(aggr_out)


Expand All @@ -182,7 +182,7 @@ class GCNConv(MessagePassing):
edge information by concatenating edge embeddings.
"""

def __init__(self, emb_dim, aggr="add"):
def __init__(self, emb_dim: int, aggr: str = "add"):
super(GCNConv, self).__init__(aggr=aggr)

self.linear = torch.nn.Linear(emb_dim, emb_dim)
Expand All @@ -193,7 +193,7 @@ def __init__(self, emb_dim, aggr="add"):
torch.nn.init.xavier_uniform_(self.edge_embedding2.weight.data)

@staticmethod
def norm(edge_index, num_nodes, dtype):
def norm(edge_index: torch.Tensor, num_nodes: int, dtype) -> torch.Tensor:
"""
Symmetric normalisation step of the adjacency matrix A:
.. math::
Expand All @@ -217,7 +217,7 @@ def norm(edge_index, num_nodes, dtype):
deg_inv_sqrt[deg_inv_sqrt == float("inf")] = 0
return deg_inv_sqrt[row] * edge_weight * deg_inv_sqrt[col]

def forward(self, x, edge_index, edge_attr):
def forward(self, x: torch.Tensor, edge_index: torch.Tensor, edge_attr: torch.Tensor) -> torch.Tensor:
"""
Message passing and aggregation function
of the adapted GCN convolutional layer.
Expand Down Expand Up @@ -261,7 +261,7 @@ class GNN(torch.nn.Module):
Combine multiple GNN layers into a network.
"""

def __init__(self, num_layers=5, embed_dim=300, gnn_type="gin"):
def __init__(self, num_layers: int = 5, embed_dim: int = 300, gnn_type: str = "gin"):
"""
Compose convolution layers into GNN. Pretrained parameters
exist for a 5-layer network with 300 hidden units.
Expand Down Expand Up @@ -300,7 +300,7 @@ def __init__(self, num_layers=5, embed_dim=300, gnn_type="gin"):
for layer in range(self.num_layers):
self.batch_norms.append(torch.nn.BatchNorm1d(self.embed_dim))

def load_pretrained(self, pretrain_type, device):
def load_pretrained(self, pretrain_type: str, device):
"""
Checks if a pretrained parameter set for the specified
pretraining procedure exists and updates the
Expand Down Expand Up @@ -328,7 +328,7 @@ def load_pretrained(self, pretrain_type, device):
)
self.load_state_dict(pretrained_state_dict)

def forward(self, x, edge_index, edge_attr):
def forward(self, x: torch.Tensor, edge_index: torch.Tensor, edge_attr: torch.Tensor) -> torch.Tensor:
"""
Forward function of the GNN class that takes a PyTorch geometric
representation of a molecule or a batch of molecules
Expand Down