Evaluating AI-Generated Protein Binders with Microsoft’s BioEmu2 Model
Earlier this week, the Microsoft Research AI team released an update to their BioEmu model (called BioEmu2), which enables molecular dynamics-like sampling of a given structural input. Over a year ago, I covered the original BioEmu model in another blog post and, since then, they’ve made some huge capability improvements in this latest update.
Most recently, my research has focused on the targeted generation of antibodies using AI models (such as my own peleke-1 antibody language models). I’m currently participating in the Adaptyv x Anthropic Protein Design Competition, and I am focusing on designing scFv binders against their various challenge targets.
While the generation of potential binders is easy, assessing various candidates for desired traits, binding performance, etc. remains a challenge. Moreover, the selection of your top n candidates for wet lab testing remains an art more than a science.
In an ideal world, molecular dynamics provides valuable information about how two molecules interact over a short period of time, though running thousands of large complexes through traditional molecular dynamics simulations is computationally infeasible. So, I’m excited to incorporate BioEmu2 in my evaluation workflow going forward.
Today, I want to show you how you can use BioEmu2 to assess your AI-designed binders using dynamics information in a much more scalable way.
About BioEmu2
BioEmu2 is a foundation model for biomolecular structure and dynamics from the AI for Science team at Microsoft Research. This new model allows you to predict folded structures, sample equilibrium ensembles, and propagate structures forward in time.
They tout the following key features:
- Support for various molecular systems containing proteins, nucleic acids, ligands, and other chemical components
- Perform equilibrium sampling and structure predictions
- Generate time-lagged trajectories with implicit transfer operator models
- Contact conditioning and inference-time steering toward desired properties
- Context-parallel sampling of structures that do not fit on one GPU
- Command line interface and Python API
Installation
To be honest, getting the tool to run was a little tricky. While the GitHub repo states that you can run with Python 3.12, 3.13, or 3.14, I could not get the dependency constraints to resolve with Python 3.12. I also had issues running this on CUDA 12.x, though they say it’s supported. CUDA 13.2 works just fine.
So, I decided to make a Conda environment instead. You can run the following shell commands to install everything:
## Get the BioEmu2 scripts from GitHub
# git clone https://github.com/microsoft/bioemu2.git
# cd bioemu2
## Create the conda environment
conda create -n bioemu2_0.1.0rc0 python=3.14 -y
conda activate bioemu2_0.1.0rc0
## Install the dependencies
# conda install pip -y
pip install ".[cuda]"Then, simply activate the environment by running conda activate bioemu2_0.1.0rc0 anytime you want to run the model.
General Usage and Propagation
To sample from an equilibrium, you can simply load in your structure (called a “system”) and specify the number of samples you want.
I’m using an anti-glycoprotein scFv candidate from my Bundigbugyo Ebola virus research called sbio-bdbvgp-355. It’s one of my more promising candidates and thus it’s a good example to play with here.
from pathlib import Path
from bioemu2 import EmuSession, System, load_model, save_frames
model = load_model()
## sbio-bdbvgp-355
system = System.from_cif('inputs/sbio-bdbvgp-355.cif')
model = load_model()
emu = EmuSession(system, model=model, mode="equilibrium")
x0 = emu.sample(num_samples=10)
x_t = emu.propagate(x0, lag_time_ns=1.0)
save_frames(x_t, Path("outputs/sbio-bdbvgp-355"))The above code samples conformations of a starting structure, then propagates it by 1ns using the same EmuSession. Propagation starts at a structure (x0) and then samples a structure (x_t) at a future time t (a time lag in nanoseconds). Each result is an endpoint, not an intermediate frame in a trajectory.
Get Colby T. Ford, Ph.D.’s stories in your inbox
Join Medium for free to get updates from this writer.
Note that the sampling process is pulling from the distribution of conformations that your input complex can take. So, for my antibody, the above code results in a topology.cif file and a samples.xtc file that contains 10 sampled states. These are in no particular order, and the orientation of the structures are random, all 1ns from the beginning structure.
After aligning the states using PyMOL, you can compare the binding orientations more easily. For my antibody (in yellow), the predicted binding is generally stable in all 10 output samples, though there are some epitope variations for me to look into.
This basic functionality may be useful to see what percentage of the time your AI-designed binder is predicted to bind to the desired site/epitope.
Trajectory Building
If you need a more ordered view of your complex, you can use the handy simulate_trajectory() function. This will build a successive propagation that results in a time-ordered trajectory.
from pathlib import Path
from bioemu2 import System, load_model, map_cif_atom_order, save_frames, simulate_trajectory
## Inputs
x0_cif = Path("inputs/sbio-bdbvgp-355.cif")
out_dir = Path("outputs/sbio-bdbvgp-355-10ns-trajectory")
## Build system from starting CIF and map atom order explicitly
system = System.from_cif(x0_cif)
x0 = map_cif_atom_order(system, x0_cif)
## 10 ns total trajectory via 1 ns propagation steps => 10 steps + initial frame
lag_time_ns = 1.0
num_trajectory_steps = 10
model = load_model()
trajectories = simulate_trajectory(
system=system,
model=model,
x0=x0,
lag_time_ns=lag_time_ns,
num_trajectory_steps=num_trajectory_steps,
seed=1337,
)
trajectory = trajectories[0]
output_path = save_frames(trajectory, out_dir)
print(f"Saved trajectory to: {output_path}")
print(f"Frames: {trajectory.n_frames}")
print(f"Total time (ns): {trajectory.time[-1] / 1000.0}")The code above results in a 10ns trajectory that can be rendered as a video (using PyMOL or some other tool). I also marked the polar contacts so that I could assess the stability of the interaction between the antibody and the antigen epitope over the time window.
This functionality was useful to see that some polar interactions from my CDR loops remained intact throughout the simulation. You can see this when I zoom in on the binding site:
You can learn ore about customizing propagation and creating trajectories here: https://github.com/microsoft/bioemu2/blob/main/docs/propagation.md
Steering
Steering is a more advanced set of tools that allows you to change the distribution a model samples at inference time (without retraining it). You choose potentials that describe the properties you want to favor. Then, depending on those potentials, you can improve physicality or favor particular conformations. Here are the pre-configured samplers:
edm / EDMSampler: Ordinary, unsteered sampling.edm_gd / GuidedFKSampler: Potential-gradient guidance, without particle resampling.edm_fk / GuidedFKSampler: Potential-gradient guidance with Feynman-Kac particle resampling.edm_fkc / FKCSampler: Feynman-Kac correction with population weighting and resampling.
For my example complex, I tested the GuidedFKSampler() option. The guidance method is a gradient with respect to denoised coordinates,
from bioemu2 import (
EmuSession,
GuidedFKSampler,
System,
get_msa,
join_trajectory_steps,
propagate_with_embeddings,
sample_equilibrium,
load_model,
save_frames,
)
from pathlib import Path
from bioemu2.denoising.steering.potentials import get_default_potentials
## Inputs
x0_cif = Path("inputs/sbio-bdbvgp-355.cif")
out_dir = Path("outputs/sbio-bdbvgp-355-guided-sampling")
## Build system from starting CIF and map atom order explicitly
system = System.from_cif(x0_cif)
model = load_model()
msa = get_msa(system)
x0 = sample_equilibrium(system, model=model, num_samples=1)
num_trajectory_steps = 25
lag_time_ns = 1.0
def potentials_for_step(step):
# Replace with potentials that depend on `step`, such as a moving umbrella window.
return get_default_potentials()
trunk_embeddings = model.embed(
system,
msa=msa,
mode="equilibrium",
)
step_frames = [x0]
for step in range(num_trajectory_steps):
sampler = GuidedFKSampler(
potentials=potentials_for_step(step),
resampling_interval=None,
)
step_frames.append(
propagate_with_embeddings(
system,
trunk_embeddings,
x0=step_frames[-1],
lag_time_ns=lag_time_ns,
model=model,
sampler=sampler,
seed=1337 + step,
progress=True,
)
)
trajectories = join_trajectory_steps(step_frames, lag_time_ns=lag_time_ns)
## Output
output_path = save_frames(trajectories[0], out_dir)
print(f"Saved guided sampling results to: {output_path}")Using this steering (or other guidance functions) allows you to direct the MD predictions. I ran this for 24ns, which resulted in the following animation:
For me, this allowed me to see some interesting movement of the HCDR3 loop, which transiently becomes part of the interface around the 12ns mark.
You can learn more about Steering here: https://github.com/microsoft/bioemu2/blob/main/docs/steering.md
Final Thoughts
I am very impressed with how far this model has improved from the original BioEmu version we saw last year. The overall functionality is very easy to use and can return multi-nanosecond trajectories in minutes. I also like that there’s both a CLI and a Python API.
I am excited to begin using this model as part of the evaluation logic of my AI-generated protein binder candidates. However, I do have a few questions:
- Will this model show dissociations of chains when the binding affinity is too weak?
- Can we control or change external factors like temperature or pH (both as initial conditions or over time)?
- How does this integrate with other frameworks so that we can monitor various metrics (e.g., charge, energy, bonds, distances) over time?
You can check out the BioEmu2 code and documentation here: https://github.com/microsoft/bioemu2/blob/main/README.md
I’d love to see what you build and test with BioEmu2. So, be sure to share it online!
If you or your organization would like to explore architectures and approaches for scalable molecular modeling, please reach out: colby@tuple.xyz ✉
Stay curious…








