New publication - Circumventing data imbalance in magnetic ground state data for magnetic moment predictions

| categories: publication, news | tags:

Modeling magnetic materials with DFT is hard. In this work we develop a machine learning approach to predicting magnetic properties of materials based on their structure. Our two stage model first predicts if a material is magnetic, and then if it is, what the magnetic moments on each atom are. We show this can lead to faster and lower energy DFT solutions.

@article{sanspeur-2024-circum-data,
  author =       {Rohan Yuri Sanspeur and John R Kitchin},
  title =        {Circumventing Data Imbalance in Magnetic Ground State Data for
                  Magnetic Moment Predictions},
  journal =      {Machine Learning: Science and Technology},
  volume =       {5},
  number =       {1},
  pages =        {015023},
  year =         2024,
  doi =          {10.1088/2632-2153/ad23fb},
  url =          {http://dx.doi.org/10.1088/2632-2153/ad23fb},
  DATE_ADDED =   {Tue Feb 6 20:13:47 2024},
}

Copyright (C) 2024 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.7-pre

Discuss on Twitter

New publication - Applying Large Graph Neural Networks to Predict Transition Metal Complex Energies Using the tmQM_wB97MV Data Set

| categories: publication, news | tags:

In this work, we show that we can use large graph neural networks to predict transition metal complex energies. We developed an improved dataset at a higher level of theory, and tested models ranging from GemNet-T (best) to SchNet (worst). The model performance saturates with the size of neutral structures, and improves with increasing size of charged structures. Finally, we showed that a pre-trained model from OC20 was even better than training from scratch. This indicates a degree of transferability from heterogeneous catalyst models to homogeneous molecular catalysts.

@article{garrison-2023-apply-large,
  author =       {Garrison, Aaron G. and Heras-Domingo, Javier and Kitchin, John
                  R. and dos Passos Gomes, Gabriel and Ulissi, Zachary W. and
                  Blau, Samuel M.},
  title =        {Applying Large Graph Neural Networks To Predict Transition
                  Metal Complex Energies Using the tmQM\_wB97MV Data Set},
  journal =      {Journal of Chemical Information and Modeling},
  volume =       0,
  number =       0,
  pages =        {null},
  year =         2023,
  doi =          {10.1021/acs.jcim.3c01226},
  URL =          {https://doi.org/10.1021/acs.jcim.3c01226},
  eprint =       {https://doi.org/10.1021/acs.jcim.3c01226},
  note =         {PMID: 38049389},
}

Copyright (C) 2023 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.7-pre

Discuss on Twitter

New publication - Chemical Properties from Graph Neural Network-Predicted Electron Densities

| categories: publication, news | tags:

The electron density is one of the most important quantities we use DFT to calculate. It is the foundation of how we compute energy, forces, and many other properties. DFT is expensive though, so in this work we show that we can build a graph neural network that can be used to predict electron densities directly from the atomic coordinates of a system. We show that the predicted densities can also be used to estimate dipole moments and Bader charges.

@article{sunshine-2023-chemic-proper,
  author =       {Sunshine, Ethan M. and Shuaibi, Muhammed and Ulissi, Zachary
                  W. and Kitchin, John R.},
  title =        {Chemical Properties From Graph Neural Network-Predicted
                  Electron Densities},
  journal =      {The Journal of Physical Chemistry C},
  volume =       0,
  number =       0,
  pages =        {null},
  year =         2023,
  doi =          {10.1021/acs.jpcc.3c06157},
  url =          {https://doi.org/10.1021/acs.jpcc.3c06157},
  eprint =       {https://doi.org/10.1021/acs.jpcc.3c06157},
}

Copyright (C) 2023 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.7-pre

Discuss on Twitter

New publication - Beyond Independent Error Assumptions in Large GNN Atomistic Models

| categories: publication, news | tags:

In this work we show that prediction errors from graph neural networks for related atomistic systems tend to be correlated, and as a result the differences in energy are more accurate than the absolute energies. This is similar to what is observed in DFT calculations where systematic errors also cancel in differences. We show this quantitatively through differences of systems with systematically different levels of similarity. This article is part of the Special Collection: 2023 JCP Emerging Investigators Special Collection.

@article{ock-2023-beyon-indep,
  author =       {Janghoon Ock and Tian Tian and John Kitchin and Zachary
                  Ulissi},
  title =        {Beyond Independent Error Assumptions in Large {GNN} Atomistic
                  Models},
  journal =      {The Journal of Chemical Physics},
  volume =       158,
  number =       21,
  pages =        {nil},
  year =         2023,
  doi =          {10.1063/5.0151159},
  url =          {http://dx.doi.org/10.1063/5.0151159},
  DATE_ADDED =   {Sun Sep 24 14:53:46 2023},
}

Copyright (C) 2023 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.7-pre

Discuss on Twitter

Adding new backends to hashcache

| categories: programming | tags:

I have been working on hashcache to make it more flexible. I like the base functionality that uses the filesystem for caching. That still works.

Here I set up a timeit decorator to show how this works.

from pycse.hashcache import hashcache
import time

!rm -fr ./cache

def timeit(func):
    def wrapper(*args, **kwargs):
        t0 = time.time()
        res = func(*args, **kwargs)
        print(f'Elapsed time = {time.time() - t0}s')
        return res
    return wrapper

Now we decorate a function that is "expensive". The first time we run it, it takes a long time.

@timeit
@hashcache
def expensive_func(x):
    time.sleep(3)
    return x

expensive_func(2)

Elapsed time = 3.007030963897705s 2

The second time is very fast, since we just look it up.

expensive_func(2)

Elapsed time = 0.0012097358703613281s 2

Where did we look it up from? It is stored on disk. You can see where by adding a verbose option to the decorator. This shows you all the data that was stored in the cache.

@hashcache(verbose=True)
def expensive_func(x):
    time.sleep(3)
    return x

expensive_func(2)

{ 'args': (2,), 'cwd': '/Users/jkitchin/Dropbox/emacs/journal/2023/09/23', 'elapsed_time': 3.0048787593841553, 'func': 'expensive_func', 'hash': 'b5436cc21714a7ea619729cc9768b8c5b3a03307', 'kwargs': {}, 'module': 'main', 'output': 2, 'run-at': 1695572717.2020931, 'run-at-human': 'Sun Sep 24 12:25:17 2023', 'standardized-kwargs': {'x': 2}, 'user': 'jkitchin', 'version': '0.0.2'} 2

1. Alternative backends for hashcache

The file system is an amazing cache with many benefits. There are few reasons you might like something different though. For example, it is slow to search if you have to iterate over all the directories and read the files, and it might be slow to sync lots of directories to another place.

hashcache is more flexible now, so you can define the functions that load and dump the cache. Here we use lmdb as a key-value database. lmdb expects the keys and values to be bytes, so we do some tricks with io.BytesIO to get these as strings from joblib.dump which expects to write to a file.

The load function has the signature (hash, verbose), and the dump function has the signature (hash, data, verbose). In both cases, hash will be a string for the key to save data in. data will be a dictionary that should be saved in a way that it can be reloaded. verbose is a flag that you can ignore or use to provide some kind of logging.

from pycse.hashcache import hashcache

import io, joblib, lmdb

def lmdb_dump(hsh, data, verbose=False):
    if verbose:
        print('running lmdb_dump')
    with io.BytesIO() as f:
        joblib.dump(data, f)
        value = f.getvalue()

    db = lmdb.Environment(hashcache.cache)
    with db.begin(write=True) as txn:
        txn.put(hsh.encode('utf-8'), value)

def lmdb_load(hsh, verbose=False):
    if verbose:
        print('running lmdb_load')
    db = lmdb.Environment(hashcache.cache)
    with db.begin() as txn:
        val = txn.get(hsh.encode('utf-8'))
        if val is None:
            return False, None
        else:
            return True, joblib.load(io.BytesIO(val))['output']
                                    
! rm -fr cache.lmdb

hashcache.cache = 'cache.lmdb'


@hashcache(loader=lmdb_load, dumper=lmdb_dump, verbose=True)
def f(x):
    return x

f(2)   

running lmdb_load running lmdb_dump 2

And we can recall the result as easily.

f(2)

running lmdb_load 2

2. a shelve version

Maybe you prefer a built in library like shelve. This is also quite simple.

from pycse.hashcache import hashcache

import io, joblib, shelve

def shlv_dump(hsh, data, verbose=False):
    print('running shlv_dump')
    with io.BytesIO() as f:
        joblib.dump(data, f)
        value = f.getvalue()

    with shelve.open(hashcache.cache) as db:
        db[hsh] = value

def shlv_load(hsh, verbose=False):
    print('running shlv_load')
    with shelve.open(hashcache.cache) as db:
        if hsh in db:
            return True, joblib.load(io.BytesIO(db[hsh]))['output']
        else:
            return False, None

hashcache.cache = 'cache.shlv'
! rm -f cache.shlv.db

@hashcache(loader=shlv_load, dumper=shlv_dump)
def f(x):
    return x

f(2)
    

running shlv_load running shlv_dump 2

And again loading is easy.

f(2)

running shlv_load 2

3. sqlite version

I am a big fan of sqlite. Here I use a simple table mapping a key to a value. I think it could be interesting to consider storing the value as json that would make it more searchable, or you could make a more complex table, but here we keep it simple.

from pycse.hashcache import hashcache

import io, joblib, sqlite3

def sql_dump(hsh, data, verbose=False):
    print('running sql_dump')
    with io.BytesIO() as f:
        joblib.dump(data, f)
        value = f.getvalue()

    with con:
        con.execute("INSERT INTO cache(hash, value) VALUES(?, ?)",
                    (hsh, value))

def sql_load(hsh, verbose=False):
    print('running sql_load')
    with con:        
        cur = con.execute("SELECT value FROM cache WHERE hash = ?",
                          (hsh,))
        value = cur.fetchone()
        if value is None:
            return False, None
        else:
            return True, joblib.load(io.BytesIO(value[0]))['output']

! rm -f cache.sql
hashcache.cache = 'cache.sql'
con = sqlite3.connect(hashcache.cache)
con.execute("CREATE TABLE cache(hash TEXT unique, value BLOB)")
        
@hashcache(loader=sql_load, dumper=sql_dump)
def f(x):
    return x

f(2)    

running sql_load running sql_dump 2

Once again, running is easy.

f(2)

running sql_load 2

4. redis

Finally, you might like a server to cache in. This opens the door to running the server remotely so it is accessible by multiple processes using the cache on different machines. We use redis for this example, but only run it locally. Make sure you run redis-server --daemonize yes

from pycse.hashcache import hashcache

import io, joblib, redis

db = redis.Redis(host='localhost', port=6379)

def redis_dump(hsh, data, verbose=False):
    print('running redis_dump')
    with io.BytesIO() as f:
        joblib.dump(data, f)
        value = f.getvalue()

    db.set(hsh, value)

def redis_load(hsh, verbose=False):
    print('running redis_load')
    if not hsh in db:
        return False, None
    else:
        return True, joblib.load(io.BytesIO(db.get(hsh)))['output']

    
import functools    
hashcache_redis = functools.partial(hashcache,
                                    loader=redis_load,
                                    dumper=redis_dump)    

@hashcache_redis
def f(x):
    return x

f(2)    

running redis_load running redis_dump 2

No surprise here, loading is the same as before.

f(2)

running redis_load 2

5. Summary

I have refactored hashcache to make it much easier to add new backends. You might do that for performance, ease of backup or transferability, to add new capabilities for searching, etc. The new code is a little cleaner than it was before IMO. I am not sure it is API-stable yet, but it is getting there.

Copyright (C) 2023 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.7-pre

Discuss on Twitter
Next Page ยป