分子骨格 (molecular framework)

祇園祭。周辺には山鉾が配置され、多くの人で賑わっているところです。
山鉾の骨格には釘を一切用いず、荒縄で木材を丁寧に結って組み立てます(縄がらみと呼びます)。
直感的に強度が心配になりますが、実はそのほうが靭やかで強い構造になるらしく、
特に、辻回しで方向転換するときに、うまく軸木から力を逃がすことができるそうです。
山鉾を見るとき、ついつい、緞通や西陣織などの外側の装飾に目を奪われがちですが、
内側の基本的な骨格にも、伝統技術が隠されているのですね。


さて、話が飛びますが、化学構造にも骨格が定義されるのでしょうか?
装飾品たる官能基以外の部分が骨格? じゃあ、フェニル基はどうなん? などと考えこんでしまいます。
実際のところ、privileged structure, scaffold structure, core structureなどと名称が混在していて、
あまつさえ、いずれも普遍的/汎用的な定義は存在しないようなのです。
それはおそらく、化学構造の複雑さ、多様さに起因しているのでしょう。


Bemisらは、鎖状構造(いわゆるリンカー)で繋がった環構造(ベンゼン環やピリジン環など)を
「molecular framework (分子骨格)」と定義しました*1
たとえば、以下の構造(ピオグリタゾン)の場合、
赤い3つの環と青い2本の鎖が骨格だ、ということです。

これもひとつの答えなのだと思います。


それでは、入力したSMILESファイルからからmolecular frameworkを見つけ、出力してみましょう。
ここでは、環構造(縮合環を含む)を1つ未満しか持たないときは、何も出力しません。



/* framework.java */

import java.io.*;
import java.util.*;

import org.openscience.cdk.interfaces.*;
import org.openscience.cdk.io.iterator.IteratingSMILESReader;
import org.openscience.cdk.interfaces.IMolecule;
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;

import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.graph.ConnectivityChecker;
import org.openscience.cdk.graph.PathTools;
import org.openscience.cdk.graph.SpanningTree;
import org.openscience.cdk.ringsearch.AllRingsFinder;
import org.openscience.cdk.smiles.SmilesGenerator;

class framework {

private static final String IS_SIDECHAIN_ATOM = "sidechain";
private static final String IS_LINKER_ATOM = "linker";

public static void main(String args[]){

if(args.length!=1){
System.err.println("fmf <smiles-file>");
System.exit(1);
}
FileInputStream fis = null;
IteratingSMILESReader isr = null;
try{
fis = new FileInputStream(new File(args[0]));
isr = new IteratingSMILESReader(fis);
} catch (Exception e) {
e.printStackTrace();
}

while( isr.hasNext() ){
IMolecule imol = (IMolecule)isr.next();
String id = (String)imol.getProperty("cdk:Title");

String smi_frame = "";

IAtomContainer atomContainer = (IAtomContainer) imol;
try {
// identify rings
AllRingsFinder arf = new AllRingsFinder(false);
arf.findAllRings(atomContainer);

for (IAtom atom : atomContainer.atoms()) {
atom.setProperty(IS_LINKER_ATOM, false);
atom.setProperty(IS_SIDECHAIN_ATOM, false);
}

markLinkers(atomContainer);
markSideChains(atomContainer);

IAtomContainer clone = removeSideChains(atomContainer);
if( hasframework(clone) ){
SmilesGenerator smilesGenerator = new SmilesGenerator(true);
smi_frame = smilesGenerator.createSMILES(clone);
}
} catch (Exception e1) {
e1.printStackTrace();
}

System.out.printf("%s\t%s\n", id, smi_frame);
}
}

static private IAtomContainer removeSideChains( IAtomContainer ac
) throws CDKException {
IAtomContainer clone;
try {
clone = (IAtomContainer) ac.clone();
} catch (CloneNotSupportedException exception) {
throw new CDKException("Error in clone" + exception.toString(), exception);
}
List<IAtom> atomsToDelete = new ArrayList<IAtom>();
for (IAtom atom : clone.atoms()) {
if (issidechain(atom)) atomsToDelete.add(atom);
}
for (IAtom anAtomsToDelete : atomsToDelete)
clone.removeAtomAndConnectedElectronContainers(anAtomsToDelete);
return (clone);
}

static private boolean isConnectedToRing(IAtomContainer ac, IAtom atom) {
List<IAtom> connectedAtoms = ac.getConnectedAtomsList(atom);
for (IAtom connectedAtom : connectedAtoms) {
if (connectedAtom.getFlag(CDKConstants.ISINRING)) return true;
}
return false;
}

static private void markLinkers(IAtomContainer atomContainer) {
// ring attachment
for (IAtom atom : atomContainer.atoms()) {
if (atom.getFlag(CDKConstants.ISINRING)) continue;
List<IAtom> conatoms = atomContainer.getConnectedAtomsList(atom);
if (conatoms.size() == 1) continue;
boolean allRingAtoms = true;
int nRingAtom = 0;
for (IAtom conatom : conatoms) {
if (conatom.getFlag(CDKConstants.ISINRING)) {
nRingAtom++;
}
}
if (nRingAtom >= 2) atom.setProperty(IS_LINKER_ATOM, true);
}

// now lets look at linker paths
for (IAtom atom1 : atomContainer.atoms()) {
if (atom1.getFlag(CDKConstants.ISINRING) ||
!isConnectedToRing(atomContainer, atom1)) continue;
for (IAtom atom2 : atomContainer.atoms()) {
if (atom2.getFlag(CDKConstants.ISINRING) ||
!isConnectedToRing(atomContainer, atom2)) continue;
List<List<IAtom>> paths = PathTools.getAllPaths(atomContainer,
atom1, atom2);
for (List<IAtom> path : paths) {
boolean allNonRing = true;
for (IAtom atom : path) {
if (atom.getFlag(CDKConstants.ISINRING)) {
allNonRing = false;
break;
}
}
if (allNonRing) { // mark them as linkers
for (IAtom atom : path) atom.setProperty(IS_LINKER_ATOM, true);
}
}
}
}
}

static private void markSideChains(IAtomContainer atomContainer) {
for (IAtom atom : atomContainer.atoms()) {
if (!isring(atom) && !islinker(atom))
atom.setProperty(IS_SIDECHAIN_ATOM, true);
}
}

static private boolean isring(IAtom atom) {
return atom.getFlag(CDKConstants.ISINRING);
}

static private boolean islinker(IAtom atom) {
Boolean o = (Boolean) atom.getProperty(IS_LINKER_ATOM);
return o != null && o;
}

static private boolean issidechain(IAtom atom) {
Boolean o = (Boolean) atom.getProperty(IS_SIDECHAIN_ATOM);
return o != null && o;
}

static private boolean islinker(IBond bond) {
return islinker(bond.getAtom(0)) || islinker(bond.getAtom(1));
}

static private boolean isZeroAtomLinker(IBond bond, IAtomContainer mol) {
boolean isRingBond = (new SpanningTree(mol)
).getCyclicFragmentsContainer().contains(bond);
return isring(bond.getAtom(0)) && isring(bond.getAtom(1)) && !isRingBond;
}

static private boolean hasframework(IAtomContainer atomContainer) {
boolean hasLinker = false;
boolean hasRing = false;
for (IAtom atom : atomContainer.atoms()) {
if (islinker(atom)) hasLinker = true;
if (isring(atom)) hasRing = true;
if (hasLinker && hasRing) break;
}
for (IBond bond : atomContainer.bonds()) {
if (isZeroAtomLinker(bond, atomContainer)) {
hasLinker = true;
break;
}
}
return hasLinker && hasRing;
}
}

cdk-1.3.12には、このmolecular frameworkを実装したMurckoFragmenterクラスがあります。
次回は、このクラスを使って記述子計算を行う予定です。

*1: Bemis and Murcko. The properties of known drugs. 1. Molecular Framework. J. Med. Chem. (1996) 39, 2887-2893 PubMed