|
| 1 | +/* |
| 2 | + * Copyright (C) 2007-2026 Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>. |
| 3 | + * |
| 4 | + * This library is free software; you can redistribute it and/or |
| 5 | + * modify it under the terms of the GNU Lesser General Public |
| 6 | + * License as published by the Free Software Foundation; either |
| 7 | + * version 2.1 of the License, or (at your option) any later version. |
| 8 | + * |
| 9 | + * This library is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | + * Lesser General Public License for more details. |
| 13 | + * |
| 14 | + * You should have received a copy of the GNU Lesser General Public |
| 15 | + * License along with this library; if not, write to the Free Software |
| 16 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
| 17 | + * MA 02110-1301 USA |
| 18 | + */ |
| 19 | +package com.bioinceptionlabs.reactionblast.api; |
| 20 | + |
| 21 | +import java.util.ArrayList; |
| 22 | +import java.util.List; |
| 23 | + |
| 24 | +import org.openscience.cdk.interfaces.IReaction; |
| 25 | +import org.openscience.cdk.silent.SilentChemObjectBuilder; |
| 26 | +import org.openscience.cdk.smiles.SmilesParser; |
| 27 | +import com.bioinceptionlabs.reactionblast.fingerprints.IPatternFingerprinter; |
| 28 | +import com.bioinceptionlabs.reactionblast.mechanism.BondChangeCalculator; |
| 29 | +import com.bioinceptionlabs.reactionblast.mechanism.MappingSolution; |
| 30 | +import com.bioinceptionlabs.reactionblast.mechanism.ReactionMechanismTool; |
| 31 | +import com.bioinceptionlabs.reactionblast.tools.StandardizeReaction; |
| 32 | + |
| 33 | +/** |
| 34 | + * Simple, clean public API for Reaction Decoder Tool. |
| 35 | + * |
| 36 | + * <pre> |
| 37 | + * // Map a reaction from SMILES |
| 38 | + * ReactionResult result = RDT.map("CC(=O)O.OCC>>CC(=O)OCC.O"); |
| 39 | + * |
| 40 | + * // Check results |
| 41 | + * System.out.println(result.getBondChanges()); // [C-O, O-H, C=O, ...] |
| 42 | + * System.out.println(result.getMappedSmiles()); // mapped SMILES |
| 43 | + * System.out.println(result.getBondChangeCount()); // number of bond changes |
| 44 | + * </pre> |
| 45 | + * |
| 46 | + * @author Syed Asad Rahman <asad.rahman@bioinceptionlabs.com> |
| 47 | + */ |
| 48 | +public final class RDT { |
| 49 | + |
| 50 | + private RDT() {} |
| 51 | + |
| 52 | + /** |
| 53 | + * Map a reaction from SMILES and extract bond changes. |
| 54 | + * |
| 55 | + * @param reactionSmiles reaction SMILES (reactants>>products) |
| 56 | + * @return ReactionResult with mapping, bond changes, and fingerprints |
| 57 | + * @throws IllegalArgumentException if SMILES is invalid |
| 58 | + */ |
| 59 | + public static ReactionResult map(String reactionSmiles) { |
| 60 | + return map(reactionSmiles, true, true); |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * Map a reaction with control over stereo perception and ring handling. |
| 65 | + * |
| 66 | + * @param reactionSmiles reaction SMILES (reactants>>products) |
| 67 | + * @param generate2D perceive 2D stereo centers |
| 68 | + * @param complexMapping handle ring system mapping |
| 69 | + * @return ReactionResult with mapping, bond changes, and fingerprints |
| 70 | + * @throws IllegalArgumentException if SMILES is invalid |
| 71 | + */ |
| 72 | + public static ReactionResult map(String reactionSmiles, boolean generate2D, boolean complexMapping) { |
| 73 | + if (reactionSmiles == null || !reactionSmiles.contains(">>")) { |
| 74 | + throw new IllegalArgumentException("Invalid reaction SMILES: must contain '>>'"); |
| 75 | + } |
| 76 | + try { |
| 77 | + SmilesParser sp = new SmilesParser(SilentChemObjectBuilder.getInstance()); |
| 78 | + IReaction reaction = sp.parseReactionSmiles(reactionSmiles); |
| 79 | + reaction.setID("RDT_" + Integer.toHexString(reactionSmiles.hashCode())); |
| 80 | + |
| 81 | + ReactionMechanismTool rmt = new ReactionMechanismTool( |
| 82 | + reaction, true, generate2D, false, complexMapping, true, new StandardizeReaction()); |
| 83 | + |
| 84 | + return extractResult(rmt, reactionSmiles); |
| 85 | + } catch (Exception e) { |
| 86 | + throw new RuntimeException("Mapping failed for: " + reactionSmiles, e); |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + private static ReactionResult extractResult(ReactionMechanismTool rmt, String inputSmiles) { |
| 91 | + MappingSolution solution = rmt.getSelectedSolution(); |
| 92 | + if (solution == null) { |
| 93 | + return new ReactionResult(inputSmiles, null, 0, 0, 0, |
| 94 | + new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); |
| 95 | + } |
| 96 | + |
| 97 | + BondChangeCalculator bcc = solution.getBondChangeCalculator(); |
| 98 | + List<String> formedCleaved; |
| 99 | + List<String> orderChanges; |
| 100 | + List<String> stereoChanges; |
| 101 | + try { |
| 102 | + formedCleaved = extractFeatures(bcc.getFormedCleavedWFingerprint()); |
| 103 | + orderChanges = extractFeatures(bcc.getOrderChangesWFingerprint()); |
| 104 | + stereoChanges = extractFeatures(bcc.getStereoChangesWFingerprint()); |
| 105 | + } catch (Exception e) { |
| 106 | + formedCleaved = new ArrayList<>(); |
| 107 | + orderChanges = new ArrayList<>(); |
| 108 | + stereoChanges = new ArrayList<>(); |
| 109 | + } |
| 110 | + |
| 111 | + String mappedSmiles = null; |
| 112 | + try { |
| 113 | + org.openscience.cdk.smiles.SmilesGenerator sg = new org.openscience.cdk.smiles.SmilesGenerator( |
| 114 | + org.openscience.cdk.smiles.SmiFlavor.Stereo | org.openscience.cdk.smiles.SmiFlavor.AtomAtomMap); |
| 115 | + mappedSmiles = sg.create(bcc.getReaction()); |
| 116 | + } catch (Exception ignored) {} |
| 117 | + |
| 118 | + return new ReactionResult( |
| 119 | + inputSmiles, |
| 120 | + mappedSmiles, |
| 121 | + formedCleaved.size(), |
| 122 | + orderChanges.size(), |
| 123 | + stereoChanges.size(), |
| 124 | + formedCleaved, |
| 125 | + orderChanges, |
| 126 | + stereoChanges); |
| 127 | + } |
| 128 | + |
| 129 | + private static List<String> extractFeatures(IPatternFingerprinter fp) { |
| 130 | + List<String> features = new ArrayList<>(); |
| 131 | + if (fp != null) { |
| 132 | + for (var feature : fp.getFeatures()) { |
| 133 | + features.add(feature.getPattern() + ":" + (int) feature.getWeight()); |
| 134 | + } |
| 135 | + } |
| 136 | + return features; |
| 137 | + } |
| 138 | +} |
0 commit comments