Ambit-tautomer (which builds on CDK), is a Java library available as a .jar file as well as a Maven artifact. We need a way to use the Java library in Python and I've found JPype to be able to provide what we need. I haven't thought about fetching the dependencies using Maven itself yet, so the trick right now is to download .jar files. I'm accessing the packages inside the .jar files using JPype.
Using conda:
conda install -c conda-forge jpype1
If this doesn't work for you, you can find other techniques at the installation page
Download the .jar files of Ambit-tautomer and CDK and place them in the libs/ folder
A more detailed user guide is available at the official website. For our purposes, just do the following:
Add these imports at the top
# Import the JPype module
import jpype
# Allows Java modules to be imported
import jpype.imports
# Import all standard Java types into the global scope
from jpype.types import *
Initialize the JVM and add the .jar files to the classpath
jpype.startJVM(classpath=['libs/ambit-tautomers-2.0.0-SNAPSHOT.jar', 'libs/cdk-2.3.jar'],
convertStrings=False)
The native JDK packages should be accessible directly as
from java.lang import System
But if you want to access the contents of a .jar file (e.g. the Ambit and CDK .jar files in our case), you need to access it using a method in JPype:
For instance, to import ambit2.core.io.FileInputState
, you will have to do
file_input_state = jpype.JPackage('ambit2').core.io.FileInputState
Based on what I understood from their example code, the following should be done:
- Create an instance of
ambit2.tautomers.TautomerManager
, say tautomerManager - Set flags for customization (optional; see next section)
- Create a
org.openscience.cdk.interfaces.IAtomContainer
object that will contain the molecule structure, say 'mol'.- This could be done using a SMILES string using a
org.openscience.cdk.smiles.SmilesParser
instance (see SmilesParser). One could call SmilesParser#parseSmiles method from this instance.
- This could be done using a SMILES string using a
- tautomerManager.setStructure(mol)
- Use one of the three algorithms to generate tautomers:
- Pure combinatorial method using tautomerManager.generateTautomers()
- Improved combinatorial method using tautomerManager.generateTautomers_ImprovedCombApproach()
- IA-DFS using tautomerManager.generateTautomersIncrementally()
- These return a
List<IAtomContainer>
(a Java object)
One can customize a lot of things, such as the rules that we want Ambit to use, the algorithm we want it to utilize, etc. I have added methods (untested) for customizing the following in ambit_tautomer.py:
- set maximum number of back tracks
- set maximum number of subcombinations (I have to read what this means)
- toggle the use of (1,3), (1,5) and (1,7) shift tautomerisation rules
- set a rule number limit
- there's a rule selection possibility.
- MoleculeFilter (I have to read how this works)
- apply duplication checks (by isomorphism or InChI; I have to see when these are done)
If you get an error like
raise TypeError("Package {0} is not Callable".format(self._name))
TypeError: Package <Java package org.openscience.cdk.tautomers.TautomerConst._name> is not Callable
despite the fact that the object you're referring to is a Java class or method, then it's likely because it couldn't locate the file properly. Check if the classpath of the libraries is correct (relative to the directory you're working in, it should allow accessing those directories), and check if the package names are correct and/or if there are any typos).
If you were doing something like
silentChemObjectBuilder = jpype.JPackage("org").openscience.cdk.silent.SilentChemObjectBuilder()
and it threw a TypeError: Cannot create Interface instances
then that's because SilentChemObjectBuilder is a Java Interface and you cannot instantiate it this way. A workaround has been suggested in the user guide.
In the above case, there was an in-built method for creating an instance
silentChemObjectBuilder = jpype.JPackage("org").openscience.cdk.silent.SilentChemObjectBuilder.getInstance()
GAT = jpype.JPackage("ambit2").tautomers.TautomerConst.GAT
throws AttributeError: type object 'ambit2.tautomers.TautomerConst' has no attribute 'GAT'
Resolution: #TODO