Skip to content

Comments

Add test for model unload/reload lifecycle#732

Merged
neworderofjamie merged 2 commits intogenn-team:masterfrom
nishantraghuvanshi:feature/test-model-lifecycle-587
Jan 16, 2026
Merged

Add test for model unload/reload lifecycle#732
neworderofjamie merged 2 commits intogenn-team:masterfrom
nishantraghuvanshi:feature/test-model-lifecycle-587

Conversation

@nishantraghuvanshi
Copy link
Contributor

Tests for Issue #587 to verify that models can be cleanly unloaded
and reloaded with proper cleanup of variable references.

Test creates a comprehensive model with:

  • Neuron populations
  • Synapse populations with STDP
  • Current sources
  • Custom updates

Verifies:

  • Model loads and runs correctly
  • After unload, all variable views/arrays are None
  • Model can be reloaded
  • Reloaded model runs correctly

The critical check ensures all variable references to the shared library are cleared, allowing the library to unload and free memory properly.

Closes #587

  Tests for Issue genn-team#587 to verify that models can be cleanly unloaded
  and reloaded with proper cleanup of variable references.

  Test creates comprehensive model with:
  - Neuron populations
  - Synapse populations with STDP
  - Current sources
  - Custom updates

  Verifies:
  - Model loads and runs correctly
  - After unload, all variable views/arrays are None
  - Model can be reloaded
  - Reloaded model runs correctly

  The critical check ensures all variable references to the shared
  library are cleared, allowing the library to properly unload and
  free memory.

  Closes genn-team#587
Copy link
Contributor

@neworderofjamie neworderofjamie left a comment

Choose a reason for hiding this comment

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

Nice work! Just a few minor comments and I'll merge

Comment on lines 114 to 137
# Check neuron population variables
for var_name, var in pre.vars.items():
assert var._view is None, f"pre.vars[{var_name}]._view not cleared"
assert var._array is None, f"pre.vars[{var_name}]._array not cleared"

for var_name, var in post.vars.items():
assert var._view is None, f"post.vars[{var_name}]._view not cleared"
assert var._array is None, f"post.vars[{var_name}]._array not cleared"

# Check synapse population variables
for var_name, var in syn.vars.items():
assert var._view is None, f"syn.vars[{var_name}]._view not cleared"
assert var._array is None, f"syn.vars[{var_name}]._array not cleared"

# Check current source variables
for var_name, var in cs.vars.items():
assert var._view is None, f"cs.vars[{var_name}]._view not cleared"
assert var._array is None, f"cs.vars[{var_name}]._array not cleared"

# Check custom update variables
cu_data = model.custom_updates["TestUpdate"]
for var_name, var in cu_data.vars.items():
assert var._view is None, f"custom_update.vars[{var_name}]._view not cleared"
assert var._array is None, f"custom_update.vars[{var_name}]._array not cleared"
Copy link
Contributor

Choose a reason for hiding this comment

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

why not put this in a function that can test either than views and arrays are or aren't None and use this whenever you test between unloads

Comment on lines 147 to 168
# Reset state to initial conditions for reproducibility
pre.vars["V"].view[:] = -70.0
pre.vars["RefracTime"].view[:] = 0.0
post.vars["V"].view[:] = -70.0
post.vars["RefracTime"].view[:] = 0.0
pre.vars["V"].push_to_device()
pre.vars["RefracTime"].push_to_device()
post.vars["V"].push_to_device()
post.vars["RefracTime"].push_to_device()

# Run simulation again
for _ in range(10):
model.step_time()
model.custom_update("CustomUpdate")

# Get reloaded values
reloaded_pre_V = pre.vars["V"].view[:].copy()
reloaded_post_V = post.vars["V"].view[:].copy()

# Verify reloaded model produces valid results
assert np.isfinite(reloaded_pre_V).all(), "Reloaded pre values contain NaN/Inf"
assert np.isfinite(reloaded_post_V).all(), "Reloaded post values contain NaN/Inf"
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is relevant to this test

Suggested change
# Reset state to initial conditions for reproducibility
pre.vars["V"].view[:] = -70.0
pre.vars["RefracTime"].view[:] = 0.0
post.vars["V"].view[:] = -70.0
post.vars["RefracTime"].view[:] = 0.0
pre.vars["V"].push_to_device()
pre.vars["RefracTime"].push_to_device()
post.vars["V"].push_to_device()
post.vars["RefracTime"].push_to_device()
# Run simulation again
for _ in range(10):
model.step_time()
model.custom_update("CustomUpdate")
# Get reloaded values
reloaded_pre_V = pre.vars["V"].view[:].copy()
reloaded_post_V = post.vars["V"].view[:].copy()
# Verify reloaded model produces valid results
assert np.isfinite(reloaded_pre_V).all(), "Reloaded pre values contain NaN/Inf"
assert np.isfinite(reloaded_post_V).all(), "Reloaded post values contain NaN/Inf"

)


def test_model_load_unload_cycle(make_model, backend):
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
def test_model_load_unload_cycle(make_model, backend):
pytest.mark.parametrize("precision", [types.Double, types.Float])
def test_model_load_unload_cycle(make_model, backend, precision):

It's not especially relevant to this test but we typically run most tests at different precisions

Comment on lines 13 to 14
create_var_ref,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
create_var_ref,
)
create_var_ref,
types,
)

  Changes:
  1. Created _check_vars_cleared() helper function to reduce code duplication
  2. Removed reset/reproducibility code (not relevant to test)
  3. Added precision parametrization to test both Float and Double
  4. Imported types module from pygenn

  Test results: Both precision variants pass (3.52s total)
  )
Copy link
Contributor

@neworderofjamie neworderofjamie left a comment

Choose a reason for hiding this comment

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

Fantastic! Thank you so much for your work on this

@neworderofjamie neworderofjamie merged commit aff4819 into genn-team:master Jan 16, 2026
1 of 2 checks passed
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.

Testing for unloading of models

2 participants