Skip to content

Conversation

@sefaaras
Copy link

Description

This PR adds MAGNUS (Multi-Attention Guided Network for Unified Segmentation), a hybrid CNN-Transformer architecture for medical image segmentation.

Key Features

  • Dual-path encoder: CNN path for local features + Vision Transformer path for global context
  • Cross-modal attention fusion: Bidirectional attention between CNN and ViT features
  • Scale-adaptive convolution: Multi-kernel (3, 5, 7) parallel convolutions
  • SE attention: Channel recalibration in decoder blocks
  • Deep supervision: Optional auxiliary outputs for improved training
  • 2D/3D support: Works with both 2D and 3D medical images

New Files

  • monai/networks/nets/magnus.py - Main implementation
  • tests/networks/nets/test_magnus.py - Unit tests (17 tests)

Modified Files

  • monai/networks/nets/__init__.py - Export MAGNUS and components

Usage Example

from monai.networks.nets import MAGNUS

model = MAGNUS(
spatial_dims=3,
in_channels=1,
out_channels=2,
features=(64, 128, 256, 512),
)

Test Results
All 17 unit tests pass ✅

Reference
Aras, E., Kayikcioglu, T., Aras, S., & Merd, N. (2026). MAGNUS: Multi-Attention Guided Network for Unified Segmentation via CNN-ViT Fusion. IEEE Access. DOI: 10.1109/ACCESS.2026.3656667

- Add MAGNUS hybrid CNN-Transformer architecture for medical image segmentation
- Implement CNNPath for hierarchical feature extraction
- Implement TransformerPath for global context modeling
- Add CrossModalAttentionFusion for bidirectional cross-attention
- Add ScaleAdaptiveConv for multi-scale feature extraction
- Add SEBlock for channel recalibration
- Support both 2D and 3D medical images
- Add deep supervision option
- Add comprehensive unit tests

Reference: Aras et al., IEEE Access 2026, DOI: 10.1109/ACCESS.2026.3656667
Signed-off-by: Sefa Aras <[email protected]>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

Adds a new MAGNUS CNN–ViT fusion segmentation model and supporting modules to monai.networks.nets. New classes: MAGNUS, CNNPath, TransformerPath, CrossModalAttentionFusion, ScaleAdaptiveConv, SEBlock, and DecoderBlock. Implements CNN and Transformer encoders, positional embedding interpolation, bidirectional cross-attention fusion with spatial alignment, multi-kernel scale-adaptive convolution, decoder blocks with optional SE and deep supervision, weight initialization, input validations, and a final segmentation head. Exposes these symbols in the package init, adds unit tests for the new components and configurations, and updates documentation to include MAGNUS.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description covers key features, files changed, usage example, and test results. However, the required template sections are incomplete or missing. Complete the checklist items from the template (mark applicable boxes, confirm test runs, document updates). The description content is strong but template structure is not followed.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: introducing MAGNUS, a CNN-Transformer hybrid segmentation model.
Docstring Coverage ✅ Passed Docstring coverage is 97.14% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@monai/networks/nets/magnus.py`:
- Around line 147-166: The transformer path lacks positional embeddings: add a
learnable positional embedding parameter (e.g., self.pos_embed =
nn.Parameter(torch.zeros(1, num_patches, hidden_dim))) initialized (truncated
normal or normal) and sized to match the sequence length produced by
self.embedding (compute num_patches from input spatial dimensions divided by
patch_size or infer from the flattened embedding shape at runtime), then in the
forward pass add this positional embedding to the flattened patch tokens before
passing them into self.transformer; ensure the parameter is registered on the
correct device and that self.norm still applies after the transformer.
🧹 Nitpick comments (4)
monai/networks/nets/magnus.py (2)

37-37: Sort __all__ alphabetically.

Per Ruff RUF022: apply isort-style sorting to __all__.

Proposed fix
-__all__ = ["MAGNUS", "CNNPath", "TransformerPath", "CrossModalAttentionFusion", "ScaleAdaptiveConv"]
+__all__ = ["CNNPath", "CrossModalAttentionFusion", "MAGNUS", "ScaleAdaptiveConv", "TransformerPath"]

703-704: Add strict=True to zip().

Ensures decoder_blocks and cnn_skips have matching lengths, catching bugs if construction changes.

Proposed fix
-        for i, (decoder_block, skip) in enumerate(zip(self.decoder_blocks, cnn_skips)):
+        for i, (decoder_block, skip) in enumerate(zip(self.decoder_blocks, cnn_skips, strict=True)):
tests/networks/nets/test_magnus.py (2)

29-35: Consider importing from monai.networks.nets to verify public exports.

Current imports bypass the public API. Testing via from monai.networks.nets import MAGNUS would also validate the __init__.py exports.


190-194: Add strict=True to zip().

Although length is asserted above, adding strict=True makes intent explicit.

Proposed fix
-        for i, (feat, out) in enumerate(zip(features, outputs)):
+        for i, (feat, out) in enumerate(zip(features, outputs, strict=True)):

Comment on lines 553 to 565
aux_weights: Sequence[float] = (0.4, 0.3, 0.3),
) -> None:
super().__init__()

if spatial_dims not in (2, 3):
raise ValueError(f"spatial_dims must be 2 or 3, got {spatial_dims}.")

self.spatial_dims = spatial_dims
self.in_channels = in_channels
self.out_channels = out_channels
self.features = list(features)
self.deep_supervision = deep_supervision
self.aux_weights = list(aux_weights)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

aux_weights is stored but never used.

The aux_weights parameter is documented and stored as an attribute but not applied anywhere in the model. Either apply them in the forward pass or remove from constructor and document that users should handle weighting externally.

🧰 Tools
🪛 Ruff (0.14.13)

558-558: Avoid specifying long messages outside the exception class

(TRY003)

- Add learnable positional embeddings to TransformerPath for proper spatial reasoning
- Implement dynamic positional embedding interpolation for varying input sizes
- Add positional dropout for regularization
- Update aux_weights docstring to clarify it's for external use only

Addresses CodeRabbit review comments on PR Project-MONAI#8717

Signed-off-by: Sefa Aras <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@monai/networks/nets/magnus.py`:
- Line 37: The __all__ export list in magnus.py omits the public classes SEBlock
and DecoderBlock; update the __all__ variable to include "SEBlock" and
"DecoderBlock" (and reorder the entries alphabetically) so these symbols are
properly exported for external importers; ensure the string names exactly match
the class names SEBlock and DecoderBlock and preserve existing entries like
MAGNUS, CNNPath, TransformerPath, CrossModalAttentionFusion, ScaleAdaptiveConv.
🧹 Nitpick comments (5)
monai/networks/nets/magnus.py (5)

