Skip to content

Enforce compatible constraints in DOF helper#480

Open
janosh wants to merge 1 commit intomainfrom
enforce-dof-compatibility
Open

Enforce compatible constraints in DOF helper#480
janosh wants to merge 1 commit intomainfrom
enforce-dof-compatibility

Conversation

@janosh
Copy link
Collaborator

@janosh janosh commented Mar 1, 2026

Summary

  • keep count_degrees_of_freedom as a public helper, but validate explicitly supplied constraints against the provided state before counting
  • centralize shared per-system DOF math in _dof_per_system and reuse it from both count_degrees_of_freedom and SimState.get_number_of_degrees_of_freedom
  • add regression tests for out-of-bounds constraints and malformed get_removed_dof outputs (wrong shape, negative, non-tensor, non-finite), plus helper/method parity and strict-vs-clamped behavior

Context

This follows the API discussion in #479 and the end-of-thread discussion in #477: keep the helper for external use, but make it safe and explicit about compatibility checks instead of allowing arbitrary mismatched state/constraint pairs.

Refs:

Test plan

  • pytest tests/test_constraints.py -q

Validate externally supplied constraints in count_degrees_of_freedom, centralize per-system DOF math, and add focused regression tests for malformed/out-of-bounds constraint inputs.
@janosh janosh added fix Bug fix tests Test all the things api API design discussions labels Mar 1, 2026
@janosh janosh requested a review from thomasloux March 1, 2026 14:52


return torch.clamp(total_dof, min=0)
def _validate_constraints_for_dof(state: SimState, constraints: list[Constraint]) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that validate_constraints is enough. The other if-statements should be tests perform by pytest.

"""
# Start with unconstrained DOF per system
total_dof = 3 * state.n_atoms_per_system
constraints_to_count = state.constraints if constraints is None else constraints
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not but to me it is simpler to only consider the input constraints. Otherwise the user should use state.get_degrees_of_freedom()

Comment on lines +631 to +645
if constraints is not None:
_validate_constraints_for_dof(state, constraints_to_count)
return torch.clamp(_dof_per_system(state, constraints_to_count), min=0)


# Subtract DOF removed by constraints
def _dof_per_system(
state: SimState, constraints: list[Constraint] | None = None
) -> torch.Tensor:
"""Compute unconstrained-minus-removed DOF per system."""
dof_per_system = 3 * state.n_atoms_per_system
if constraints is not None:
for constraint in constraints:
total_dof -= constraint.get_removed_dof(state)
dof_per_system -= constraint.get_removed_dof(state)
return dof_per_system

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not convinced by the value of separating these two helper functions

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok I saw that you used it with state.get_degrees_of_freedom. Then I'm fine with the 2 functions

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

Labels

api API design discussions fix Bug fix tests Test all the things

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants