Molecule Fingerprints and Visualization

(*New Developments in Wolfram Language Chemistry  *)

Jason Biggs, Wolfram Research Inc
Wolfram Technology Conference 2020

The Outline Slide

A Brief History of Chemistry in the Wolfram Language

ChemicalData -> Entity -> Molecule

A look to the future - new data structures

Molecule Fingerprints

Introduction

Different types of fingerprints

Fingerprint similarity -> MoleculeNearest

Molecule Resource Functions

Interactive Molecule Editor

A Brief History of Chemistry in the Wolfram Language

TimelinePlot[<|(*"Mathematica 1.0 released"->DateObject[" June 1988"],*)"Version 6.0: ChemicalData"->(DateObject["May 2007", DateObject[{2007,5},"Month","Gregorian",-5.`]]),"Version 10.0: EntityValue"->(DateObject["Jul 2014", DateObject[{2014,7},"Month","Gregorian",-5.`]]),"Version 12.0: Molecule"->DateObject["April 2019"],"PubChem Service Connection"->DateObject["March 2016"],"Version 12.1: MoleculeRecognize, new Import/Export formats"->DateObject["March 2020"](*,"WTC 2020 title and abstract deadline"->DateObject["July 2020"]*)(*,"WTC talk"->Today*)|>]

(HyperlinkDefault[ChemicalData, paclet:ref/ChemicalData, paclet:ref/ChemicalData])

?ChemicalData

ChemicalData was introduced in version 6, along with ElementData and IsotopeData,

Features curated experimental data from authoritative sources such as NIST, PubChem, ChemSpider

ChemicalData["Tetraphenoxysilane","CombustionHeat"]

ChemicalData["3Methoxy1,2Propanediol","HenryLawConstant"]

ChemicalData["IsononanoylPeroxide","VanDerWaalsConstants"]

(HyperlinkDefault["\"Chemical\"", paclet:ref/entity/Chemical, paclet:ref/entity/Chemical]) entities

Introduced in version 10, Entities provide a unified interface to the Wolfram Knowledgebase

Expanded syntax for querying multiple entity or multiple properties

EntityValue[{(Entity["diphenylmethane", Entity["Chemical","Diphenylmethane"], "Entity[\"Chemical\", \"Diphenylmethane\"]", "chemical"]),(Entity["piperidine", Entity["Chemical","Piperidine"], "Entity[\"Chemical\", \"Piperidine\"]", "chemical"])},{(EntityProperty[(Subscript["K", "a"]), EntityProperty["Chemical","AcidityConstants"], "EntityProperty[\"Chemical\", \"AcidityConstants\"]"]),(EntityProperty["critical pressure", EntityProperty["Chemical","CriticalPressure"], "EntityProperty[\"Chemical\", \"CriticalPressure\"]"]),(EntityProperty["critical temperature", EntityProperty["Chemical","CriticalTemperature"], "EntityProperty[\"Chemical\", \"CriticalTemperature\"]"]),(EntityProperty["density", EntityProperty["Chemical","Density"], "EntityProperty[\"Chemical\", \"Density\"]"]),(EntityProperty["vapor pressure", EntityProperty["Chemical","VaporPressure"], "EntityProperty[\"Chemical\", \"VaporPressure\"]"])},"Dataset"]

A Brief History of Chemistry in the Wolfram Language

Molecule

Introduced in version 12.0

Live CEOing Ep 377: New Chemistry Functionality Design Review in Wolfram Language

Live CEOing (188) Chemistry in #WolfLang

Live CEOing (175) Chemistry in #WolfLang

Live CEOing (166) Chemistry in #WolfLang

Live CEOing (147) Chemistry Functions in #WolfLang

Live CEOing (141) Chemistry Functions in #WolfLang

Live CEOing (122) Chemistry Functions in #WolfLang

Live CEOing (117) Chemistry Functions in #WolfLang

Live CEOing (52) Chemistry in #WolfLang

Live CEOing (47) Chemistry in #WolfLang

Live CEOing (45) Chemistry in #WolfLang

Live CEOing (42) Chemistry in #WolfLang

A  fully computable symbolic representation of a chemical species

Not reliant on database calls - create a completely new Molecule that doesn’t exist in any database

