Skip to content

Conversation

mstapelberg
Copy link
Contributor

@mstapelberg mstapelberg commented Apr 24, 2025

This PR adds a new ase_fire flavor to the FIRE optimizers, mimicking ASE's implementation, alongside the existing vv_fire (Velocity-Verlet based). Users can now select the desired behavior via the md_flavor argument in fire, unit_cell_fire, and frechet_cell_fire. Breaking: The default changed from vv_fire to ase_fire.

Highlights:

  • ase_fire supports max_step (unlike original vv_fire)
  • new tests for both flavors, including negative power branches, batch/fixed-cell consistency, and more edge cases. Existing FIRE tests are now parameterized by md_flavor.
  • new example examples/scripts/7_Others/7.6_Compare_ASE_to_VV_FIRE.py compares ase_fire and vv_fire
  • bump setup-uv GitHub Action to v6, cleanup pytest config in pyproject.toml

@cla-bot cla-bot bot added the cla-signed Contributor license agreement signed label Apr 24, 2025
optimizing atomic positions in a batched setting. Uses velocity Verlet
integration with adaptive velocity mixing.
optimizing atomic positions in a batched setting. Logic adapted to follow
ASE FIRE implementation more closely.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that @abhijeetgangan chose to follow the literature implementation intentionally. We've been talking about this internally should offer an implementation that matches ASE but there's a question about how best to do that without duplicating code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood! In my tests with NEB, I found the ASE FIRE implementation to converge quicker for some reason though. Maybe it's unique to NEB, I can test FireState vs this ASEFireState on a few other structures?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can test FireState vs this ASEFireState on a few other structures?

that would be much appreciated!

@janosh janosh changed the base branch from optimizer_ase_test to main April 25, 2025 14:51
@janosh
Copy link
Collaborator

janosh commented Apr 25, 2025

it appears with the changes in this PR, the relaxation @AdeeshKolluru tested in #146 still takes longer to reduce max force than ASE: goes below 1 eV/A in step 17 instead of step 3 for ASE

torch-sim optimize
step    energy (eV)     max_force (eV/A)        volume (A^3)
batch=1
1       -70.05          8.8642          112.35
2       -70.187         7.9693          112.35
3       -70.379         6.663           112.35
4       -70.582         5.1552          112.36
5       -70.76          3.6524          112.36
batch=2
6       -70.886         2.3402          112.37
7       -70.958         1.4841          112.38
8       -70.97          2.437           112.39
9       -70.971         2.4155          112.39
10      -70.975         2.3515          112.39
batch=3
11      -70.98          2.2467          112.39
12      -70.987         2.1036          112.39
13      -70.996         1.9255          112.39
14      -71.005         1.7168          112.4
15      -71.015         1.4576          112.4
batch=4
16      -71.025         1.145           112.4
17      -71.036         0.78201         112.4
18      -71.044         0.54492         112.41
19      -71.049         0.41231         112.41
ASE optimize

      Step     Time          Energy          fmax
FIRE:    0 11:08:23      -69.999269        9.183465
FIRE:    1 11:08:23      -70.941082        1.783544
FIRE:    2 11:08:23      -70.972607        1.473498
FIRE:    3 11:08:23      -71.014896        0.960106
FIRE:    4 11:08:23      -71.042696        0.525267
FIRE:    5 11:08:23      -71.047455        0.817320
FIRE:    6 11:08:23      -71.049077        0.775535
FIRE:    7 11:08:24      -71.052065        0.694864
FIRE:    8 11:08:24      -71.055961        0.581221
FIRE:    9 11:08:24      -71.060210        0.444012
FIRE:   10 11:08:24      -71.064289        0.393639
FIRE:   11 11:08:24      -71.067836        0.374165
FIRE:   12 11:08:24      -71.070756        0.356804
FIRE:   13 11:08:24      -71.073478        0.339223
FIRE:   14 11:08:24      -71.076349        0.380043
FIRE:   15 11:08:24      -71.079865        0.389464
FIRE:   16 11:08:24      -71.084310        0.347161
FIRE:   17 11:08:24      -71.089341        0.249172
FIRE:   18 11:08:24      -71.093816        0.162485
FIRE:   19 11:08:24      -71.096501        0.192828
FIRE:   20 11:08:25      -71.097859        0.309374

