Skip to content
This repository was archived by the owner on Sep 15, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
29cfde4
Add examples
mattwthompson Aug 14, 2026
88c7b33
Update lockfile
mattwthompson Aug 14, 2026
c1a3f32
Bump hooks
mattwthompson Aug 14, 2026
52ee602
Update environments
mattwthompson Aug 14, 2026
657d59e
Use Toolkit 0.19
mattwthompson Aug 14, 2026
a75e547
Do not fail fast
mattwthompson Aug 14, 2026
edb38bd
Shuffle dependencies
mattwthompson Aug 14, 2026
811ba31
Update Pixi environment
mattwthompson Aug 24, 2026
953befa
Debug
mattwthompson Aug 24, 2026
25541fa
Debug
mattwthompson Aug 24, 2026
a696c00
Debug
mattwthompson Aug 24, 2026
a7462b1
Debug
mattwthompson Aug 24, 2026
ca12ca5
Update lockfile
mattwthompson Aug 24, 2026
936dbe5
Kick
mattwthompson Aug 24, 2026
441d47a
Safer skipping
mattwthompson Aug 24, 2026
c5cf94d
Remove some debug
mattwthompson Aug 24, 2026
5ade9c0
Merge remote-tracking branch 'upstream/main' into examples
mattwthompson Aug 24, 2026
92b4f95
Bump
mattwthompson Aug 24, 2026
bb75e65
Update lockfile
mattwthompson Aug 24, 2026
570da29
Merge remote-tracking branch 'upstream/main' into examples
mattwthompson Sep 1, 2026
e8c1bb3
Update lockfile
mattwthompson Sep 1, 2026
bc2d334
Bring down whole toolkit
mattwthompson Sep 1, 2026
ee993d4
Modify test-examples task to include output directory
mattwthompson Sep 3, 2026
45fbc3f
Fix formatting in parameter gradients example
mattwthompson Sep 3, 2026
88d98eb
Changes from review
mattwthompson Sep 3, 2026
00344fe
Merge remote-tracking branch 'upstream/examples' into examples
mattwthompson Sep 3, 2026
4705f9f
Fix
mattwthompson Sep 3, 2026
9667e74
Revert "Fix formatting in parameter gradients example"
mattwthompson Sep 3, 2026
43ec0a1
Fix
mattwthompson Sep 3, 2026
f902f0a
More fixes
mattwthompson Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,30 @@ jobs:
PIXI_ENV: ${{ matrix.base }}-py${{ matrix.python-version }}

strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["312", "313"]
# Exclude the CUDA envs as we can't test them on GitHub Actions
base:
- base
- mm
- examples

steps:
- uses: actions/checkout@v7

- name: Write OE license file
env:
OE_LICENSE_TEXT: ${{ secrets.OE_LICENSE }}
run: echo "${OE_LICENSE_TEXT}" > "${OE_LICENSE}"

- name: Setup virtual environment
uses: prefix-dev/setup-pixi@v0.10.2
with:
cache: true
environments: ${{ env.PIXI_ENV }}

- name: Install and license OpenEye Toolkits
if: false
- name: Write OE license file
if: ${{ !contains(env.PIXI_ENV, 'docs') }}
run: |
echo "${SECRET_OE_LICENSE}" > ${OE_LICENSE}
pixi run -e ${{ matrix.environment }} python -c "from openeye import oechem; assert oechem.OEChemIsLicensed()"
pixi run -e "${{ env.PIXI_ENV }}" python -c "from openeye import oechem; assert oechem.OEChemIsLicensed()"
Comment thread
mattwthompson marked this conversation as resolved.
env:
SECRET_OE_LICENSE: ${{ secrets.OE_LICENSE }}

Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repos:
- id: end-of-file-fixer
exclude: ".*\\.offxml$"
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.1
rev: v0.16.3
hooks:
- id: ruff-check
types_or: [python, pyi, jupyter]
Expand All @@ -17,7 +17,7 @@ repos:
- id: ruff-format
types_or: [python, pyi, jupyter]
- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.26.0
rev: v2.27.1
hooks:
- id: pyproject-fmt
- repo: https://github.com/ComPWA/taplo-pre-commit
Expand Down
8 changes: 8 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Examples

This directory contains a number of examples of how to use `fitlib`. They currently include:

