Open In Colab

Crystal Generation using Diffusion Models#

In this tutorial, we’ll explore how to generate inorganic crystal structures using Chemeleon, a diffusion-based generative model for materials discovery.

Whereas autoregressive models like CrystaLLM generate structures one token at a time, diffusion models work by iteratively denoising a noisy latent representation to arrive at a valid crystal structure.

We will:

  • Understand the intuition behind diffusion-based generation

  • Use a pretrained Chemeleon model to generate new materials

  • Visualize and interpret the generated crystal structures

Reference: Chemeleon GitHub

Diffusion Models: A Brief Overview#

Diffusion models are a class of generative models inspired by physical processes of noise and denoising.

Forward process: Gradually add noise to a structure until it becomes pure noise.

Reverse process (learned): Train a neural network to remove noise step-by-step and reconstruct the original data.

Advantages:

  • High-quality, diverse generations

  • Better suited for continuous and spatially structured data (like crystal coordinates)

In Chemeleon, the diffusion model operates on a latent representation of crystal structures derived from atom types and fractional coordinates.

What is Chemeleon?#

Chemeleon is a graph-based diffusion model for crystal structure generation. It uses:

  • A crystal graph representation with node features (elements) and position embeddings

  • A denoising network trained on the Materials Project dataset

  • A score-based diffusion process in latent space to generate valid structures

Key components:

  • Graph Neural Networks (GNNs) for encoding

  • Denoising Score Matching for training

  • Support for conditional generation (e.g. given composition)

Paper: ChemRxiv

execute next cell only if you run on google colab

import locale
locale.getpreferredencoding = lambda: "UTF-8"

! pip install janus-core[all] data-tutorials chemeleon ase
get_ipython().kernel.do_shutdown(restart=True)

Load up the required functions#

from chemeleon import Chemeleon
from chemeleon.visualize import Visualizer
from ase.io import write
import os

Load the model#

We load up a pre-trained model. We start with the composition mode. This model only takes compositions as a prompt. Note that the download and loading takes some time (2 - 5 minutes).

%%time
composition_model = Chemeleon.load_composition_model()

Generate Structures#

Here we generate just one sample, to run quickly. But you can increase this later. Since we are using the composition only model, it can only take elements as a prompt.

# Set parameters
n_samples = 2
n_atoms = 8
prompt = "Li P S"
%%time
# Generate crystal structures
atoms_list = composition_model.sample(prompt, n_atoms, n_samples)

Output Interpretation#

The output of Chemeleon is a valid 3D crystal structure, represented as a pymatgen Structure object.

Each structure includes:

  • Lattice parameters

  • Atomic coordinates

  • Element types

You can export to CIF using:

structure.to(filename="generated_structure.cif")
# Visualise
visualizer = Visualizer(atoms_list)
visualizer.view(index=0)

from ase.io import write
write("generated_structures.extxyz",atoms_list)
write("generated_structure_0.cif",atoms_list[0])

Diffusion vs. Autoregressive Generation#

Feature

Diffusion (Chemeleon)

Autoregressive (CrystaLLM)

Generation style

Denoising from noise

Token-by-token sampling

Data representation

Continuous 3D + elements

Discrete sequence

Conditioning support

Via latent / graph inputs

Via token prompts

Output format

pymatgen Structure

CIF / token sequence

Typical advantages

Spatial realism, diversity

Interpretability, simplicity

Question for students:

  • Which approach seems more flexible for structure-property targeting?

  • Can you imagine combining both approaches in a hybrid model?

Try generation with more conditions#

We can use the general_text_model which allows us to use natural language to impose more conditions.

# Load default model checkpoint (general text types)
chemeleon = Chemeleon.load_general_text_model()
# Set parameters
n_samples = 3
n_atoms = 56
text_inputs = "A crystal structure of LiMn2O4 with cubic symmetry"
# Generate crystal structure
atoms_list = chemeleon.sample(text_inputs, n_atoms, n_samples)
# Visualize the generated crystal structure
visualizer = Visualizer(atoms_list)
visualizer.view(index=0)

View the generation trajectory#

We can visualise the diffusion Langevin dynamcis that is used to generate the final structure from the initial sampling. To achive this we return the trajectory from the genertation process.

In the visulaisation note how the composition, the positions and the lattice are updated at the same time.

n_samples = 1
n_atoms = 56
text_inputs = "A crystal structure of LiMn2O4 with cubic symmetry"

# Generate crystal structure with trajectory
trajectory = chemeleon.sample(text_inputs, n_atoms, n_samples, return_trajectory=True)
# Visualize the trajectory
idx = 0
traj_0 = [t[idx] for t in trajectory][::10] + [trajectory[-1][idx]]
visualizer = Visualizer(traj_0, resolution=15)
visualizer.view_trajectory(duration=0.1)

Exercise#

Use Chemeleon to generate some of the materials that you generated previously with CrystaLLM. Then export these structures and compare the formation energies of the two different generative models.