@CompRhys CompRhys changed the base branch from main to optimizer_ase_test May 2, 2025 09:21
@CompRhys CompRhys force-pushed the feature/fire-optimizer-changes branch from 7f7340c to 25b53aa Compare May 2, 2025 09:30
# )
# Get atom-wise masks
downhill_mask_atoms = downhill_mask_batch[state.batch]
_uphill_mask_atoms = uphill_mask_batch[state.batch]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_uphill_mask_atoms is unused

state: FireState,
alpha_start: float = alpha_start,
dt_start: float = dt_start,
dt_start: float = dt_start, # noqa: ARG001
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dt_start is unused?

@mstapelberg
Copy link
Contributor Author

mstapelberg commented May 3, 2025

Would providing an ASEFireState class be a possible solution? Depending on what the user wants, they can select fire or ase-fire as their optimizer? I have a refactored version of. the ASE FIRE implementation while leaving the closer to paper implementation alone that I can push here. In that implementation I resolved the un-used variables and cleaned things up.

The ASEFireState I've written seems to converge as fast as ASE in NEB at least as shown here: #90 (comment)

@janosh
Copy link
Collaborator

janosh commented May 3, 2025

The ASEFireState I've written seems to converge as fast as ASE in NEB at least as shown here: #90 (comment)

that's great to hear! but as you suggest, we need benchmarks across multiple systems to gain some confidence that the ASE implementation is consistently faster to converge

Would providing an ASEFireState class be a possible solution? Depending on what the user wants, they can select fire or ase-fire as their optimizer?

i think to minimize API surface and make it easier to see how the ASE and original FIRE implementations differ, adding a keyword to the existing fire would be better. could call it md_flavor:

    Args:
        model (torch.nn.Module): Model that computes energies, forces, and stress
        dt_max (float): Maximum allowed timestep
        dt_start (float): Initial timestep
        n_min (int): Minimum steps before timestep increase
        f_inc (float): Factor for timestep increase when power is positive
        f_dec (float): Factor for timestep decrease when power is negative
        alpha_start (float): Initial velocity mixing parameter
        f_alpha (float): Factor for mixing parameter decrease
        maxstep (float): Maximum distance an atom can move per iteration (default
            value is 0.2). Only used when md_flavor='ase'.
+        md_flavor ('velocity_verlet' | 'ase'): The type of molecular dynamics flavor to run. Options are
+            'velocity_verlet' (default) or 'ase' (mimics ASE's FIRE implementation which 
+             differs from the original paper).

@mstapelberg
Copy link
Contributor Author

Sounds good! Will work on this today/tomorrow.

@mstapelberg
Copy link
Contributor Author

mstapelberg commented May 6, 2025

I just finished modifying optimizer.py and added a test case / example in torch_sim/examples/scripts/7_Others/7.6_Compare_ASE_to_VV_FIRE.py

I tested pure Si (512 atoms), Fe (125 atoms), and Cu (125 atoms), and then Si, Fe, and Cu (same size) with 2 random vacancies in each structure.

Here's the output, seems like the ASE implementation is slightly better overall!

