fix(connectivity): robust normalization; clamp coherence to [0,1]#62
Conversation
- Replace zero-clamp with epsilon-clamp for denominators to prevent division by zero - Clip coherence magnitude and imaginary coherence to valid [0,1] range - Add comprehensive tests verifying bounds with edge cases - Handle numerical instability in normalization paths gracefully 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove unused pytest import from test_coherence_bounds.py - Apply black formatting to modified files - Resolve F401 flake8 error 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull Request Overview
This PR improves numerical stability in coherency calculations by implementing robust normalization and mathematical bounds enforcement. It replaces zero-clamp operations with epsilon-clamping to prevent division by zero, and clips coherence values to their valid [0,1] range to ensure mathematically correct outputs.
- Replace zero-clamp with epsilon-clamp for denominators to prevent numerical instability
- Add bounds clipping for coherence magnitude and imaginary coherence to [0,1] range
- Include comprehensive test suite to verify proper bounds handling with edge cases
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| spectral_connectivity/connectivity.py | Core coherency calculations with epsilon-clamping and bounds clipping |
| tests/test_coherence_bounds.py | New comprehensive test suite for bounds verification |
| examples/*.ipynb | Whitespace cleanup removing trailing newlines |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| self._power[..., :, xp.newaxis] * self._power[..., xp.newaxis, :] | ||
| ) | ||
| norm[norm == 0] = xp.nan | ||
| norm = xp.maximum(norm, xp.finfo(norm.dtype).eps) |
There was a problem hiding this comment.
Using xp.finfo(norm.dtype).eps repeatedly could be inefficient. Consider caching this value once since norm.dtype is consistent within the method.
| norm = xp.maximum(norm, xp.finfo(norm.dtype).eps) | |
| eps = xp.finfo(norm.dtype).eps | |
| norm = xp.maximum(norm, eps) |
| denominator = xp.sqrt( | ||
| self._power[..., :, xp.newaxis] * self._power[..., xp.newaxis, :] | ||
| ) | ||
| denominator = xp.maximum(denominator, xp.finfo(denominator.dtype).eps) |
There was a problem hiding this comment.
Similar to the coherency method, xp.finfo(denominator.dtype).eps could be cached to avoid repeated computation since the dtype remains constant.
| denominator = xp.maximum(denominator, xp.finfo(denominator.dtype).eps) | |
| eps = xp.finfo(denominator.dtype).eps | |
| denominator = xp.maximum(denominator, eps) |
| ).astype(np.complex128) | ||
|
|
||
| # Add very small values that could cause numerical issues | ||
| fourier_coefficients[0, 0, 0, 0, :] = 1e-15 |
There was a problem hiding this comment.
The magic number 1e-15 should be defined as a named constant (e.g., SMALL_VALUE = 1e-15) to improve code readability and maintainability.
| ).astype(np.complex128) | ||
|
|
||
| # Add edge case with very small power | ||
| fourier_coefficients[0, 0, 0, 0, :] = 1e-16 |
There was a problem hiding this comment.
The magic number 1e-16 should be defined as a named constant to improve code readability and maintainability, similar to the 1e-15 value used elsewhere.
Merged latest master branch changes. Notebook conflict resolved by taking master version. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Document epsilon-clamping and bounds clipping changes in PR #62. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Summary
Changes
xp.maximum(norm, xp.finfo().eps)instead ofnorm[norm == 0] = xp.nanxp.clip(magnitude, 0, 1)to ensure valid boundsRisks & Mitigations
Test plan
🤖 Generated with Claude Code