* [Evaluating the energy of a water dimer with virtual sites](compute-energy.ipynb)
* [Minimizing the conformer of a molecule](conformer-minimization.ipynb)
* [Computing the gradient of the energy w.r.t. force field parameters](parameter-gradients.ipynb)
* [Differentiably compute ensemble averages from MD simulations](md-simulations.ipynb)
Comment thread
Copilot marked this conversation as resolved.
348 changes: 348 additions & 0 deletions examples/compute-energy.ipynb

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions examples/conformer-minimization.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "261b79c7042b8a6f",
"metadata": {
"collapsed": false
},
"source": [
"# Conformer Minimization\n",
"\n",
"This example will show how to optimize a conformer of paracetamol.\n",
"\n",
"Load in a paracetamol molecule, generate a conformer for it, and perturb the conformer to ensure it needs minimization."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "b081ee3aecf864ac",
"metadata": {
"ExecuteTime": {
"end_time": "2023-10-17T21:18:13.134692Z",
"start_time": "2023-10-17T21:18:10.562001Z"
},
"collapsed": false
},
"outputs": [],
"source": [
"import openff.toolkit\n",
"import openff.units\n",
"import torch\n",
"\n",
"molecule = openff.toolkit.Molecule.from_smiles(\"CC(=O)NC1=CC=C(C=C1)O\")\n",
"molecule.generate_conformers(n_conformers=1)\n",
"\n",
"conformer = torch.tensor(molecule.conformers[0].m_as(openff.units.unit.angstrom)) * 1.10\n",
"conformer.requires_grad = True"
]
},
{
"cell_type": "markdown",
"id": "f4168aec7a72494c",
"metadata": {
"collapsed": false
},
"source": [
"We specify that the gradient of the conformer is required so that we can optimize it using PyTorch.\n",
"\n",
"Parameterize the molecule using OpenFF Interchange and convert it into a PyTorch tensor representation."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8d00fd2dcf4c27cf",
"metadata": {
"ExecuteTime": {
"end_time": "2023-10-17T21:18:16.758187Z",
"start_time": "2023-10-17T21:18:13.138018Z"
},
"collapsed": false
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d8c8c3f62d1448a4b07498d18cf6dc5f",
"version_major": 2,
"version_minor": 0
},
"text/plain": []
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import openff.interchange\n",
"\n",
"interchange = openff.interchange.Interchange.from_smirnoff(\n",
" openff.toolkit.ForceField(\"openff-2.3.0.offxml\"),\n",
" molecule.to_topology(),\n",
")\n",
"\n",
"import fitlib.converters\n",
"\n",
"force_field, [topology] = fitlib.converters.convert_interchange(interchange)"
]
},
{
"cell_type": "markdown",
"id": "792cb057cb419fa8",
"metadata": {
"collapsed": false
},
"source": [
"We can minimize the conformer using any of PyTorch's optimizers. "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "facd656a27cf46a8",
"metadata": {
"ExecuteTime": {
"end_time": "2023-10-17T21:18:17.036136Z",
"start_time": "2023-10-17T21:18:16.761394Z"
},
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 0: E=102.10968017578125 kcal / mol\n",
"Epoch 5: E=7.088213920593262 kcal / mol\n",
"Epoch 10: E=-18.331130981445312 kcal / mol\n",
"Epoch 15: E=-22.182296752929688 kcal / mol\n",
"Epoch 20: E=-30.369152069091797 kcal / mol\n",
"Epoch 25: E=-36.81045150756836 kcal / mol\n",
"Epoch 30: E=-38.517852783203125 kcal / mol\n",
"Epoch 35: E=-40.50505828857422 kcal / mol\n",
"Epoch 40: E=-42.08476257324219 kcal / mol\n",
"Epoch 45: E=-42.19199752807617 kcal / mol\n",
"Epoch 50: E=-42.37827682495117 kcal / mol\n",
"Epoch 55: E=-42.6767692565918 kcal / mol\n",
"Epoch 60: E=-42.799903869628906 kcal / mol\n",
"Epoch 65: E=-42.94251251220703 kcal / mol\n",
"Epoch 70: E=-43.037200927734375 kcal / mol\n",
"Epoch 74: E=-43.084136962890625 kcal / mol\n"
]
}
],
"source": [
"import fitlib\n",
"\n",
"optimizer = torch.optim.Adam([conformer], lr=0.02)\n",
"\n",
"for epoch in range(75):\n",
" energy = fitlib.compute_energy(topology, force_field, conformer)\n",
" energy.backward()\n",
"\n",
" optimizer.step()\n",
" optimizer.zero_grad()\n",
"\n",
" if epoch % 5 == 0 or epoch == 74:\n",
" print(f\"Epoch {epoch}: E={energy.item()} kcal / mol\")"
]
},
{
"cell_type": "markdown",
"id": "360d6eb9cf2b6cc4",
"metadata": {
"collapsed": false
},
"source": [
"We can then re-store the optimized conformer back into the molecule. Here we add the conformer to the molecule's conformer list, but we could also replace the original conformer."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "eaec04c4039ca59b",
"metadata": {
"ExecuteTime": {
"end_time": "2023-10-17T21:18:17.052947Z",
"start_time": "2023-10-17T21:18:17.036498Z"
},
"collapsed": false
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "449fcae6d9eb4e5a8a3d765f0608e399",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"NGLWidget(max_frame=1)"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"molecule.add_conformer(conformer.detach().numpy() * openff.units.unit.angstrom)\n",
"molecule.visualize(backend=\"nglview\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading