SMILES化学式解析

解析和匹配

一,建立对象
(一)键
package com.Demo2;

import java.util.ArrayList;

public class ChemicalKey {
    //键的名称
    private String key;
    //键的指向的对象
    private ArrayList<ChemicalElement> chemicalElements;
    public static String[] allKeys;
    //构造函数
    public ChemicalKey(String key) {
        this.key = key;
        chemicalElements = new ArrayList<ChemicalElement>(2);
    }

    /**键,两边的元素
     * @param
     */
    public void setTarget(ChemicalElement target1,ChemicalElement target2) {
        try{
            chemicalElements.add(target1);
            chemicalElements.add(target2);
        }catch (Exception e){
            System.out.println("键连接的元素超过2个了!");
        }
    }
    //获得键两边元素
    public ArrayList<ChemicalElement> getChemicalElements() {
        return chemicalElements;
    }
    public void print(){
        for(int i=0;i<2;i++){
            System.out.print(chemicalElements.get(i).getLabel());
            System.out.print('-');
        }
        System.out.println();
    }
   //把一些常见的键,存起来,String形式,不是对象
    private static void  SetterAllKeys(){
        allKeys = new String[6];
        allKeys[0]=".";
        allKeys[1]="-";
        allKeys[2]="=";
        allKeys[3]="#";
        allKeys[4]="/";
        allKeys[5]="\\\\";
    }

    public String getKey() {
        return key;
    }

    /**获得所有的Key字符
     * @return
     */
    public static String[] getAllKeys(){
        SetterAllKeys();
        return allKeys;
    }
}
(二)元素

每个元素都是独立的对象,元素有属性:1,元素名字 label 2,元素的临近的元素environments_2 3,临近元素相应的键keys_2
其他为了方便设置的 4,元素的整体序号 mark 5 所有元素的符号,string类型,6,断键
缺少:离子键的带的电荷量

package com.Demo2;

import java.util.ArrayList;

public class ChemicalElement {
    //元素名称
    private String label;
    //元素的整体序号
    private int mark;
    //周围的元素和相应的键

    private ArrayList<ChemicalElement> environments_2;

    private ChemicalKey[] Keys;
    private ArrayList<ChemicalKey> Keys_2;
    //是元素的个数
    public int count=0;
    private static String[] allElement;
    //断开的键的类型
    private ChemicalKey disconnectKey;


    public ChemicalElement(String label, int mark) {
        this.label = label;
        this.mark = mark;
        this.Keys = new ChemicalKey[4];
        this.Keys_2  =new ArrayList<>();
        this.environments_2 = new ArrayList<>();
    }




    //设置断键
    public void setDisconnectKey(ChemicalKey disconnectKey) {
        this.disconnectKey = disconnectKey;
    }
    //设置断键的数字,这里假设一个原SMILES表示最多一个断键
    public void setMark(int mark) {
        this.mark = mark;
    }
    /**添加key的方法,同时把键连接的元素也添加进去
     * @param key
     */
    public void addToKeys(ChemicalKey key,ChemicalElement ce){
        Keys_2.add(key);
        this.environments_2.add(ce);

    }
    //返回,list,周围的元素
    public ArrayList<ChemicalElement> getEnvironments_2() {
        return environments_2;
    }

    /**返回所有的键
     * @return
     */
    public ArrayList<ChemicalKey> getKeys_2() {
        return Keys_2;
    }

//获得断键的序号,默认为0
    public int getMark() {
        return mark;
    }
//获得断键信息
    public ChemicalKey getDisconnectKey() {
        return disconnectKey;
    }
    /**获得元素名称
     * @return
     */
    public String getLabel() {
        return new String(label);
    }
    //获得与之相连元素的键对象
    public ChemicalKey getKeyy(ChemicalElement ce){

        for(ChemicalKey ck:Keys_2){
            if(ck.getChemicalElements().contains(ce)){//某个键,包含这个元素,返回这个键的对象
                System.out.println("!!!!!检测到元素在键:"+ck.getKey()+"中");
                return ck;
            }
        }
        return null;
    }


    public static void main(String[] args) {
        ChemicalElement c1 = new ChemicalElement("H",0);

    }

    private static void SetAllElement(){
        allElement = new String[100];
        allElement[0]="C";
        allElement[1]="N";
        allElement[2]="O";
        allElement[3]="F";
        allElement[4]="Br";
        allElement[5]="Cl";
        allElement[6]="H";
        allElement[7]="S";
    }
    public static String[] getAllElement() {
        SetAllElement();
        return allElement;
    }

}
(三)化学式

化学式,所有的元素(键信息在元素中)

package com.Demo2;

import java.util.ArrayList;

public class ChemicalFormula {
    private ArrayList<ChemicalElement> Formula;

    /**
     * @return
     */
    public ArrayList<ChemicalElement> getFormula() {
        return Formula;
    }

    /**
     * @param formula
     */
    public void setFormula(ArrayList<ChemicalElement> formula) {
        Formula = formula;
    }
}
二,解析

解析没有达到的一些地方:

  • Aromaticity 芳香环的结构识别。小写的字母表示 未识别
  • 离子键等带 [] 这个结构的
  • @符号,立体结构
  • 断键合并,默认是没有重复数字的如1212 ,像1111这种不行。
    简单说,本程序识别,仅仅是带括号,大些字母的那种
Input:   String  s, //待解析的字符串
       Int  count, //当前解析的位置
Parse(S,Position)   
While(count<s长度){
判断是不是断开的键——数字  
判断是不是键——-=#.\/         
判断是不是元素——CNOFSBrCl[]++
判断是不是括号
   Parse(s',position')
   (出现括号“[”,将括号当成规模小一点的子问题,仍用这个方法。因此,这个方法要返回头元素,或者第一个元素;
出现括号“]”,终结条件,返回头元素)
}

在这里插入图片描述

package com.Demo2;

import java.util.ArrayList;

public class ParseChain {
    /**
     * 待解析的字符串对象
     */
    private String smiles;

    /**
     * 解析字符串的位置标记
     */
    private int position;


    /**
     * 解析后存放的位置
     * cmf:化学式存放
     * MyElements_2 以前的list存放
     */
    private ArrayList<ChemicalElement> MyElements_2;
    private ChemicalFormula cmf;

    /**MyElement 要删除的
     * 构造函数
     * @param smiles
     */
    public ParseChain(String smiles) {
        this.smiles = smiles.toUpperCase();
        String[] smilesList =this.smiles.split("");
        MyElements_2 = new ArrayList<ChemicalElement>();
        cmf = new ChemicalFormula();
    }

    
    public ArrayList<ChemicalElement> getMyElements_2() {
        return MyElements_2;
    }

    public ChemicalFormula getCmf() {
        return cmf;
    }

    public ChemicalElement Parsing(){
        String[] smilesList =this.smiles.split(""); //将字符串转成数组
        //第一步,完成图示解C步骤
        ChemicalElement st = subParsing(smilesList,null,null);
        //第二步,从C步骤,完成B步骤,对数字相同的进行拼凑;断键合并
        Combine();
        cmf.setFormula(MyElements_2);  //解析完,将元素存到化学键中
        return st;
    }
    private ChemicalElement subParsing(String[] smileList,ChemicalElement formerElement,ChemicalKey formerKey){
        //头元素,记录信息
        ChemicalElement start = null;
        while(position<smileList.length){
            System.out.print(smileList[position]);
            //是否是数字,表示断键
            if((position<smileList.length) && Character.isDigit(smileList[position].charAt(0))){
                formerElement.setMark((int)smileList[position].charAt(0));  //旧-将数字加进去
                formerElement.setDisconnectKey(formerKey);  //将断键的类型加上去
                formerKey = new ChemicalKey("-");            //将标记键置为默认
                position++;
            }
            //是键
            else if(isKey(smileList[position])){
                formerKey = new ChemicalKey(smileList[position]);//将键对象保存
                position++;
            }
            //是元素
            else if(isElement(smileList[position])){
                ChemicalElement current=null;
                if(smileList[position].equals("[")) {  //离子情形
                    ArrayList<String> Ion = new ArrayList<>();
                    for (int i = position; i < smileList.length; i++) {
                        if (smileList[position].equals("]")) {  //结束的情形
                            position++;
                            break;}
                        if (smileList[position].equals("[") || smileList[position].equals("+") || smileList[position].toUpperCase().equals("H")) {
                            continue;
                        }
                        Ion.add(smileList[position]);
                    }
                    current = new ChemicalElement(Ion.toString(), 0);
                }else {
                    current = new ChemicalElement(smileList[position], 0); //旧-将元素添加进去
                }
                MyElements_2.add(current);         //新-将元素添加到MyElements_2

                if(start==null){    //第一个元素
                    start=current;
                }
                if(formerElement==null){   //旧-前面是空的,第一个元素情形;此时,键也应该是空的
                    formerElement = current;
                    formerKey = new ChemicalKey("-");
                }else {

                    //新 添加元素
                    formerKey.setTarget(current,formerElement); //键 ---(m,n)
                    formerElement.addToKeys(formerKey,current);  //元素---添加键
                    current.addToKeys(formerKey,formerElement);

                    //旧-更新前一个元素;更新键值
                    formerElement = current;
                    formerKey = new ChemicalKey("-");
                }
                position++;
            }
               //括号内() 迭代,返回头元素
            else if(smileList[position].equals("(")){
                //旧-此时第一个元素应该被外面的吸纳,其他的循环就好;此时前置的元素依然不能变,即使出现了)符号,这个也是不能变的。
                position++;
                //判断后面的元素是不是键,如果是,要提前存起来,更新。
                if(isKey(smileList[position])){
                    formerKey = new ChemicalKey(smileList[position]);
                }
                ChemicalElement ce =  subParsing(smileList,null,null);
                if(formerElement!=null){  //括号前面的部分处理

                    //新的添加方式
                    formerKey.setTarget(ce,formerElement);
                    formerElement.addToKeys(formerKey,ce);
                    ce.addToKeys(formerKey,formerElement);
                }else{
                    formerElement = ce;
                    formerKey = new ChemicalKey("-");
                }
                //括号后面部分的处理
                formerKey = new ChemicalKey("-");
                position++;
            }
            else if(smileList[position].equals(")")){  //这里不对位置进行操作 留在109处理
                // 但是要对formerkey做处理 留在109 循环出来后
                return start;
            }
        }
        return start;
    }

    //判断是否是键
    String[] allKeys = ChemicalKey.getAllKeys();
    public boolean isKey(String element){
        for(String c:allKeys){
            if(element.equals(c)){
                //  System.out.println("是键");
                return true;
            }
        }
        return false;
    }
    //判断是否是元素  
    //两位的元素没搞,比如Br Cl
    String[] elements = ChemicalElement.getAllElement();
    public boolean isElement(String element){
        for(String s:elements){
            if(element.equals(s)){
                //   System.out.println("是元素");
                return true;
            }
            if(element.equals("[")){
                return true;
            }
        }
        return false;
    }

    //将断开的键合起来
    // 断键信息1111 这种形式标记的 没搞
    private void Combine(){
        int maxNumber =0;
        for(ChemicalElement ce :MyElements_2){
            if(ce.getMark()>maxNumber)
                maxNumber =ce.getMark();
        }
        for(int i=0;i<MyElements_2.size();i++){
            if(MyElements_2.get(i).getMark()!=0){
                for(int j=i+1;j<MyElements_2.size();j++){
                    if(MyElements_2.get(i).getMark()==MyElements_2.get(j).getMark()){
                        MyElements_2.get(i).getDisconnectKey().setTarget(MyElements_2.get(i),MyElements_2.get(j));
                        MyElements_2.get(i).addToKeys(MyElements_2.get(i).getDisconnectKey(),MyElements_2.get(j));
                        MyElements_2.get(j).addToKeys(MyElements_2.get(i).getDisconnectKey(),MyElements_2.get(i));
                        break;
                    }
                }
            }
        }

    }
}

三,匹配

经过查资料,这是一个NP问题,属于图的同构问题。采用一个类似深度优先遍历加回溯的方案,算法叫VF算法。算法流程:
在这里插入图片描述
中文文章解释:(百度吧)
1,SMILES表达式的子结构关系检测算法_彭彬
2,VF算法在化学结构检索中的应用_李琰
3,基于VF2算法的分子二维子结构检索_李欣
英文原文:
1,An_Improved_Algorithm_for_Matching_Large_Graphs
2,(Sub)Graph Isomorphism Algorithm for Matching Large Graphs Luigi P. Cordella, Pasquale Foggia, Carlo Sansone,and Mario Vento
过程主要设计的概念:

  • SSR 状态空间。上面说到这是一个图问题,所以深度遍历过程中会有一个解空间树。这个SSR状态空间,就是真正解的一个子集空间,用来记录路径的。
  • 候选状态集。 当进行到某个元素,这个候选状态集就是:接下来的所有的可选路径。
  • Feasibility Rules 。回溯的条件,对于问题,不满足一定条件,就回溯。
package com.Demo2;

import java.util.*;

public class VFMatch {
    //query和db图   dbG=databaseGraph
    private ChemicalFormula quG;
    private ChemicalFormula dbG;
    //Ms用于存放空间状态点,也应该是个Map才对
    //由于匹配过程,一一对应,不会出现一对多,所以mapde 键是元素是可以的
    private Map<ChemicalElement,ChemicalElement> Ms;

    public VFMatch(ChemicalFormula quG, ChemicalFormula dbG) {
        this.quG = quG;
        this.dbG = dbG;
    }

    /**
     * @param state  临时的态
     * @param quG     query
     * @param dbG     dbG
     * @return         boolean
     */
    public boolean Match(HashMap<ChemicalElement,ChemicalElement> state, ChemicalFormula quG, ChemicalFormula dbG){
        boolean flag = false;
        //Mqu 已经在state中的query元素  Mdb 已经在state中的db元素
        Set<ChemicalElement> quG_Mid0 = state.keySet();
        ArrayList<ChemicalElement> Mqu = new ArrayList<>(quG_Mid0);
        Collection<ChemicalElement> dbG_Mid0 = state.values();
        ArrayList<ChemicalElement> Mdb= new ArrayList<>(dbG_Mid0);
        //IF M(S)cover all the nodes of G2
        if(Mqu.size()!=0 && Mqu.size()==quG.getFormula().size()){
                System.out.println("到了最后+++++++++++++++++++++++++++++++++++++");
                flag =  true;
        }else{
            //compute the candidate of inclusion of Ms
            Map<Integer,ArrayList<ChemicalElement>> P = new HashMap<>();
            CandidateP(P,quG,dbG,Mqu,Mdb);
            System.out.println("计算完成候选的结果");
            Set<Integer> entry = P.keySet();
            //Foreach p in P:
            for(int number:entry){
                ChemicalElement que = P.get(number).get(0); //query
                ChemicalElement dbe = P.get(number).get(1);  //db
                //IF the feasibility rules succeed for inclusion of p in Ms
                if(FeasibilityRules(que,dbe,Mqu,Mdb,state)){
                    //compute the state s'
                    state.put(que,dbe);  //添加进去
                    //call match(s')
                    flag = Match(state,quG,dbG);
                    state.remove(que);  //回溯,要删除之前的状态
                }
                if(flag==true){ 
                    break;//找到一个解,就返回解,不再探索
                }
            }
            //从这里出来,就是说明没有那走到最后,所以返回
        }
        return flag;
    }


    /**搜索的空间P Candidate Pair Set
     * @param result 所有可能性集合容器
     * @param quG  query
     * @param bgG  database
     * @param Mqu  M_query
     * @param Mdb  M_database
     * @return
     * <1,<C,N>>  <2,<C,C>> ...
     */
    public void CandidateP(Map<Integer,ArrayList<ChemicalElement>> result, ChemicalFormula quG, ChemicalFormula bgG,
                                                           ArrayList<ChemicalElement> Mqu, ArrayList<ChemicalElement> Mdb){
        int count=0;
        for(ChemicalElement ce:quG.getFormula()){
            if(!Mqu.contains(ce)) {  //query元素没被选的元素
                for (ChemicalElement qe : bgG.getFormula()) {
                    if(!Mdb.contains(qe)){ //db元素,没被选的元素
                        System.out.print(ce.getLabel()+"-"+qe.getLabel()+" ");
                        ArrayList<ChemicalElement> res = new ArrayList<>();
                        res.add(ce);
                        res.add(qe);
                        result.put(count,res);
                        count++;
                    }
                }
            }
        }
    }

    /**
     * @param quG query
     * @param dbG dataBase
     * @param Mqu  已经加入的结点集合
     * @param Mdb
     * @param match 已经匹配好的对象
     * @return
     */
    public boolean FeasibilityRules(ChemicalElement quG,ChemicalElement dbG,ArrayList<ChemicalElement> Mqu,ArrayList<ChemicalElement> Mdb,
                                    Map<ChemicalElement,ChemicalElement> match){

        if(!quG.getLabel().equals(dbG.getLabel())){       //两个元素名称是否一致
            return  false;
        }
        if(quG.getEnvironments_2().size()>dbG.getEnvironments_2().size()){  //quG的度要小于等于dbG的度
            return false;
        }
        if(Mqu.size()==0){   //如果是第一对元素,
            System.out.println("   是第一个元素");
            return true;
        }
        //接下来检测,M中是否已经有跟quG相连的元素,如果相连,必须满足条件:
        //1)quG_vID和dbG_vID与已经match的那些节点对中的【至少】一对(quVid,dbVid)分别相邻(quG_vID和dbG_vID分别是已经match的节点quVid和dbVid的“neighbor节点”)
        //2)如果存在多个相邻对(quVid,dbVid),则必须要求【所有的】邻接边对( edge(quG_vID,quVid), edge(dbG_vID,dbVid) )的label一样

        for(ChemicalElement quG_Mid:Mqu){
            if(quG_Mid.getEnvironments_2().contains(quG)){ //quG_Mid是quG的连接元素  
                ChemicalElement dbG_Mid = match.get(quG_Mid);  //映射的元素
                if(!dbG_Mid.getEnvironments_2().contains(dbG)){  //映射元素dbG_Mid与dbG之间没有连接
                    return false;
                }
                /***
                 * 下面是需要改正的,键对象创建的太多了。
                 * 需要把键当成元素的属性,目前是元素是键的属性,用一个map比较好
                 */
                if(!quG.getKeyy(quG_Mid).getKey().equals( dbG.getKeyy(dbG_Mid).getKey())){  //同一个键对象
                    return false;
                }
            }
        }
        //从这里出来,
        //1)可能是不存在与Mqu相连接的情况,因此 return true
        //2) 存在于Mqu相连接的情况,且验证符合
        return true;
    }
}

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值