Generate 2D or 3D coordinates, compute properties

MoleculePlot3D[Molecule@"(2S)-N-[(1S)-2-carbamoyl-1-[[(2S)-1-[(2S)-2-[[(1S)-1-[[(1S)-1-carbamoyl-2-(4-methoxyphenyl)ethyl]carbamoyl]-4-(diaminomethylideneamino)butyl]carbamoyl]pyrrolidin-1-yl]-1-oxo-3-sulfanylidene-propan-2-yl]carbamoyl]ethyl]-2-[[(2S)-2-[[(2S)-3-(4-hydroxyphenyl)-2-[[2-(1-sulfanylcyclohexyl)acetyl]amino]propanoyl]amino]-3-phenyl-propanoyl]amino]pentanediamide"]

BioSequence - new in 12.2

A new structure for representing biological sequence data in the Wolfram Language

Presented in the WTC 2019 presentation "Representing Biological Sequence Data in the Wolfram Language" by John Cassel

Can represent DNA, RNA, amino acid sequences, and more.

Tightly integrated with the Wolfram Knowledge Base to allow obtaining the BioSequences associated with gene and protein entities

?BioSequence*

Create a DNA sequence

sequence=BioSequence["DNA","CGAAAGACAATGGATAAGCCAATACTCGAGCAC"]

{BioSequenceTranslate[sequence],BioSequenceTranscribe[sequence]}

BioSequenceTranscribe[sequence]

BioSequenceConvert[sequence,"Peptide"]

Molecule@First@%

MoleculePlot3D@%

Molecule Fingerprints - Introduction

This is future functionality - requires more design and review before release into the “System`” context

The basic idea behind molecule fingerprints convert a molecule into a sequence of 1’s and 0’s (a bit vector) depending on the presence of certain chemical features.  Alternatively we can make a count vector, keeping track of how often those features are present in the molecule

Various distance measures can be used to give a “molecule similarity” - two molecules that set the same bit are more likely to be similar

Different types of chemical features lead to different types of “similarity” - as a result there are many different types of fingerprints available

<<Chemistry`

FingerprintFunction returns an operator which can be applied to molecules to return a fingerprint.

FingerprintFunction["RDKit"]

MoleculeFingerprintSimilarity - takes 2 molecules as input and returns a numerical value between 0 and 1 representing how “similar” the molecules are.  Options are available to specify the fingerprint type and distance function.

MoleculeNearest - takes a list of molecules and a query molecule.

Molecule Fingerprints - Substructure fingerprints

The first type of fingerprint we will look at are based on “substructure keys”

A predefined set of substructures is enumerated, bits or counts in the fingerprint are set based on substructure presence

A basic feature set

Consider the most fundamental chemical substructure - a single atom.

We know that water contains oxygen

MoleculeContainsQ[Molecule["water"],(Entity["oxygen", Entity["Element","Oxygen"], "Entity[\"Element\", \"Oxygen\"]", element])]

but does not contain sulfur

MoleculeContainsQ[Molecule["water"],(Entity["sulfur", Entity["Element","Sulfur"], "Entity[\"Element\", \"Sulfur\"]", "element"])]

We can define a small set of common atoms to demonstrate a chemical feature fingerprint

commonElements={(Entity["hydrogen", Entity["Element","Hydrogen"], "Entity[\"Element\", \"Hydrogen\"]", "element"]),(Entity["carbon", Entity["Element","Carbon"], "Entity[\"Element\", \"Carbon\"]", "element"]),(Entity["oxygen", Entity["Element","Oxygen"], "Entity[\"Element\", \"Oxygen\"]", "element"]),(Entity["nitrogen", Entity["Element","Nitrogen"], "Entity[\"Element\", \"Nitrogen\"]", "element"]),(Entity["fluorine", Entity["Element","Fluorine"], "Entity[\"Element\", \"Fluorine\"]", "element"]),(Entity["chlorine", Entity["Element","Chlorine"], "Entity[\"Element\", \"Chlorine\"]", "element"]),(Entity["sulfur", Entity["Element","Sulfur"], "Entity[\"Element\", \"Sulfur\"]", "element"]),(Entity["phosphorus", Entity["Element","Phosphorus"], "Entity[\"Element\", \"Phosphorus\"]", "element"]),(Entity["bromine", Entity["Element","Bromine"], "Entity[\"Element\", \"Bromine\"]", "element"]),(Entity["silicon", Entity["Element","Silicon"], "Entity[\"Element\", \"Silicon\"]", "element"])};

