com.actelion.research.chem.reaction.mapping.ChemicalRule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openchemlib Show documentation
Show all versions of openchemlib Show documentation
Open Source Chemistry Library
/*
* Copyright (c) 1997 - 2016
* Actelion Pharmaceuticals Ltd.
* Gewerbestrasse 16
* CH-4123 Allschwil, Switzerland
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Thomas Sander
*/
package com.actelion.research.chem.reaction.mapping;
import com.actelion.research.chem.Molecule;
import com.actelion.research.chem.StereoMolecule;
import com.actelion.research.chem.reaction.Reaction;
import com.actelion.research.chem.reaction.ReactionEncoder;
import com.actelion.research.util.SortedList;
import java.util.Arrays;
/**
* A ChemicalRule is basically a chemical reaction (transformation) defined by a reaction substructure
* and a product substructure with full stoichiometry and completely mapped atoms.
* ChemicalRules are used the following way:
* - a substructure search locates all matches of the rule's reactant structure in the to-be-mapped reactant
* - for every match the transformation of the ChemicalRule is applied to the to-be-mapped reactant
* - the modified reactant is similarity-graph-mapped with the original product and scored
* - the score of the unmodified reaction is compared to all modified reaction scores considering the ChemicalRule's score delta
* - the best scoring mapping is taken as final mapping
* In order to facilitate an efficient application of the rule to any query reactant, the ChemicalRule object
* maintains an array of ChemicalRuleBond objects, which describe those bonds that need to be changed,
* created, or broken in the query reactant. A
*/
public class ChemicalRule {
private String mName,mIDCode;
private float mPanalty;
private StereoMolecule mReactant,mProduct;
private ChemicalRuleBond[] mRuleBonds;
private int[] mInvertedTHParity;
private int[] mReactantAtomSymmetryConstraint;
public ChemicalRule(String name, String idcode, float panalty) {
mName = name;
mIDCode = idcode;
mPanalty = panalty;
}
public void initialize() {
Reaction rxn = ReactionEncoder.decode(mIDCode, false);
mReactant = rxn.getReactant(0);
mProduct = rxn.getProduct(0);
mReactant.ensureHelperArrays(Molecule.cHelperNeighbours);
mProduct.ensureHelperArrays(Molecule.cHelperSymmetrySimple);
// key: lower bondAtom mapNo, higher bondAtom mapNo
// value: reactantAtom1, reactantAtom2, productBondOrder
SortedList bondList = new SortedList<>();
int[] mapNoToReactantAtom = new int[mReactant.getAtoms()+1];
mapNoToReactantAtom[0] = -1; // to cause exceptions in case of faulty logic
for (int atom=0; atom mapNoToProduct[connMapNo2]) ^ (connAtom1 > connAtom2))
inversion = !inversion;
}
}
}
return inversion;
}
private void addInvertedParityAtom(int atom) {
mInvertedTHParity = Arrays.copyOf(mInvertedTHParity, mInvertedTHParity.length+1);
mInvertedTHParity[mInvertedTHParity.length-1] = atom;
}
public void apply(StereoMolecule reactant, int[] match) {
reactant.ensureHelperArrays(Molecule.cHelperNeighbours);
for (ChemicalRuleBond ruleBond:mRuleBonds) {
int reactantAtom1 = match[ruleBond.atom1];
int reactantAtom2 = match[ruleBond.atom2];
int reactantBond = reactant.getBond(reactantAtom1, reactantAtom2);
if (reactantBond == -1)
reactant.addBond(reactantAtom1, reactantAtom2, ruleBond.newBondType);
else if (ruleBond.newBondType == ChemicalRuleBond.BOND_TYPE_DELETE)
reactant.markBondForDeletion(reactantBond);
else if (ruleBond.newBondType != ChemicalRuleBond.BOND_TYPE_KEEP_UNCHANGED)
reactant.setBondType(reactantBond, ruleBond.newBondType);
}
reactant.deleteMarkedAtomsAndBonds();
if (mInvertedTHParity.length != 0) {
reactant.ensureHelperArrays(Molecule.cHelperRings);
for (int atom:mInvertedTHParity) {
int reactantAtom = match[atom];
int reactantParity = reactant.getAtomParity(reactantAtom);
reactant.setAtomParity(reactantAtom, reactantParity == Molecule.cAtomParity1 ?
Molecule.cAtomParity2 : Molecule.cAtomParity1, false);
reactant.setStereoBondFromAtomParity(reactantAtom);
}
}
}
public StereoMolecule getReactant() {
return mReactant;
}
public StereoMolecule getProduct() {
return mProduct;
}
public String getName() {
return mName;
}
public float getPanalty() {
return mPanalty;
}
/**
* If the rule's reactant matches the real reactant multiple times,
* then some of these matches may be symmetrically equivalent. To avoid building and
* scoring redundant mapping graphs, these should be sorted out early. Reasons for
* redundant matches may be:
* - if the rule reactant is one fragment, this may be symmetrical Cn or Dn
* - the rule reactant may contain multiple equivalent fragments, e.g. metathese
* - matching atoms in the real reactant my be symmetrical
* Otherwise, there certain causes may exist, that break these symmetries:
* - a symmetrical rule fragment must not considered symmetrical, if it doesn't react
* symmetrically, i.e. if its matching rule product atoms are not equivalent anymore.
* - in case of multiple symmetrical fragments in the rule's reactant (e.g. metathese),
* inverted/rotated individual fragment matches cause different products, that means
* that the relative match orientation of multiple symmetrical fragments breaks symmetry,
* if the real matching atoms are not equivalent.
* This method calculates symmetry breaking values using these two reasons to be passed to
* the reactant rule substructure searcher.
*/
private void calculateReactantAtomSymmetryConstraints(int[] mapNoToReactantAtom) {
// break symmetries because of un-symmetrical rule products
mReactantAtomSymmetryConstraint = new int[mReactant.getAtoms()];
for (int atom=0; atom 1) {
int[] atomIndex = new int[fragmentCount];
for (int atom=0; atom {
static final int BOND_TYPE_KEEP_UNCHANGED = -2;
static final int BOND_TYPE_DELETE = -1;
int atom1,atom2,mapNo1,mapNo2,newBondType;
public ChemicalRuleBond(int atom1, int atom2, int mapNo1, int mapNo2, int newBondType) {
if (mapNo1 < mapNo2) {
this.atom1 = atom1;
this.atom2 = atom2;
this.mapNo1 = mapNo1;
this.mapNo2 = mapNo2;
}
else {
this.atom1 = atom2;
this.atom2 = atom1;
this.mapNo1 = mapNo2;
this.mapNo2 = mapNo1;
}
this.newBondType = newBondType;
}
@Override
public int compareTo(ChemicalRuleBond crb) {
if (mapNo1< crb.mapNo1)
return -1;
if (mapNo1> crb.mapNo1)
return 1;
if (mapNo2< crb.mapNo2)
return -1;
if (mapNo2> crb.mapNo2)
return 1;
return 0;
}
public void setNewBondType(int newBondType) {
this.newBondType = newBondType;
}
}