\n--- Running optimization with MD Flavor: ase_fire ---
Step 50: Active structures: 3, Total converged: 0/5
Step 70: Converged indices [2]. Total converged: 1/5
Step 80: Converged indices [1]. Total converged: 2/5
Step 100: Converged indices [0]. Total converged: 3/5
Step 100: Active structures: 1, Total converged: 3/5
Step 150: Active structures: 2, Total converged: 3/5
Step 180: Converged indices [4]. Total converged: 4/5
Step 200: Active structures: 1, Total converged: 4/5
Step 250: Active structures: 1, Total converged: 4/5
Step 300: Active structures: 1, Total converged: 4/5
Step 310: Converged indices [3]. Total converged: 5/5
All structures converged or max iterations reached by batcher.
Finished ase_fire in 31.12 seconds.
\n--- Running optimization with MD Flavor: vv_fire ---
Step 50: Active structures: 3, Total converged: 0/5
Step 80: Converged indices [2]. Total converged: 1/5
Step 90: Converged indices [1]. Total converged: 2/5
Step 100: Active structures: 1, Total converged: 2/5
Step 130: Converged indices [0]. Total converged: 3/5
Step 150: Active structures: 2, Total converged: 3/5
Step 200: Active structures: 2, Total converged: 3/5
Step 230: Converged indices [4]. Total converged: 4/5
Step 250: Active structures: 1, Total converged: 4/5
Step 300: Active structures: 1, Total converged: 4/5
Step 350: Active structures: 1, Total converged: 4/5
Step 400: Active structures: 1, Total converged: 4/5
Step 450: Active structures: 1, Total converged: 4/5
Step 470: Converged indices [3]. Total converged: 5/5
All structures converged or max iterations reached by batcher.
Finished vv_fire in 43.49 seconds.
\n--- Comparison ---
Force tolerance: 0.05 eV/Å
Initial energies: ['-1546.829', '-375.742', '-899.794', '-140.563', '-240.803'] eV
Final ASE energies: ['-2705.613', '-497.718', '-1053.351', '-2692.331', '-487.226'] eV
Final VV energies:  ['-2705.623', '-497.717', '-1053.351', '-2632.801', '-487.219'] eV
Mean Disp (ASE-VV): ['0.0158', '0.0101', '0.0023', '0.9886', '0.0263'] Å
Convergence steps (ASE FIRE): [100, 80, 70, 310, 180]
Convergence steps (VV FIRE):  [130, 90, 80, 470, 230]