m=Molecule["4-bromo-3-fluorobenzenesulfonyl chloride"]

fingerprint=Table[MoleculeContainsQ[m,element],{element,commonElements}]

This is a basic fingerprint.  We can construct a second fingerprint and compare it to the first,

m2=Molecule[(Entity["fomesafen", Entity["Chemical","Fomesafen"], "Entity[\"Chemical\", \"Fomesafen\"]", "chemical"])]

fingerprint2=Table[MoleculeContainsQ[m2,element],{element,commonElements}]

Given these two boolean vectors we can compute their similarity

1.0-JaccardDissimilarity[fingerprint,fingerprint2]

BitVector - test for substructure presence

Given the variety of fingerprint types, the current design is to first create a fingerprinting function and then apply that to different molecules.  To create a fingerprint function using a custom set of substructures, we use

fpfunction = FingerprintFunction["Substructure",
	<|"SubstructurePatterns" -> commonElements|>
]

Now get the fingerprints for the two molecules defined above:

{fingerprint,fingerprint2}=fpfunction/@{Molecule[],Molecule[]}

These return (HyperlinkDefault["BitVector", paclet:ref/datastructure/BitVector, paclet:ref/datastructure/BitVector]) data structures, a new-in-12.1 feature enabled by the compiler.

fingerprint["Visualization"]
fingerprint2["Visualization"]

Using the built-in bit vector operations we can compute the same similarity we found earlier:

total=fingerprint["BitCount"]+fingerprint2["BitCount"]
common=fingerprint["Copy"]["BitAnd",fingerprint2]["BitCount"]
N[common/(total-common)]

For efficiency’s sake we can have one function compute the fingerprints and return the similarity

MoleculeFingerprintSimilarity[m,m2,"FingerprintType"->fpfunction]

CountVector fingerprints and Multiset similarities

Instead of just asking if a given substructure is present in a molecule, we ask how many times does it occur.

#->Length@FindMoleculeSubstructure[Molecule[],#,All]&/@commonElements

We can create a fingerprint function to return a count vector directly

fpfunction = FingerprintFunction[{"Substructure", "CountVector"},
	<|"SubstructurePatterns" -> commonElements|>
]

We can apply this function to the same two molecules as before

{fingerprint,fingerprint2}=fpfunction/@{Molecule[],Molecule[]}

