Fix/ase fire deform grad forces - #602
Conversation
20831b3 to
3a8163d
Compare
|
|
||
| @dataclass(kw_only=True) | ||
| class CellOptimState(OptimState): | ||
| class CellOptimState(OptimState, DeformGradMixin): |
There was a problem hiding this comment.
adding this mixin is the key fix of this PR, it gives the CellOptimState the reference_row_vector_cell attribute.
| # Get current deformation gradient | ||
| # reference_cell.mT: [S, 3, 3], row_vector_cell: [S, 3, 3] | ||
| cur_deform_grad = cell_filters.deform_grad( | ||
| state.reference_cell.mT, state.row_vector_cell |
There was a problem hiding this comment.
this is such a subtle bug that ONLY affected FIRE, and NOT the BFGS or L-BFGS optimizers since the BFGS optimizers read the reference_cell directly. whereas if you look at fire, it looked for getattr(state, "reference_row_vector_cell",
There was a problem hiding this comment.
I could've fixed this bug by making the fire implementation match the other 2, but it's cleaner to just add the DeformGradMixin to the CellOptimState and use shared helper functions
| "frechet_method", | ||
| } | ||
|
|
||
| def deform_grad_forces(self) -> torch.Tensor: |
There was a problem hiding this comment.
I considered adding this function to DeformGradMixin but decided against it since it needs forces and system_idx which are missing from DeformGradMixin but CellOptimState provides
| """ | ||
| # per-atom row vector @ its system's deform_grad: | ||
| # (n_atoms, 1, 3) @ (n_atoms, 3, 3) -> (n_atoms, 1, 3) -> (n_atoms, 3) | ||
| return torch.bmm( |
There was a problem hiding this comment.
ase's version of this function is here:
https://gitlab.com/ase/ase/-/blob/3.29.0/ase/_4/optimize/cellutil.py#L213
279bea9 to
a7a6a95
Compare
|
|
||
| # Transform forces to scaled coordinates | ||
| # forces: [N, 3], cur_deform_grad[system_idx]: [N, 3, 3] | ||
| forces_scaled = torch.bmm( |
There was a problem hiding this comment.
fire, bfgs, and l-bfgs all individually calculate frac_positions which we will use the state.frac_positions() helper function now. I think this bug arose since we had different implementations to calculate forces_scaled, so factoring out all this logic into the same helpers is defensive programming
CellOptimState declared its own reference_cell instead of inheriting DeformGradMixin, so it never had the reference_row_vector_cell property. The getattr fallback in _ase_fire_step therefore silently resolved to the current cell, making deform_grad(current, current) the identity and the forces @ F transform a no-op (the arguments were also swapped relative to deform_grad's (reference, current) signature, which the missing attribute masked).
9734c4a to
23048cc
Compare
|
This PR comes after curtischong#18 (it's rebased off this one for clarity), but I'd probably review that one too if you're in a reviewing spree this weekend rhys |
Summary
The problem lies in this code https://github.com/curtischong/torch-sim/blob/98c3ca52d5e6e45030efbbc3442ba99f91e2a335/torch_sim/optimizers/fire.py#L323-L328:
Since the fire state (which is a child of CellOptimState) never has
reference_row_vector_cell, thestate.row_vector_cellis always used instead.Here is the definition of CellOptimState, we can see it doesn't have
reference_row_vector_cell.https://github.com/curtischong/torch-sim/blob/98c3ca52d5e6e45030efbbc3442ba99f91e2a335/torch_sim/optimizers/cell_filters.py#L427
Adding the DeformGradMixin to CellOptimState gives it the
reference_row_vector_cellattribute.I found this bug bc I saw that torchsim didn't match the ASE implementation. I ran the test script below to show that our optimizer diverges from ASE's because of this bug.
This was the script used to replicate the bug on main
Results:
main:
this branch: