Skip to content

Conversation

@Akshat-Raj
Copy link

@Akshat-Raj Akshat-Raj commented Feb 10, 2026

Fixes #5223

Changes made in this Pull Request:

  • Implemented bounds checking for atom_resindex and residue_segindex in Universe.empty() to ensure indices are within the valid range.
  • Added a test test_empty_universe_bounds in testsuite/MDAnalysisTests/core/test_universe.py to confirm that a ValueError is raised for out-of-bounds indices.
  • Updated CHANGELOG and AUTHORS to reflect these changes.

LLM / AI generated code disclosure

LLMs or other AI-powered tools (beyond simple IDE use cases) were used in this contribution: no

PR Checklist

  • [ x] Issue raised/referenced?
  • [ x] Tests updated/added?
  • Documentation updated/added?
  • [ x] package/CHANGELOG file updated?
  • [ x] Is your name in package/AUTHORS? (If it is not, add it!)
  • [ x] LLM/AI disclosure was updated.

Developers Certificate of Origin

I certify that I can submit this code contribution as described in the Developer Certificate of Origin, under the MDAnalysis LICENSE.


📚 Documentation preview 📚: https://mdanalysis--5228.org.readthedocs.build/en/5228/

Copilot AI review requested due to automatic review settings February 10, 2026 21:48
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Hello there first time contributor! Welcome to the MDAnalysis community! We ask that all contributors abide by our Code of Conduct and that first time contributors introduce themselves on GitHub Discussions so we can get to know you. You can learn more about participating here. Please also add yourself to package/AUTHORS as part of this PR.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses Issue #5223 by adding early validation in Universe.empty() so invalid atom_resindex / residue_segindex mappings fail fast with a clear error, and adds a regression test to confirm the behavior.

Changes:

  • Added bounds checks for atom_resindex and residue_segindex in Universe.empty().
  • Added a testsuite regression test asserting ValueError is raised on out-of-bounds indices.
  • Updated CHANGELOG and AUTHORS.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
testsuite/MDAnalysisTests/core/test_universe.py Adds coverage for out-of-bounds index inputs to Universe.empty().
package/MDAnalysis/core/universe.py Introduces bounds-check guards for index mapping inputs.
package/CHANGELOG Records the fix in release notes (currently with formatting/date issues).
package/AUTHORS Adds contributor name for 2026.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 749 to 762
atom_resindex = np.asarray(atom_resindex)
if np.any(atom_resindex >= n_residues) or np.any(atom_resindex < 0):
raise ValueError(
"atom_resindex contains invalid residue indices. "
"All values must be between 0 and n_residues-1."
)

if residue_segindex is not None:
residue_segindex = np.asarray(residue_segindex)
if np.any(residue_segindex >= n_segments) or np.any(residue_segindex < 0):
raise ValueError(
"residue_segindex contains invalid segment indices. "
"All values must be between 0 and n_segments-1."
)
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The bounds check does np.any(atom_resindex >= n_residues) after np.asarray(atom_resindex). If atom_resindex contains non-numeric values (e.g., strings/object dtype), this comparison raises a TypeError, changing the failure mode from the ValueError this guard intends to provide. Consider coercing to an integer array (or catching TypeError/ValueError) and re-raising a ValueError with a clear message.

Suggested change
atom_resindex = np.asarray(atom_resindex)
if np.any(atom_resindex >= n_residues) or np.any(atom_resindex < 0):
raise ValueError(
"atom_resindex contains invalid residue indices. "
"All values must be between 0 and n_residues-1."
)
if residue_segindex is not None:
residue_segindex = np.asarray(residue_segindex)
if np.any(residue_segindex >= n_segments) or np.any(residue_segindex < 0):
raise ValueError(
"residue_segindex contains invalid segment indices. "
"All values must be between 0 and n_segments-1."
)
try:
# Ensure atom_resindex is an integer array so that bounds
# checks do not raise unexpected TypeError for non-numeric input.
atom_resindex = np.asarray(atom_resindex, dtype=int)
except (TypeError, ValueError):
raise ValueError(
"atom_resindex must be an array of integers."
) from None
if np.any(atom_resindex >= n_residues) or np.any(atom_resindex < 0):
raise ValueError(
"atom_resindex contains invalid residue indices. "
"All values must be between 0 and n_residues-1."
)
if residue_segindex is not None:
try:
# Ensure residue_segindex is an integer array so that bounds
# checks do not raise unexpected TypeError for non-numeric input.
residue_segindex = np.asarray(residue_segindex, dtype=int)
except (TypeError, ValueError):
raise ValueError(
"residue_segindex must be an array of integers."
) from None
if np.any(residue_segindex >= n_segments) or np.any(residue_segindex < 0):
raise ValueError(
"residue_segindex contains invalid segment indices. "
"All values must be between 0 and n_segments-1."
)

Copilot uses AI. Check for mistakes.
Comment on lines 750 to 761
if np.any(atom_resindex >= n_residues) or np.any(atom_resindex < 0):
raise ValueError(
"atom_resindex contains invalid residue indices. "
"All values must be between 0 and n_residues-1."
)

if residue_segindex is not None:
residue_segindex = np.asarray(residue_segindex)
if np.any(residue_segindex >= n_segments) or np.any(residue_segindex < 0):
raise ValueError(
"residue_segindex contains invalid segment indices. "
"All values must be between 0 and n_segments-1."
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Same issue as atom_resindex: np.any(residue_segindex >= n_segments) can raise TypeError for non-numeric/object inputs, which undermines the goal of raising a consistent ValueError on invalid indices. Consider validating/coercing input (or catching TypeError/ValueError) and raising ValueError with an informative message.

Suggested change
if np.any(atom_resindex >= n_residues) or np.any(atom_resindex < 0):
raise ValueError(
"atom_resindex contains invalid residue indices. "
"All values must be between 0 and n_residues-1."
)
if residue_segindex is not None:
residue_segindex = np.asarray(residue_segindex)
if np.any(residue_segindex >= n_segments) or np.any(residue_segindex < 0):
raise ValueError(
"residue_segindex contains invalid segment indices. "
"All values must be between 0 and n_segments-1."
try:
invalid_atom_resindex = (
np.any(atom_resindex >= n_residues)
or np.any(atom_resindex < 0)
)
except (TypeError, ValueError) as err:
raise ValueError(
"atom_resindex must be an array of integers between "
"0 and n_residues-1."
) from err
else:
if invalid_atom_resindex:
raise ValueError(
"atom_resindex contains invalid residue indices. "
"All values must be between 0 and n_residues-1."
)
if residue_segindex is not None:
residue_segindex = np.asarray(residue_segindex)
try:
invalid_residue_segindex = (
np.any(residue_segindex >= n_segments)
or np.any(residue_segindex < 0)
)
except (TypeError, ValueError) as err:
raise ValueError(
"residue_segindex must be an array of integers between "
"0 and n_segments-1."
) from err
else:
if invalid_residue_segindex:
raise ValueError(
"residue_segindex contains invalid segment indices. "
"All values must be between 0 and n_segments-1."

Copilot uses AI. Check for mistakes.
Comment on lines 751 to 755
raise ValueError(
"atom_resindex contains invalid residue indices. "
"All values must be between 0 and n_residues-1."
)

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Formatting: the closing parenthesis for the raise ValueError(...) call is indented inconsistently with other multi-line raises in this file (e.g., Universe.load_new), and there appears to be trailing whitespace on the blank line following it. Align the closing ) with the raise and remove trailing whitespace to match the file's style.

Copilot uses AI. Check for mistakes.
Comment on lines 1872 to 1875
with pytest.raises(ValueError, match="atom_resindex contains invalid residue indices."):
mda.Universe.empty(n_atoms=2, n_residues=2, atom_resindex=[0, 2], trajectory=True)

with pytest.raises(ValueError, match="residue_segindex contains invalid segment indices."):
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

pytest.raises(..., match=...) treats match as a regex. The trailing . in the pattern will match any character, not a literal period, making the assertion looser than intended. Escape the dot (or use re.escape(...)) so the test strictly matches the error message.

Suggested change
with pytest.raises(ValueError, match="atom_resindex contains invalid residue indices."):
mda.Universe.empty(n_atoms=2, n_residues=2, atom_resindex=[0, 2], trajectory=True)
with pytest.raises(ValueError, match="residue_segindex contains invalid segment indices."):
with pytest.raises(ValueError, match="atom_resindex contains invalid residue indices\."):
mda.Universe.empty(n_atoms=2, n_residues=2, atom_resindex=[0, 2], trajectory=True)
with pytest.raises(ValueError, match="residue_segindex contains invalid segment indices\."):

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add guards to index assignment in Universe creation

1 participant