Since these are count vectors, the JaccardDissimilarity function will not work on them. Instead we use the (HyperlinkTemplate[MultisetJaccardDissimilarity, https://resources.wolframcloud.com/FunctionRepository/resources/MultisetJaccardDissimilarityNone, https://resources.wolframcloud.com/FunctionRepository/resources/MultisetJaccardDissimilarity, HyperlinkActionRecycled, HyperlinkActive, , ]) resource function:

1.0-[fingerprint,fingerprint2]

As before we can generate and compare the fingerprints in one call,

MoleculeFingerprintSimilarity[m,m2,"FingerprintType"->fpfunction]

Molecule Fingerprints - Substructure Fingerprints 2

Define a set of functional groups

Using elements as substructures will not lead to very meaningful results.  We can be a bit more sophisticated using a set of predefined functional groups.

Short[patterns=,10]

WordCloud@Keys@patterns

We can see which patterns are present in a given molecule,

m=Molecule[];

keysPresent=Keys@Select[patterns,MoleculeContainsQ[m,#]&]

patt=First@keysPresent;
Row[{PopupMenu[Dynamic[patt],keysPresent],Dynamic[MoleculePlot[m,patterns[patt],ImageSize->400]]}]

Define a substructure fingerprinter

We can take the patterns defined previously and make a fingerprint function to efficiently count the presence of the various substructures:

fpfunction=FingerprintFunction[{"Substructure","CountVector"},<|"SubstructurePatterns"->patterns|>]

fpfunction@m//Dataset

Application: MACCS Keys

The MACCS, Molecular ACCess System, keys are a common set of substructures used to generate bitvector fingerprints, and are described in (ExternalIdentifierURL[ExternalIdentifier["DOI","10.1021/ci010132r"], "DOI", "10.1021/ci010132r", https://doi.org/10.1021/ci010132r]).

fpfunction=FingerprintFunction["MACCSKeys"]

Molecule Fingerprints - Atom pairs and substructure hashing

Hash-based fingerprints do not have a predefined set of substructures - substructures are generated algorithmically from each molecule.

These substructures, along with certain atom and bond properties, are used to create a “hash” - often a 64-bit integer

Properties commonly used include vertex degree, bond conjugation, π-electron counts, and more

Each substructure is then hashed to a 64-bit integer, resulting in a very sparse vector

Each molecule will set a small number of bits, tens to hundreds, out of a total of (2^64)-1 possible bits

If desired, this sparse vector is “folded down” to a more modestly sized bit vector

This results in a smaller fingerprint, with faster similarity comparisons

Can result in “bit collisions” where two different substructures end up setting the same bit

Atom Pairs

Atom pairs are molecular substructures defined by two atoms and the number of bonds along the shortest path between them. The following plot shows three atom pairs, two with four intervening bonds and one with seven intervening bonds.

MoleculePlot[Molecule@"2,3,4,5-tetrahydroxyhexanedioic acid",{MoleculePattern[{Atom["O","PiElectronCount"->0,"HeavyAtomCoordinationNumber"->1],_,_,_,_,_,_,Atom["O","PiElectronCount"->0,"HeavyAtomCoordinationNumber"->1]},{Bond[{1,2}],Bond[{2,3}],Bond[{3,4}],Bond[{4,5}],Bond[{5,6}],Bond[{6,7}],Bond[{7,8}]}],MoleculePattern[{Atom["O","PiElectronCount"->1,"HeavyAtomCoordinationNumber"->1],_,_,_,Atom["O","PiElectronCount"->0,"HeavyAtomCoordinationNumber"->1]},{Bond[{1,2}],Bond[{2,3}],Bond[{3,4}],Bond[{4,5}]}]},
PlotLegends->Placed[SwatchLegend[{Text["OX1\[Hyphen](7)\[Hyphen]OX1"],Text["O·X1\[Hyphen](4)\[Hyphen]OX1"]},LegendMarkers->"Bubble",LegendLabel->Text["Atom pairs"]],Scaled[{.95,.1}]]]

For a substructure defined like this, it isn’t practical to enumerate all possible atom pairs beforehand so they are only defined at the point of generating the fingerprint.  For efficiency’s sake, the atom pairs like “OX1-(7)-OX1” are hashed to a 64-bit integer and these are used as the keys in the fingerprint

fpfunction=FingerprintFunction[{"AtomPairs","CountVector"}]

fpfunction@Molecule[]

By ignoring count information it’s possible to get a bit vector fingerprint using the atom pairs sub

FingerprintFunction[{"AtomPairs","BitVector"}]@Molecule[]

%["BitList"]

Molecule Fingerprints - Advanced Substructures

Path-based fingerprints

For path-based fingerprints, similar to those produced by Daylight, we find all paths of a given length in a molecule.  Those paths, together with information about the atoms and bonds along that path, define the substructures.

m=Molecule[];

length6Paths=Values@FindMoleculeSubstructure[m,MoleculePattern["*~*~*~*~*~*"],All];
Grid[Partition[MoleculePlot[m,{BondList[m,#]}]&/@length6Paths[[;;6]],2]]

FingerprintFunction["RDKit"]@m

%["BitList"]

By changing the minimum path length from 1 to 4, we set fewer bits in the fingerprint

FingerprintFunction[{"RDKit","BitVector"},\[LeftAssociation]"MinPathLength"->4,"MaxPathLength"->7,"BitVectorSize"->2048\[RightAssociation]]@m

%["BitList"]

Circular fingerprints

This method uses the “Morgan” algorithm to generate substructures, finding all atoms within a given “radius”.

m=Molecule[];

atomsInRadius[mol_,atom_,radius_]:=With[{mat=mol["GraphDistanceMatrix"]},
Lookup[PositionIndex[mat[[atom]]],radius]]

MoleculePlot[m,{{11},atomsInRadius[m,11,#]},PlotLabel->StringTemplate["Radius `1`"][#]]&/@Range[4]

Molecule Fingerprints - MoleculeNearest

Create molecules from the list of PubChem central nervous system (CNS) agents (extracted 14 Nov 2018). The PubChem CID is stored in the molecule using MetaInformation.

CNSagents=ResourceFunction["DynamicMap"][Molecule[#[[2]],MetaInformation->#[[1]]]&,];

RandomChoice@CNSagents

Now define a target molecule,

diazepam=Molecule["Clc1c([H])c2c(c([H])c1[H])N(C([H])([H])[H])C(=O)C([H])([H])N=C2c1c([H])c([H])c([H])c([H])c1[H]"]

mol=Molecule[]

#->Row[MoleculePlot/@MoleculeNearest[CNSagents,diazepam,3,"FingerprintType"->#]]&/@{"RDKit","AtomPairs","Morgan","MACCSKeys","Avalon","TopologicalTorsion","Pattern","Layered"}//Column

m=randomMolecule[]

#->Row[MoleculePlot/@MoleculeNearest[CNSagents,m,3,"FingerprintType"->#]]&/@{"RDKit","AtomPairs","Morgan","MACCSKeys","Avalon","TopologicalTorsion","Pattern","Layered"}//Column

Resource Functions - MoleculeValuePlot3D

Highlight atoms depending on whether they are aromatic:

ResourceFunction["MoleculeValuePlot3D"][Molecule["adenosine"],"AromaticAtomQ"]

Highlight atoms depending on whether they are in a ring:

ResourceFunction["MoleculeValuePlot3D"][Molecule["adenosine"],"RingAtomQ"]

Highlight atoms based on their computed partial charge:

ResourceFunction["MoleculeValuePlot3D"][Molecule["adenosine"],"GasteigerPartialCharge"]

Resource Functions - MoleculeSymmetryPlot3D

(HyperlinkTemplate[MoleculeSymmetryPlot3D, https://resources.wolframcloud.com/FunctionRepository/resources/MoleculeSymmetryPlot3DNone, https://resources.wolframcloud.com/FunctionRepository/resources/MoleculeSymmetryPlot3D, HyperlinkActionRecycled, HyperlinkActive, , ]) shows a molecule in 3D together with its symmetry elements

m=Molecule["tetramethyl ammonium"];
m["PointGroupDisplay"]

@m

dimanganeseDecacarbonyl=Molecule[];

dimanganeseDecacarbonyl["PointGroup"]

@dimanganeseDecacarbonyl

Resource Functions - PubChemSimilaritySearch

PubChem is a publicly accessible database of nearly 100 million chemical compounds, maintained by the National Institutes of Health.

Access to the PubChem API is seamlessly integrated into the WolframLanguage through functions like ServiceConnect and ExternalIdentifier

m=Molecule[];
m["PubChemCompoundID"]

Molecule@First@%

The resource function PubChemSimilaritySearch is a convenient way to search for similar compounds:

({{"[◼]", "PubChemSimilaritySearch" }})[m,"SearchType"->"SameTautomer"]

MoleculePlot@*Molecule/@%

MoleculeDraw

Interactively create and edit molecules

Add and modify atoms and bonds

MoleculeDraw[]

MoleculeDraw@MoleculeRecognize@

Future directions - what to do next

Enhancements to 2D and 3D plots

Atom and Bond labels

Customizable R-Group labels

Custom Atom and Bond styling

Computing with and visualizing electronic orbitals

New data structures:

ChemicalReaction

ChemicalSolution

MacroMolecule

Tighter connections to external sources: WikiData, PubChem, CHEMBL

The Last Slide

The Wolfram Chemistry Team

GraphicsGrid[{{,,},{,}}]

Open-source packages used by the Wolfram Language

RDKit: Open-Source Cheminformatics Software

OPSIN: Open Parser for Systematic IUPAC Nomenclature

Symmetrizer: Algorithmic determination of point groups in nearly symmetric molecules

MolVec - Optical Character Recognition for Molecular Structures

Open Babel: The Open Source Chemistry Toolbox