Examples¶
Generating representations using the Compound
class¶
The following example demonstrates how to generate a representation via
the qml.Compound
class.
# Read in an xyz or cif file.
water = Compound(xyz="water.xyz")
# Generate a molecular coulomb matrices sorted by row norm.
water.generate_coulomb_matrix(size=5, sorting="row-norm")
print(water.representation)
Might print the following representation:
[ 73.51669472 8.3593106 0.5 8.35237809 0.66066557 0.5
0. 0. 0. 0. 0. 0. 0.
0. 0. ]
Generating representations via the qml.representations
module¶
import numpy as np
from qml.representations import *
# Dummy coordinates for a water molecule
coordinates = np.array([[1.464, 0.707, 1.056],
[0.878, 1.218, 0.498],
[2.319, 1.126, 0.952]])
# Oxygen, Hydrogen, Hydrogen
nuclear_charges = np.array([8, 1, 1])
# Generate a molecular coulomb matrices sorted by row norm.
cm1 = generate_coulomb_matrix(nuclear_charges, coordinates,
size=5, sorting="row-norm")
print(cm1)
The resulting Coulomb-matrix for water:
[ 73.51669472 8.3593106 0.5 8.35237809 0.66066557 0.5
0. 0. 0. 0. 0. 0. 0.
0. 0. ]
# Generate all atomic coulomb matrices sorted by distance to
# query atom.
cm2 = generate_atomic_coulomb_matrix(atomtypes, coordinates,
size=5, sort="distance")
print cm2
[[ 73.51669472 8.3593106 0.5 8.35237809 0.66066557 0.5
0. 0. 0. 0. 0. 0.
0. 0. 0. ]
[ 0.5 8.3593106 73.51669472 0.66066557 8.35237809 0.5
0. 0. 0. 0. 0. 0.
0. 0. 0. ]
[ 0.5 8.35237809 73.51669472 0.66066557 8.3593106 0.5
0. 0. 0. 0. 0. 0.
0. 0. 0. ]]
Calculating a Gaussian kernel¶
The input for most of the kernels in QML is a numpy array, where the first dimension is the number of representations, and the second dimension is the size of each representation. An brief example is presented here, where compounds
is a list of Compound()
objects:
import numpy as np
from qml.kernels import gaussian_kernel
# Generate a numpy-array of the representation
X = np.array([c.representation for c in compounds])
# Kernel-width
sigma = 100.0
# Calculate the kernel-matrix
K = gaussian_kernel(X, X, sigma)
Calculating a Gaussian kernel using a local representation¶
The easiest way to calculate the kernel matrix using an explicit, local representation is via the wrappers module. Note that here the sigmas is a list of sigmas, and the result is a kernel for each sigma. The following examples currently work with the atomic coulomb matrix representation and the local SLATM representation:
import numpy as np
from qml.kernels import get_local_kernels_gaussian
# Assume the QM7 dataset is loaded into a list of Compound()
for compound in qm7:
# Generate the desired representation for each compound
compound.generate_atomic_coulomb_matrix(size=23, sort="row-norm")
# Make a big array with all the atomic representations
X = np.concatenate([mol.representation for mol in qm7])
# Make an array with the number of atoms in each compound
N = np.array([mol.natoms for mol in qm7])
# List of kernel-widths
sigmas = [50.0, 100.0, 200.0]
# Calculate the kernel-matrix
K = get_local_kernels_gaussian(X, X, N, N, sigmas)
print(K.shape)
(3, 7101, 7101)
Note that mol.representation
is just a 1D numpy array.
Generating the SLATM representation¶
The Spectrum of London and Axillrod-Teller-Muto potential (SLATM) representation requires additional input to reduce the size of the representation.
This input (the types of many-body terms) is generate via the get_slatm_mbtypes()
function. The function takes a list of the nuclear charges for each molecule in the dataset as input. E.g.:
from qml.representations import get_slatm_mbtypes
# Assume 'qm7' is a list of Compound() objects.
mbtypes = get_slatm_mbtypes([mol.nuclear_charges for compound in qm7])
# Assume the QM7 dataset is loaded into a list of Compound()
for compound in qm7:
# Generate the desired representation for each compound
compound.generate_slatm(mbtypes, local=True)
The local
keyword in this example specifies that a local representation is produced. Alternatively the SLATM representation can be generate via the qml.representations
module:
from qml.representations import generate_slatm
# Dummy coordinates
coordinates = ...
# Dummy nuclear charges
nuclear_charges = ...
# Dummy mbtypes
mbtypes = get_slatm_mbtypes( ... )
# Generate one representation
rep = generate_slatm(coordinates, nuclear_charges, mbtypes)
Here coordinates
is an Nx3 numpy array, and nuclear_charges
is simply a list of charges.
Generating the FCHL representation¶
The FCHL representation does not have an explicit representation in the form of a vector, and the kernel elements must be calculated analytically in a separate kernel function.
The syntax is analogous to the explicit representations (e.g. Coulomb matrix, BoB, SLATM, etc), but is handled by kernels from the separate qml.fchl
module.
The code below show three ways to create the input representations for the FHCL kernel functions.
First using the Compound
class:
# Assume the dataset is loaded into a list of Compound()
for compound in mols:
# Generate the desired representation for each compound, cut off in angstrom
compound.generate_fchl_representation(size=23, cut_off=10.0)
# Make Numpy array of the representation, which can be parsed to the kernel
X = np.array([c.representation for c in mols])
The dimensions of the array should be (number_molecules, size, 5, size)
, where size
is the
size keyword used when generating the representations.
In addition to using the Compound
class to generate the representations, FCHL representations can also be generated via the qml.fchl.generate_fchl_representation()
function, using similar notation to the functions in the qml.representations.*
functions.
from qml.fchl import generate_representation
# Dummy coordinates for a water molecule
coordinates = np.array([[1.464, 0.707, 1.056],
[0.878, 1.218, 0.498],
[2.319, 1.126, 0.952]])
# Oxygen, Hydrogen, Hydrogen
nuclear_charges = np.array([8, 1, 1])
rep = generate_representation(coordinates, nuclear_charges)
To create the representation for a crystal, the notation is as follows:
from qml.fchl import generate_representation
# Dummy fractional coordinates
fractional_coordinates = np.array(
[[ 0. , 0. , 0. ],
[ 0.75000042, 0.50000027, 0.25000015],
[ 0.15115386, 0.81961403, 0.33154037],
[ 0.51192691, 0.18038651, 0.3315404 ],
[ 0.08154025, 0.31961376, 0.40115401],
[ 0.66846017, 0.81961403, 0.48807366],
[ 0.08154025, 0.68038678, 0.76192703],
[ 0.66846021, 0.18038651, 0.84884672],
[ 0.23807355, 0.31961376, 0.91846033],
[ 0.59884657, 0.68038678, 0.91846033],
[ 0.50000031, 0. , 0.50000031],
[ 0.25000015, 0.50000027, 0.75000042]]
)
# Dummy nuclear charges
nuclear_charges = np.array(
[58, 58, 8, 8, 8, 8, 8, 8, 8, 8, 23, 23]
)
# Dummy unit cell
unit_cell = np.array(
[[ 3.699168, 3.699168, -3.255938],
[ 3.699168, -3.699168, 3.255938],
[-3.699168, -3.699168, -3.255938]]
)
# Generate the representation
rep = generate_representation(fractional_coordinates, nuclear_charges,
cell=unit_cell, neighbors=100, cut_distance=7.0)
The neighbors keyword is the max number of atoms with the cutoff-distance
Generating the FCHL kernel¶
The following example demonstrates how to calculate the local FCHL kernel elements between FCHL representations. X1
and X2
are numpy arrays with the shape (number_compounds,max_size, 5,neighbors)
, as generated in one of the previous examples. You MUST use the same, or larger, cut-off distance to generate the representation, as to calculate the kernel.
from qml.fchl import get_local_kernels
# You can get kernels for multiple kernel-widths
sigmas = [2.5, 5.0, 10.0]
# Calculate the kernel-matrices for each sigma
K = get_local_kernels(X1, X2, sigmas, cut_distance=10.0)
print(K.shape)
As output you will get a kernel for each kernel-width.
(3, 100, 200)
In case X1
and X2
are identical, K will be symmetrical. This is handled by a separate function with exploits this symmetry (thus being twice as fast).
from qml.fchl import get_local_symmetric_kernels
# You can get kernels for multiple kernel-widths
sigmas = [2.5, 5.0, 10.0]
# Calculate the kernel-matrices for each sigma
K = get_local_kernels(X1, sigmas, cut_distance=10.0)
print(K.shape)
(3, 100, 100)
In addition to the local kernel, the FCHL module also provides kernels for atomic properties (e.g. chemical shifts, partial charges, etc). These have the name “atomic”, rather than “local”.
from qml.fchl import get_atomic_kernels
from qml.fchl import get_atomic_symmetric_kernels
The only difference between the local and atomic kernels is the shape of the input.
Since the atomic kernel outputs kernels with atomic resolution, the atomic input has the shape (number_atoms, 5, size)
.