177-200: Unused parameter x in _interpolate_pos_encoding.

Docstring says "for device reference" but it's not used. Either remove it or use x.device if device placement is needed.

Proposed fix (remove unused parameter)
-    def _interpolate_pos_encoding(self, x: torch.Tensor, num_patches: int) -> torch.Tensor:
+    def _interpolate_pos_encoding(self, num_patches: int) -> torch.Tensor:
         """
         Interpolate positional embeddings to match the number of patches.

         Args:
-            x: input tensor for device reference.
             num_patches: target number of patches.

         Returns:
             Interpolated positional embeddings of shape (1, num_patches, hidden_dim).
         """

And update the call site at line 222:

-        pos_embed = self._interpolate_pos_encoding(x_flat, num_patches)
+        pos_embed = self._interpolate_pos_encoding(num_patches)

746-746: Add strict=True to zip() for safety.

Lengths should match by construction, but explicit strictness prevents silent bugs if refactored later.

Proposed fix
-        for i, (decoder_block, skip) in enumerate(zip(self.decoder_blocks, cnn_skips)):
+        for i, (decoder_block, skip) in enumerate(zip(self.decoder_blocks, cnn_skips, strict=True)):

690-705: Minor: Kaiming initialization with nonlinearity="relu" applied to GELU layers.

TransformerEncoderLayer uses GELU activation. While Kaiming init defaults assume ReLU, PyTorch lacks a GELU option. This is acceptable but worth noting.


580-601: Missing Raises section in docstring.

Per coding guidelines, raised exceptions should be documented. ValueError is raised at line 601 but not documented.

Proposed docstring addition

Add to the docstring before Example::

    Raises:
        ValueError: If spatial_dims is not 2 or 3.

250-261: Missing Raises section in docstring.

ValueError is raised when channels is not divisible by num_heads.

Proposed docstring addition

Add to the class docstring:

    Raises:
        ValueError: If channels is not divisible by num_heads.

from monai.networks.blocks import Convolution, UpSample
from monai.networks.layers.utils import get_act_layer, get_norm_layer

__all__ = ["MAGNUS", "CNNPath", "TransformerPath", "CrossModalAttentionFusion", "ScaleAdaptiveConv"]
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

__all__ is missing SEBlock and DecoderBlock.

These are public classes that should be exported. Also, consider sorting alphabetically per static analysis.

Proposed fix
-__all__ = ["MAGNUS", "CNNPath", "TransformerPath", "CrossModalAttentionFusion", "ScaleAdaptiveConv"]
+__all__ = [
+    "CNNPath",
+    "CrossModalAttentionFusion",
+    "DecoderBlock",
+    "MAGNUS",
+    "ScaleAdaptiveConv",
+    "SEBlock",
+    "TransformerPath",
+]
🧰 Tools
🪛 Ruff (0.14.13)

37-37: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

🤖 Prompt for AI Agents
In `@monai/networks/nets/magnus.py` at line 37, The __all__ export list in
magnus.py omits the public classes SEBlock and DecoderBlock; update the __all__
variable to include "SEBlock" and "DecoderBlock" (and reorder the entries
alphabetically) so these symbols are properly exported for external importers;
ensure the string names exactly match the class names SEBlock and DecoderBlock
and preserve existing entries like MAGNUS, CNNPath, TransformerPath,
CrossModalAttentionFusion, ScaleAdaptiveConv.

- Update type annotations to modern Python 3.10+ syntax (X | Y instead of Union)
- Remove unused imports (Optional, Union)
- Add docstrings to all __init__ methods for better coverage
- Apply black and isort formatting
- Fix ruff linting issues

Improves docstring coverage from 72% to 80%+

Signed-off-by: Sefa Aras <[email protected]>
- Export SEBlock and DecoderBlock in __all__ and __init__.py

- Add unit tests for SEBlock and DecoderBlock components

- Fix TransformerEncoder warning with enable_nested_tensor=False

- Add MAGNUS documentation to networks.rst
Copy link
Contributor

@dzenanz dzenanz left a comment

Choose a reason for hiding this comment

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

Thank you for contributing this interesting new architecture!

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.

2 participants