Fingerprint Database¶
The following four examples perform the same task, detailed below:
reading a query structure
printing out the similarity score between the fingerprint of this query and the fingerprint generated for each molecule read from a database file.
In Listing 9
, after importing the query
structure and generating its path fingerprint, the program loops over
the database file creating a path fingerprint for each structure.
Then the program calculates the Tanimoto similarity between the
fingerprint of the query and the database entry by calling the
OETanimoto
function.
Listing 9: Similarity calculation from file
public class SimCalcFromFile
{
public static int Main(string[] args)
{
if (args.Length != 2)
{
OEChem.OEThrow.Usage("SimCalcFromFile <queryfile> <targetfile>");
}
oemolistream ifs = new oemolistream();
if (!ifs.open(args[0]))
{
OEChem.OEThrow.Fatal("Unable to open " + args[0] + " for reading");
}
OEGraphMol qmol = new OEGraphMol();
if (!OEChem.OEReadMolecule(ifs, qmol))
{
OEChem.OEThrow.Fatal("Unable to read query molecule");
}
OEFingerPrint qfp = new OEFingerPrint();
OEGraphSim.OEMakeFP(qfp, qmol, OEFPType.Path);
if (!ifs.open(args[1]))
{
OEChem.OEThrow.Fatal("Unable to open " + args[1] + " for reading");
}
OEFingerPrint tfp = new OEFingerPrint();
foreach (OEGraphMol tmol in ifs.GetOEGraphMols())
{
OEGraphSim.OEMakeFP(tfp, tmol, OEFPType.Path);
Console.WriteLine("{0:0.000}", OEGraphSim.OETanimoto(qfp, tfp));
}
return 0;
}
}
In Listing 10
only the code block that is
different from Listing 9
is shown.
In this example, it is assumed that the fingerprints are
pre-calculated and stored in an OEB
binary file as generic data
attached to the corresponding molecules. The program loops over the
file and accesses the pre-generated fingerprints or calculates them if
they are not available.
The obvious advantage of this process is that the fingerprints one have to be generated once when the binary file is created. This can be significantly faster, than generating the fingerprints on-the-fly every time the program is executed.
See also
The Storage and Retrieval section shows an example of how to
generate an OEB
binary file which stores molecule along
with their corresponding fingerprints.
Listing 10: Similarity calculation from OEB file
OEFingerPrint tfp = new OEFingerPrint();
foreach (OEGraphMol tmol in ifs.GetOEGraphMols())
{
if (tmol.HasData("PATH_FP"))
{
tfp = OEGraphSim.OEGetFP(tmol, "PATH_FP");
}
else
{
OEChem.OEThrow.Warning("Unable to access fingerprint for" + tmol.GetTitle());
OEGraphSim.OEMakeFP(tfp, tmol, OEFPType.Path);
}
Console.WriteLine("{0:0.000}", OEGraphSim.OETanimoto(qfp, tfp));
}
Listing 11
differs from
Listing 9
in that
it uses an OEFPDatabase object to store the
generated fingerprints.
The OEFPDatabase class is designed to perform
in-memory fingerprint searches.
Listing 11: Similarity calculation with fingerprint database from file
OEFPDatabase fpdb = new OEFPDatabase(qfp.GetFPTypeBase());
foreach (OEGraphMol tmol in ifs.GetOEGraphMols())
{
fpdb.AddFP(tmol);
}
foreach (OESimScore score in fpdb.GetScores(qfp))
{
Console.WriteLine("{0:0.000}", score.GetScore());
}
After building the fingerprint database, the scores can be accessed by
the OEFPDatabase.GetScores
method.
This will return an iterator over the similarity scores calculated.
Note
The OEFPDatabase only stores
fingerprints and not the molecules from which they are generated.
A correspondence between a molecule and its fingerprint stored in
the database can be established by using the index returned by the
OEFPDatabase.AddFP
method.
See also
Listing 13
shows how to keep
track of the correspondence between a fingerprint added to a
OEFPDatabase object and a molecule from which
it is calculated.
In the last example (Listing 12
),
OEFPDatabase is used again
to store the fingerprints. If the fingerprint is read from the
OEB
input binary file, then it is directly added to the
database, otherwise the fingerprint is generated on-the-fly when
passing the OEMolBase molecule itself to the
OEFPDatabase.AddFP
method.
Listing 12: Similarity calculation with fingerprint database from OEB
OEFPDatabase fpdb = new OEFPDatabase(qfp.GetFPTypeBase());
OEFingerPrint tfp = new OEFingerPrint();
foreach (OEGraphMol tmol in ifs.GetOEGraphMols())
{
if (tmol.HasData("PATH_FP"))
{
tfp = OEGraphSim.OEGetFP(tmol, "PATH_FP");
fpdb.AddFP(tfp);
}
else
{
OEChem.OEThrow.Warning("Unable to access fingerprint for" + tmol.GetTitle());
fpdb.AddFP(tmol);
}
}
foreach (OESimScore score in fpdb.GetScores(qfp))
{
Console.WriteLine("{0:0.000}", score.GetScore());
}
Sorted Search¶
Similarity searching based on a 2D representation of molecular structure (such as fingerprints) is one of the most common approaches for virtual screening. A molecule that is structurally similar to an active molecule is more likely to be active.
A virtual screening strategy involves going through a molecule database and calculating the similarity between a reference structure and each of the molecules, followed by ranking the similarity scores in descending order to identify molecules that are the most similar to the reference structure.
Listing 13
shows how to search a molecule
database using the OEFPDatabase.GetSortedScores
method to identify analogs based on their fingerprint similarity.
First the molecules are imported using an OEMolDatabase object.
Then a OEFPDatabase object is created that will
store the corresponding fingerprints.
Iterating over the molecules a fingerprints are added to the database by calling
the OEFPDatabase.AddFP
method.
In case when a molecule can not be accessed from the OEMolDatabase
object, an empty fingerprint is added to the OEFPDatabase
object. This ensures that the indices of the two databases are synchronized.
After generating the fingerprints, the program reads
reference molecules (in the SMILES format) from standard input.
Then the input SMILES string is parsed and the fingerprint database is
searched to identify structures with the highest similarity scores.
Finally, the SMILES string of the best hits are written to
standard output.
Listing 13: Similarity search in memory
public class MemSimSearch
{
public static void Main(string[] args)
{
if (args.Length != 1)
OEChem.OEThrow.Usage("MemSimSearch <database>");
oemolistream ifs = new oemolistream();
if (!ifs.open(args[0]))
OEChem.OEThrow.Fatal("Cannot open database molecule file!");
// load molecules
OEMolDatabase moldb = new OEMolDatabase(ifs);
uint nrmols = moldb.GetMaxMolIdx();
// generate fingerprints
OEFPDatabase fpdb = new OEFPDatabase(OEFPType.Path);
OEFingerPrint emptyfp = new OEFingerPrint();
emptyfp.SetFPTypeBase(fpdb.GetFPTypeBase());
OEGraphMol mol = new OEGraphMol();
for (uint idx = 0; idx < nrmols; ++idx)
{
if (moldb.GetMolecule(mol, idx))
fpdb.AddFP(mol);
else
fpdb.AddFP(emptyfp);
}
uint nrfps = fpdb.NumFingerPrints();
OEWallTimer timer = new OEWallTimer();
while (true)
{
try
{
// read query SMILES from System.in
Console.Write("Enter SMILES> ");
StreamReader isr = new StreamReader(Console.OpenStandardInput());
// parse query
string str = isr.ReadLine();
OEGraphMol query = new OEGraphMol();
if (!OEChem.OEParseSmiles(query, str))
{
OEChem.OEThrow.Warning("Invalid SMILES String");
continue;
}
// calculate similarity scores
timer.Start();
OESimScoreIter siter = fpdb.GetSortedScores(query, 5);
Console.WriteLine("{0} seconds to search {1} fingerprints",
timer.Elapsed(), nrfps);
OEGraphMol hit = new OEGraphMol();
for (siter.ToFirst(); siter.IsValid(); siter.Increment())
{
if (moldb.GetMolecule(hit, siter.Target().GetIdx()))
{
string smiles = OEChem.OEMolToSmiles(hit);
Console.WriteLine("Tanimoto score {0,4:0.000} {1}",
siter.Target().GetScore(), smiles);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
As mentioned before, OEFPDatabase is a
fingerprint container and does not store the corresponding
molecule. Therefore the molecules have to be stored in a separate container
in this case in an OEMolDatabase object.
When the OEFPDatabase.GetSortedScores
returns
the iterator over the best similarity scores, the associated index can
be utilized to access the corresponding structure in the
OEMolDatabase object.
In the above example, the entire database was searched to identify structurally similar molecules. However, the user can also specify a segment of the database to be searched by providing a begin and end index.
See also
OEMolDatabase class in the OEChem TK manual
Examples of fingerprint searches in the API section:
OEFPDatabase.GetScores
methodOEFPDatabase.GetSortedScores
method
Searching with User-defined Similarity Measures¶
By default, the Tanimoto
similarity is used when calling either the
OEFPDatabase.GetScores
method or the
OEFPDatabase.GetSortedScores
method.
The user can set other types of similarity measures to be applied by
calling the OEFPDatabase.SetSimFunc
method with
a value from the OESimMeasure
namespace.
Each of the constants from this namespace corresponds to one of the
built-in similarity calculation methods.
There is also a facility to use user-defined similarity measures when searching a fingerprint database. The following example shows how a similarity calculation can be implemented by deriving from the OESimFuncBase class.
Formula: \(Sim_{Simpson}(A,B) = \sqrt{\frac{bothAB}{min(onlyA+ bothAB),(onlyB+ bothAB))}}\)
private class SimpsonSimFunc : OESimFuncBase
{
public override float Call(OEFingerPrint fpA, OEFingerPrint fpB)
{
uint onlyA, onlyB, bothAB;
OEChem.OEGetBitCounts(fpA, fpB, out onlyA, out onlyB, out bothAB);
float sim = (float)bothAB;
sim /= (float)Math.Min((onlyA + bothAB), (onlyB + bothAB));
return sim;
}
public override OESimFuncBase CreateCopy()
{
OESimFuncBase copy = new SimpsonSimFunc();
copy.ReleaseOwnership();
return copy;
}
public override string GetSimTypeString()
{
return "Simpson";
}
}
After implementing the similarity calculation, it can be added to an OEFPDatabase object, henceforth this new similarity calculation will be used.
OEFPDatabase fpdb = new OEFPDatabase(OEFPType.Path);
fpdb.SetSimFunc(new SimpsonSimFunc());
See also
The User-defined Similarity Measures section