As an aside, I think the bigger differences in the NEB (See #90 ) are due to my batched NEB implementation assigning each image its own FIRE optimizer parameters. Whereas in the ASE version of fire I had used scalar values of timestep, a (mixing parameter), and Nsteps that was used for the entire trajectory (which I think makes more sense to do).

I've used Ruff on the optimizer.py. If you guys think this is fine, I can implement the same md_flavor in the UnitCell and FrechetCell versions of FIRE?

Also, P.S: InFlightAutoBatcher is Legendary 🚀 Would it be possible to get your feedback on how I used it in the 7.6_Compare_ASE_to_VV_FIRE.py?

@janosh
Copy link
Collaborator

janosh commented May 6, 2025

@mstapelberg this is great! 👍 thanks a lot

i only managed a quick glance. i'll take a closer look tomorrow. porting the same behavior to UnitCell and FrechetCell sounds good! we'll definitely need unit tests for the new md_flavor kwarg and your examples/scripts/7_Others/7.6_Compare_ASE_to_VV_FIRE.py is a great start for that. i can probably take care of writing the tests tomorrow (unless you'd like to have a go)

@mstapelberg
Copy link
Contributor Author

@mstapelberg this is great! 👍 thanks a lot

i only managed a quick glance. i'll take a closer look tomorrow. porting the same behavior to UnitCell and FrechetCell sounds good! we'll definitely need unit tests for the new md_flavor kwarg and your examples/scripts/7_Others/7.6_Compare_ASE_to_VV_FIRE.py is a great start for that. i can probably take care of writing the tests tomorrow (unless you'd like to have a go)

You bet! Will work to port the same behavior to UnitCell and FrechetCell, while doing the same comparisons.

Should I adopt/add-on to the test_optimizers.py for this? I notice a lot of the tests there are much shorter/simpler, and don't require a GPU. First time writing proper test cases like this, so happy to give it a go.

@janosh
Copy link
Collaborator

janosh commented May 7, 2025

Should I adopt/add-on to the test_optimizers.py for this?

exactly

First time writing proper test cases like this, so happy to give it a go.

usually straightforward to port code that works on GPUs to CPUs for unit tests. common strategy for fast tests is to take whatever code you were developing/debugging with and reduce the number of steps and system sizes to as small as possible while still testing something complex enough to cover many edge cases

… and FrechetCellFireState to have md_flavor to select vv or ase. ASE currently coverges in 1/3 as long. test cases for all three FIRE schemes added to test_optimizers.py with both md_flavors
@mstapelberg
Copy link
Contributor Author

@janosh

Apologies for the delay on this. I have updated the optimizers.py module to have md_flavor selection for all three types of FIRE.

I added test_cases for test_fire_optimization, test_unit_cell_fire_optimization, and test_frechet_cell_fire_optimization.

The "ase-fire" flavor consistently converges in 1/3 the number of steps.

janosh added 7 commits May 9, 2025 11:04
…nary states, md_flavor validation, non-positive volume warnings

brings optimizers.py test coverage up to 96%
…IRE initialization and batch consistency checks

maintains same 96% coverage
…_vv_fire_step function modified by functools.partial for different unit cell optimizations (unit/frechet/bare fire=no cell relax)

- more concise and maintainable code
Copy link
Collaborator

@orionarcher orionarcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great change to see! Very nice contribution and happy to see some reduction in code redundancy.

alpha_start: float = 0.1,
f_alpha: float = 0.99,
maxstep: float = 0.2,
md_flavor: MdFlavor = vv_fire_key,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could make ase the default if it consistently converges faster?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was also going to suggest that 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your kind words Orion!

Comment on lines 527 to 531
- The "vv_fire" flavor follows the original paper closely, including
integration with Velocity Verlet steps.
- The "ase_fire" flavor mimics the implementation in ASE, which differs slightly
in the update steps and does not explicitly use atomic masses in the
velocity update step.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be nice to link to the original paper and ASE implementation

mstapelberg and others added 7 commits May 9, 2025 14:12
…n of FIRE and a link to the original FIRE paper.
…ws asymmetry in batched mode, batch 0 stalls
fix RuntimeError: a leaf Variable that requires grad is being used in an in-place operation:

7. Position / cell update
state.positions += dr_atom
@janosh janosh changed the title feat(fire-optimizer-changes) Update fire_step in optimizers.py based … Introduce ASE-style FIRE optimizer (departing from velocity Verlet in orig FIRE paper) and improve coverage in test_optimizers.py May 14, 2025
@janosh janosh added fix Bug fix breaking Breaking changes geo-opt Geometry optimization labels May 14, 2025
…in fire|unit_cell_fire|frechet_cell_fire doc strings
Copy link
Collaborator

@janosh janosh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks a lot @mstapelberg for the massive amount of work that went into this PR! much appreciated! 👍

and apologies for the slow review

@janosh janosh merged commit b0c3088 into TorchSim:optimizer_ase_test May 14, 2025
1 check passed
janosh added a commit that referenced this pull request May 14, 2025
* adds comparative test for fire optimizer with ase

* rm a few comments

* updates CI for the test

* update changelog for v0.2.0 (#147)

* update changelog for v0.2.0

* minor modification for PR template

* formatting fixes

* formatting and typos

* remove contributors bc they aren't linked

* update test with a harder system

* fix torch.device not iterable error in test_torchsim_vs_ase_fire_mace.py

* fix: should compare the row_vector cell, clean: fix changelog typo

* clean: delete .coverage and newline for pytest command

* Introduce ASE-style `FIRE` optimizer (departing from velocity Verlet in orig FIRE paper) and improve coverage in `test_optimizers.py` (#174)

* feat(fire-optimizer-changes) Update fire_step in optimizers.py based feature/neb-workflow

* reset optimizers.py to main version prior to adding updated changes

* (feat:fire-optimizer-changes) - Added ase_fire_step and renamed fire_step to vv_fire_step. Allowed for selection of md_flavor

* (feat:fire-optimizer-changes) - lint check on optimizers.py with ruff

* (feat:fire-optimizer-changes) - added test cases and example script in examples/scripts/7_Others/7.6_Compare_ASE_to_VV_FIRE.py

* (feat:fire-optimizer-changes) - updated FireState, UnitCellFireState, and FrechetCellFireState to have md_flavor to select vv or ase. ASE currently coverges in 1/3 as long. test cases for all three FIRE schemes added to test_optimizers.py with both md_flavors

* ruff auto format

* minor refactor of 7.6_Compare_ASE_to_VV_FIRE.py

* refactor optimizers.py: define MdFlavor type alias for SSoT on MD flavors

* new optimizer tests: FIRE and UnitCellFIRE initialization with dictionary states, md_flavor validation, non-positive volume warnings

brings optimizers.py test coverage up to 96%

* cleanup test_optimizers.py: parameterize tests for FIRE and UnitCellFIRE initialization and batch consistency checks

maintains same 96% coverage

* refactor optimizers.py: consolidate vv_fire_step logic into a single _vv_fire_step function modified by functools.partial for different unit cell optimizations (unit/frechet/bare fire=no cell relax)

- more concise and maintainable code

* same as prev commit but for _ase_fire_step

instead of _vv_fire_step

* (feat:fire-optimizer-changes) - added references to ASE implementation of FIRE and a link to the original FIRE paper.

* (feat:fire-optimizer-changes) switched md_flavor type from str to MdFlavor and set default to ase_fire_step

* pytest.mark.xfail frechet_cell_fire with ase_fire flavor, reason: shows asymmetry in batched mode, batch 0 stalls

* rename maxstep to max_step for consistent snake_case

fix RuntimeError: a leaf Variable that requires grad is being used in an in-place operation:

7. Position / cell update
state.positions += dr_atom

* unskip frechet_cell_fire in test_optimizer_batch_consistency, can no longer repro error locally

* code cleanup

* bumpy set-up action to v6, more descriptive CI test names

* pin to fairchem_core-1.10.0 in CI

* explain differences between vv_fire and ase_fire and link references in fire|unit_cell_fire|frechet_cell_fire doc strings

* merge test_torchsim_frechet_cell_fire_vs_ase_mace.py with comparative ASE vs torch-sim test for Frechet Cell FIRE optimizer into test_optimizers.py

- move `ase_mace_mpa` and `torchsim_mace_mpa` fixtures into `conftest.py` for wider reuse

* redirect MACE_CHECKPOINT_URL to mace_agnesi_small for faster tests

* on 2nd thought, keep test_torchsim_frechet_cell_fire_vs_ase_mace in a separate file (thanks @CompRhys)

* define MaceUrls StrEnum to avoid breaking tests when "small" checkpoints get redirected in mace-torch

---------

Co-authored-by: Orion Cohen <[email protected]>
Co-authored-by: Janosh Riebesell <[email protected]>
Co-authored-by: Rhys Goodall <[email protected]>
Co-authored-by: Myles Stapelberg <[email protected]>
@mstapelberg
Copy link
Contributor Author

thanks a lot @mstapelberg for the massive amount of work that went into this PR! much appreciated! 👍

and apologies for the slow review

Hi @janosh no worries and sorry for the slow reply on my end (was on travel)! Happy to help with this; and excited to use it for my research as well. Thanks for building this great tool!

@janosh
Copy link
Collaborator

janosh commented May 16, 2025

if you have time, maybe you'd like to give #199 a try to see if it improves your FIRE relaxation results

@mstapelberg
Copy link
Contributor Author

if you have time, maybe you'd like to give #199 a try to see if it improves your FIRE relaxation results

Sure! Will do this later tonight/tomorrow morning and will post the results here? Or should I post them on #199 ?

@janosh
Copy link
Collaborator

janosh commented May 16, 2025

over at #199 would be great or in a new issue if you find any more problems

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaking changes cla-signed Contributor license agreement signed fix Bug fix geo-opt Geometry optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants