RDkitを用いた分子構造生成１（A-B型）

# はじめに
BRICSによって生成したフラグメントを組み合わせることで新しい分子構造を生成する。([BRICSについて](https://chemoinformatics.hatenablog.com/entry/2021/01/06/132925))
以前は、[BRICSBuildによってランダムに構造を生成した](https://chemoinformatics.hatenablog.com/entry/2021/01/07/013246)が、今回は、二つのフラグメント（A,B）を結合させることで新たな分子（A-B）を生成する。

水溶解度データ（[金子研究室HPのデータセット](https://datachemeng.com/pythonassignment/)）に含まれる1290分子の構造をフラグメント化し構造生成に利用する。 

**今回のコードは明治大学金子先生の["structure_generator_based_on_r_group"](https://github.com/hkaneko1985/structure_generator_based_on_r_group/blob/master/structure_generator.py)を参考に（コードを改変して）作成しました。(金子先生ありがとうございます)**

# 前準備
###モジュールのインストール
```
conda install -c conda-forge rdkit
```

# コード

###モジュールのインポート
```python
import numpy as np
from rdkit import rdBase, Chem
from rdkit.Chem import AllChem, Draw, BRICS, Descriptors
from rdkit.ML.Descriptors import MoleculeDescriptors
```
   
### フラグメントの準備
- 二つのフラグメントをつなぐので、ダミーアトム（結合可能部位）を一つ持つ分子をピックアップする。
- 複雑な構造のフラグメント（分子量300以上）は削除する。

```python
suppl = Chem.SDMolSupplier('logSdataset1290_2d.sdf')
mols_list = [mol for mol in suppl if mol is not None]

#１290分子を分解してフラグメントを準備する
fragment_set = set()
for mol in mols_list:
    fragment = BRICS.BRICSDecompose(mol)
    fragment_set = fragment_set | fragment

fragment_smiles = list(fragment_set)


#フラグメントのリストから、ダミーアトムの数が一つのものを抽出する。
frag_1dummy_s = [smiles for smiles in fragment_smiles if smiles.count('*')==1]
frag_1dummy = np.array([Chem.MolFromSmiles(smiles) for smiles in frag_1dummy_s])


#複雑なフラグメント（分子量300以上）は削除する
descriptor_calc = MoleculeDescriptors.MolecularDescriptorCalculator(['MolWt'])
MolWt_list = np.array([descriptor_calc.CalcDescriptors(mol)[0] for mol in frag_1dummy])

frag_1dummy = frag_1dummy[np.where(MolWt_list<=300)]

print('number of fragment:',len(frag_1dummy))
# >>> number of fragment: 333

img = Draw.MolsToGridImage(frag_1dummy[:3], molsPerRow=3,legends=frag_1dummy_s[:3])
img.save('frag_1dummy.png')
img
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348376/fc97be2c-0d99-60bd-0662-250b2bab12d4.png)

  
## フラグメント二つから分子一つを作る

```python

#フラグメントを選ぶ（フラグメントの一つを主骨格(main_mol)とみなしてそこにフラグメント(fragment)を結合するテイ。
main_mol = frag_1dummy[0]
fragment = frag_1dummy[1]

bond_list = [Chem.rdchem.BondType.UNSPECIFIED, Chem.rdchem.BondType.SINGLE, Chem.rdchem.BondType.DOUBLE,
         Chem.rdchem.BondType.TRIPLE, Chem.rdchem.BondType.QUADRUPLE, Chem.rdchem.BondType.QUINTUPLE,
         Chem.rdchem.BondType.HEXTUPLE, Chem.rdchem.BondType.ONEANDAHALF, Chem.rdchem.BondType.TWOANDAHALF,
         Chem.rdchem.BondType.THREEANDAHALF, Chem.rdchem.BondType.FOURANDAHALF, Chem.rdchem.BondType.FIVEANDAHALF,
         Chem.rdchem.BondType.AROMATIC, Chem.rdchem.BondType.IONIC, Chem.rdchem.BondType.HYDROGEN,
         Chem.rdchem.BondType.THREECENTER, Chem.rdchem.BondType.DATIVEONE, Chem.rdchem.BondType.DATIVE,
         Chem.rdchem.BondType.DATIVEL, Chem.rdchem.BondType.DATIVER, Chem.rdchem.BondType.OTHER,
         Chem.rdchem.BondType.ZERO]

# main_mol の操作 =======================================================
# main_mol のadjacency matrixの作成と原子の情報を取得する
main_adjacency_matrix = Chem.rdmolops.GetAdjacencyMatrix(main_mol)

for bond in main_mol.GetBonds():
    main_adjacency_matrix[bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] = bond_list.index(bond.GetBondType())
    main_adjacency_matrix[bond.GetEndAtomIdx(), bond.GetBeginAtomIdx()] = bond_list.index(bond.GetBondType())
    
main_atoms = []
for atom in main_mol.GetAtoms():
    main_atoms.append(atom.GetSymbol())

# ダミーアトム（*）の位置を調べる
r_index_in_main_molecule_old = [index for index, atom in enumerate(main_atoms) if atom == '*']

# matrix,atom を並び替えて新しく分子を生成する。
for index, r_index in enumerate(r_index_in_main_molecule_old):
    modified_index = r_index - index
    atom = main_atoms.pop(modified_index)
    main_atoms.append(atom)
    tmp = main_adjacency_matrix[:, modified_index:modified_index + 1].copy()
    main_adjacency_matrix = np.delete(main_adjacency_matrix, modified_index, 1)
    main_adjacency_matrix = np.c_[main_adjacency_matrix, tmp]
    tmp = main_adjacency_matrix[modified_index:modified_index + 1, :].copy()
    main_adjacency_matrix = np.delete(main_adjacency_matrix, modified_index, 0)
    main_adjacency_matrix = np.r_[main_adjacency_matrix, tmp]

#新しく生成した分子でのダミーアトム(*)の位置を取得する
r_index_in_main_molecule_new = [index for index, atom in enumerate(main_atoms) if atom == '*']

#*がどの原子と結合しているか調べる。
r_bonded_atom_index_in_main_molecule = []
for number in r_index_in_main_molecule_new:
    r_bonded_atom_index_in_main_molecule.append(np.where(main_adjacency_matrix[number, :] != 0)[0][0])

#結合のタイプを調べる
r_bond_number_in_main_molecule = main_adjacency_matrix[
    r_index_in_main_molecule_new, r_bonded_atom_index_in_main_molecule]

# *原子を削除
main_adjacency_matrix = np.delete(main_adjacency_matrix, r_index_in_main_molecule_new, 0)
main_adjacency_matrix = np.delete(main_adjacency_matrix, r_index_in_main_molecule_new, 1)

for i in range(len(r_index_in_main_molecule_new)):
    main_atoms.remove('*')
main_size = main_adjacency_matrix.shape[0]
# Frag_1 の操作ここまで ========================================================

#Frag_2の操作ここまで===========================================================
r_number_in_molecule = 0

#ここから、主骨格の処理と同じ
fragment_adjacency_matrix = Chem.rdmolops.GetAdjacencyMatrix(fragment)

for bond in fragment.GetBonds():
    fragment_adjacency_matrix[bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] = bond_list.index(
        bond.GetBondType())
    fragment_adjacency_matrix[bond.GetEndAtomIdx(), bond.GetBeginAtomIdx()] = bond_list.index(
        bond.GetBondType())
fragment_atoms = []
for atom in fragment.GetAtoms():
    fragment_atoms.append(atom.GetSymbol())

# integrate adjacency matrix
r_index_in_fragment_molecule = fragment_atoms.index('*')

r_bonded_atom_index_in_fragment_molecule = \
    np.where(fragment_adjacency_matrix[r_index_in_fragment_molecule, :] != 0)[0][0]

# 後で * を削除するのでそのための調整（たぶん）
if r_bonded_atom_index_in_fragment_molecule > r_index_in_fragment_molecule:
    r_bonded_atom_index_in_fragment_molecule -= 1

fragment_atoms.remove('*')
fragment_adjacency_matrix = np.delete(fragment_adjacency_matrix, r_index_in_fragment_molecule, 0)
fragment_adjacency_matrix = np.delete(fragment_adjacency_matrix, r_index_in_fragment_molecule, 1)
#Frag_2の操作ここまで===========================================================

#Frag_1とFrag_2をつなげる=========================================
generated_molecule_atoms = main_atoms[:]
generated_adjacency_matrix = main_adjacency_matrix.copy()

#新たに生成する分子用のマトリックス作成
main_size = generated_adjacency_matrix.shape[0]
generated_adjacency_matrix = np.c_[generated_adjacency_matrix, np.zeros(
    [generated_adjacency_matrix.shape[0], fragment_adjacency_matrix.shape[0]], dtype='int32')]
generated_adjacency_matrix = np.r_[generated_adjacency_matrix, np.zeros(
    [fragment_adjacency_matrix.shape[0], generated_adjacency_matrix.shape[1]], dtype='int32')]

#マトリックスに結合のタイプを記述
generated_adjacency_matrix[r_bonded_atom_index_in_main_molecule[r_number_in_molecule], 
                           r_bonded_atom_index_in_fragment_molecule + main_size] = \
    r_bond_number_in_main_molecule[r_number_in_molecule]

generated_adjacency_matrix[r_bonded_atom_index_in_fragment_molecule + main_size, 
                           r_bonded_atom_index_in_main_molecule[r_number_in_molecule]] = \
    r_bond_number_in_main_molecule[r_number_in_molecule]

generated_adjacency_matrix[main_size:, main_size:] = fragment_adjacency_matrix
#フラグメントのマトリックスを入力(マトリックスの右下)
#Frag_1とFrag_2をつなげる=========================================

# integrate atoms
generated_molecule_atoms += fragment_atoms

# generate structures 
generated_molecule = Chem.RWMol()
atom_index = []

for atom_number in range(len(generated_molecule_atoms)):
    atom = Chem.Atom(generated_molecule_atoms[atom_number])
    molecular_index = generated_molecule.AddAtom(atom)
    atom_index.append(molecular_index)

for index_x, row_vector in enumerate(generated_adjacency_matrix):    
    for index_y, bond in enumerate(row_vector):      
        if index_y <= index_x:
            continue
        if bond == 0:
            continue
        else:
            generated_molecule.AddBond(atom_index[index_x], atom_index[index_y], bond_list[bond])

generated_molecule = generated_molecule.GetMol()
smiles = Chem.MolToSmiles(generated_molecule)

generated_mol = Chem.MolFromSmiles(smiles)


#作った分子を描画する
img = Draw.MolsToGridImage([main_mol,fragment,generated_mol],
                           molsPerRow=3,
                           legends=['main_mol','fragment','generated_mol'])
img.save('generated_mol.png')
img
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348376/13adab3f-8b81-9bff-a326-ae5882e431bb.png)
  
  
#関数化してみた

```python
def structure_generator(main_mol,fragment):

    bond_list = [Chem.rdchem.BondType.UNSPECIFIED, Chem.rdchem.BondType.SINGLE, Chem.rdchem.BondType.DOUBLE,
             Chem.rdchem.BondType.TRIPLE, Chem.rdchem.BondType.QUADRUPLE, Chem.rdchem.BondType.QUINTUPLE,
             Chem.rdchem.BondType.HEXTUPLE, Chem.rdchem.BondType.ONEANDAHALF, Chem.rdchem.BondType.TWOANDAHALF,
             Chem.rdchem.BondType.THREEANDAHALF, Chem.rdchem.BondType.FOURANDAHALF, Chem.rdchem.BondType.FIVEANDAHALF,
             Chem.rdchem.BondType.AROMATIC, Chem.rdchem.BondType.IONIC, Chem.rdchem.BondType.HYDROGEN,
             Chem.rdchem.BondType.THREECENTER, Chem.rdchem.BondType.DATIVEONE, Chem.rdchem.BondType.DATIVE,
             Chem.rdchem.BondType.DATIVEL, Chem.rdchem.BondType.DATIVER, Chem.rdchem.BondType.OTHER,
             Chem.rdchem.BondType.ZERO]

    # main_mol の操作 =======================================================
    # main_mol のadjacency matrixの作成と原子の情報を取得する
    main_adjacency_matrix = Chem.rdmolops.GetAdjacencyMatrix(main_mol)

    for bond in main_mol.GetBonds():
        main_adjacency_matrix[bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] = bond_list.index(bond.GetBondType())
        main_adjacency_matrix[bond.GetEndAtomIdx(), bond.GetBeginAtomIdx()] = bond_list.index(bond.GetBondType())

    main_atoms = []
    for atom in main_mol.GetAtoms():
        main_atoms.append(atom.GetSymbol())

    # ダミーアトム（*）の位置を調べる
    r_index_in_main_molecule_old = [index for index, atom in enumerate(main_atoms) if atom == '*']

    # matrix,atom を並び替えて新しく分子を生成する。
    for index, r_index in enumerate(r_index_in_main_molecule_old):
        modified_index = r_index - index
        atom = main_atoms.pop(modified_index)
        main_atoms.append(atom)
        tmp = main_adjacency_matrix[:, modified_index:modified_index + 1].copy()
        main_adjacency_matrix = np.delete(main_adjacency_matrix, modified_index, 1)
        main_adjacency_matrix = np.c_[main_adjacency_matrix, tmp]
        tmp = main_adjacency_matrix[modified_index:modified_index + 1, :].copy()
        main_adjacency_matrix = np.delete(main_adjacency_matrix, modified_index, 0)
        main_adjacency_matrix = np.r_[main_adjacency_matrix, tmp]

    #新しく生成した分子でのダミーアトム(*)の位置を取得する
    r_index_in_main_molecule_new = [index for index, atom in enumerate(main_atoms) if atom == '*']

    #*がどの原子と結合しているか調べる。
    r_bonded_atom_index_in_main_molecule = []
    for number in r_index_in_main_molecule_new:
        r_bonded_atom_index_in_main_molecule.append(np.where(main_adjacency_matrix[number, :] != 0)[0][0])

    #結合のタイプを調べる
    r_bond_number_in_main_molecule = main_adjacency_matrix[
        r_index_in_main_molecule_new, r_bonded_atom_index_in_main_molecule]

    # *原子を削除
    main_adjacency_matrix = np.delete(main_adjacency_matrix, r_index_in_main_molecule_new, 0)
    main_adjacency_matrix = np.delete(main_adjacency_matrix, r_index_in_main_molecule_new, 1)

    for i in range(len(r_index_in_main_molecule_new)):
        main_atoms.remove('*')
    main_size = main_adjacency_matrix.shape[0]
    # Frag_1 の操作ここまで =======================================================

    #Frag_2の操作ここまで===========================================================
    r_number_in_molecule = 0

    #ここから、主骨格の処理と同じ
    fragment_adjacency_matrix = Chem.rdmolops.GetAdjacencyMatrix(fragment)

    for bond in fragment.GetBonds():
        fragment_adjacency_matrix[bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] = bond_list.index(
            bond.GetBondType())
        fragment_adjacency_matrix[bond.GetEndAtomIdx(), bond.GetBeginAtomIdx()] = bond_list.index(
            bond.GetBondType())
    fragment_atoms = []
    for atom in fragment.GetAtoms():
        fragment_atoms.append(atom.GetSymbol())

    # integrate adjacency matrix
    r_index_in_fragment_molecule = fragment_atoms.index('*')

    r_bonded_atom_index_in_fragment_molecule = \
        np.where(fragment_adjacency_matrix[r_index_in_fragment_molecule, :] != 0)[0][0]

    # 後で * を削除するのでそのための調整（たぶん）
    if r_bonded_atom_index_in_fragment_molecule > r_index_in_fragment_molecule:
        r_bonded_atom_index_in_fragment_molecule -= 1

    fragment_atoms.remove('*')
    fragment_adjacency_matrix = np.delete(fragment_adjacency_matrix, r_index_in_fragment_molecule, 0)
    fragment_adjacency_matrix = np.delete(fragment_adjacency_matrix, r_index_in_fragment_molecule, 1)
    #Frag_2の操作ここまで===========================================================

    #Frag_1とFrag_2をつなげる=========================================
    generated_molecule_atoms = main_atoms[:]
    generated_adjacency_matrix = main_adjacency_matrix.copy()

    #新たに生成する分子用のマトリックス作成
    main_size = generated_adjacency_matrix.shape[0]
    generated_adjacency_matrix = np.c_[generated_adjacency_matrix, np.zeros(
        [generated_adjacency_matrix.shape[0], fragment_adjacency_matrix.shape[0]], dtype='int32')]
    generated_adjacency_matrix = np.r_[generated_adjacency_matrix, np.zeros(
        [fragment_adjacency_matrix.shape[0], generated_adjacency_matrix.shape[1]], dtype='int32')]

    #マトリックスに結合のタイプを記述
    generated_adjacency_matrix[r_bonded_atom_index_in_main_molecule[r_number_in_molecule], 
                               r_bonded_atom_index_in_fragment_molecule + main_size] = \
        r_bond_number_in_main_molecule[r_number_in_molecule]

    generated_adjacency_matrix[r_bonded_atom_index_in_fragment_molecule + main_size, 
                               r_bonded_atom_index_in_main_molecule[r_number_in_molecule]] = \
        r_bond_number_in_main_molecule[r_number_in_molecule]

    generated_adjacency_matrix[main_size:, main_size:] = fragment_adjacency_matrix
    #フラグメントのマトリックスを入力(マトリックスの右下)
    #Frag_1とFrag_2をつなげる=========================================

    # integrate atoms
    generated_molecule_atoms += fragment_atoms

    # generate structures 
    generated_molecule = Chem.RWMol()
    atom_index = []

    for atom_number in range(len(generated_molecule_atoms)):
        atom = Chem.Atom(generated_molecule_atoms[atom_number])
        molecular_index = generated_molecule.AddAtom(atom)
        atom_index.append(molecular_index)

    for index_x, row_vector in enumerate(generated_adjacency_matrix):    
        for index_y, bond in enumerate(row_vector):      
            if index_y <= index_x:
                continue
            if bond == 0:
                continue
            else:
                generated_molecule.AddBond(atom_index[index_x], atom_index[index_y], bond_list[bond])

    generated_molecule = generated_molecule.GetMol()
    smiles = Chem.MolToSmiles(generated_molecule)

    generated_mol = Chem.MolFromSmiles(smiles)

    return generated_mol
```

## 関数を使い、ランダムに構造を生成してみる

```python
frag_1 = frag_1dummy[np.random.randint(len(frag_1dummy))]
frag_2 = frag_1dummy[np.random.randint(len(frag_1dummy))]

generated_mol = structure_generator(frag_1,frag_2)
#作った分子を描画する
img = Draw.MolsToGridImage([frag_1,frag_2,generated_mol],
                           molsPerRow=3,
                           legends=['main_mol','fragment','generated_mol'])
img.save('generated_mol_random.png')
img
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348376/9b63141a-f194-627f-fa65-5da559839795.png)


# 参考
- [hkaneko1985/structure_generator_based_on_r_group](https://github.com/hkaneko1985/structure_generator_based_on_r_group/blob/master/structure_generator.py)