Skip to content

fix: stop using InfoNode.index as subnetwork scratch state#398

Draft
antoneri wants to merge 1 commit intomasterfrom
codex/fix-subnetwork-index-side-channel
Draft

fix: stop using InfoNode.index as subnetwork scratch state#398
antoneri wants to merge 1 commit intomasterfrom
codex/fix-subnetwork-index-side-channel

Conversation

@antoneri
Copy link
Copy Markdown
Member

@antoneri antoneri commented Apr 3, 2026

Summary

Stop using InfoNode.index as scratch storage while generating sub-Infomap networks.

InfomapBase::generateSubNetwork(InfoNode&) previously overwrote each child node's index with its temporary position in the new subnetwork, then reused that mutated state to clone internal edges. That worked as a local shortcut, but it also leaked a write to a field that carries real optimizer and tree state elsewhere in the codebase.

This change replaces that side-channel with a local InfoNode* -> position map and adds a regression test that proves subnetwork generation no longer clobbers parent node indices.

Problem

The old implementation did this during subnetwork construction:

  • clone each child node
  • write node.index = childIndex
  • later use edge.source->index / edge.target->index to find the cloned endpoints

That meant generateSubNetwork(...) mutated caller-visible InfoNode.index state as an implementation detail and never restored it.

Why that is a bug:

  • InfoNode.index is not scratch-only state
  • it is used elsewhere for optimizer module assignment, consolidation, and other tree/partition bookkeeping
  • a helper that silently rewrites it creates hidden coupling and makes the surrounding code harder to reason about

Even when the mutation does not immediately surface as a user-visible partition bug, it is still state corruption at the function boundary.

Fix

In src/core/InfomapBase.cpp, replace the temporary node.index write with a local std::unordered_map<InfoNode*, unsigned int>:

  • map original child node pointer -> cloned subnetwork position
  • use that map when cloning internal edges
  • leave the original nodes' index values untouched

Tests

Add a regression in test/cpp/test_infomap_wrapper.cpp that:

  • builds a small partitioned fixture
  • records sentinel index values on the parent module's children
  • generates the subnetwork
  • verifies those sentinel indices are preserved
  • verifies the generated subnetwork clones the module's internal edge set exactly

Also add the required -fno-access-control compile option for the lifecycle test target in CMakeLists.txt so the regression can exercise the internal path directly.

Risk

Medium.

This changes C++ core logic, but the surface is tightly bounded:

  • one helper function
  • no public API changes
  • behavior is preserved except for removing the leaked side effect on InfoNode.index

The new mapping is local to subnetwork generation and does not change the cloned graph semantics.

Verification

Ran:

  • PATH="/opt/homebrew/bin:$PATH" make test-native JOBS=1

Why This Should Merge Independently

This fix stands on its own.

It is not inherently tied to the larger CSR experimentation branch; that work only made the hidden coupling around InfoNode.index more obvious. Removing the side-channel is independently valuable because it:

  • makes generateSubNetwork(...) respect its caller's state
  • removes a hidden dependency on a semantically overloaded field
  • adds a focused regression for a fragile internal path

@antoneri antoneri changed the title core: stop using InfoNode.index as subnetwork scratch state fix stop using InfoNode.index as subnetwork scratch state Apr 3, 2026
@antoneri antoneri changed the title fix stop using InfoNode.index as subnetwork scratch state fix: stop using InfoNode.index as subnetwork scratch state Apr 3, 2026
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.

1 participant