Routing Module
Overview
At a Glance
- Purpose:
Path computation algorithms for optical network routing
- Location:
fusion/modules/routing/- Key Files:
registry.py,k_shortest_path.py,k_path_cache.py- Depends On:
fusion.interfaces.router,fusion.core.properties- Used By:
fusion.core(SDNController),fusion.core.adapters(RoutingAdapter),fusion.pipelines
The routing module provides algorithm implementations for computing paths between source and destination nodes in optical networks. These algorithms are the foundation of the RSA (Routing and Spectrum Assignment) problem.
What this module does:
Computes K-shortest paths between node pairs
Implements advanced routing strategies (congestion-aware, fragmentation-aware, etc.)
Provides 1+1 protection routing for network survivability
Caches pre-computed paths for performance optimization (K-Path Cache)
Exposes a registry pattern for algorithm discovery and selection
When you would work here:
Adding a new routing algorithm
Modifying how paths are selected or weighted
Implementing custom path constraints
Optimizing routing performance
Understanding Legacy vs. Orchestrator
Important
FUSION supports two simulation architectures that coexist. Understanding which path your code uses is critical for making modifications.
The routing module algorithms are used by BOTH architecture paths:
+===========================================================================+
| ROUTING MODULE USAGE |
+===========================================================================+
| |
| use_orchestrator = False use_orchestrator = True |
| (Legacy Path) (Orchestrator Path) |
| |
| +------------------+ +------------------+ |
| | SDNController | | SDNOrchestrator | |
| | (fusion/core/) | | (fusion/core/) | |
| +--------+---------+ +--------+---------+ |
| | | |
| | Direct instantiation | Via RoutingAdapter |
| v v |
| +------------------+ +------------------+ |
| | fusion/core/ | | fusion/core/ | |
| | routing.py | | adapters/ | |
| | (Routing class) | | routing_adapter | |
| +--------+---------+ +--------+---------+ |
| | | |
| | Uses | Wraps |
| v v |
| +-------------------------------------------------------+ |
| | fusion/modules/routing/ | |
| | | |
| | KShortestPath, CongestionAwareRouting, | |
| | OnePlusOneProtection, etc. | |
| +-------------------------------------------------------+ |
| |
+===========================================================================+
Key insight: The algorithms in fusion/modules/routing/ are the same code
used by both paths. The difference is only in how they are invoked.
How Adapters and Pipelines Work with Routing
Component |
Location |
Role |
|---|---|---|
RoutingAdapter |
|
Wraps legacy routing for orchestrator. Converts |
RoutingPipeline |
|
Fresh orchestrator implementation for protected routing (1+1). Does NOT
wrap legacy - uses |
routing_strategies.py |
|
Strategy pattern implementations for orchestrator path selection. |
When to modify which:
Adding a new algorithm ->
fusion/modules/routing/+ registerChanging adapter behavior ->
fusion/core/adapters/routing_adapter.pyNew orchestrator-only features ->
fusion/pipelines/
Key Concepts
- K-Shortest Paths
The foundational routing approach. Computes the K shortest paths (by hop count or physical length) between two nodes. Most other algorithms build on this.
- Path Weight
The metric used to rank paths. Can be
hops(hop count),length(physical distance in km), or custom weights based on network state.- Modulation Format Selection
Each path has a maximum reach determined by physical constraints. Longer paths require more robust (but less efficient) modulation formats. The routing module coordinates with modulation configuration to filter valid formats.
- Link Disjointness
For protection, paths must not share any common links. This ensures a single failure cannot affect both working and backup paths.
- Action Masking
In RL contexts, action masking prevents selection of infeasible paths (e.g., paths with insufficient spectrum or that traverse failed links).
Tip
If you’re new to optical networking, the key insight is that routing is more constrained than IP routing. Paths must satisfy physical layer constraints (distance, SNR, available spectrum) in addition to connectivity.
Architecture
Module Structure
fusion/modules/routing/
|-- __init__.py # Public API exports
|-- README.md # Module overview
|-- registry.py # Algorithm discovery and creation
|-- k_shortest_path.py # Base K-SP routing (foundation)
|-- k_path_cache.py # Pre-computed path caching
|-- congestion_aware.py # Load-balanced routing
|-- least_congested.py # Bottleneck-aware routing
|-- fragmentation_aware.py # Spectrum fragmentation routing
|-- nli_aware.py # Non-linear impairment aware
|-- xt_aware.py # Cross-talk aware (multi-core)
|-- one_plus_one_protection.py # 1+1 disjoint protection
|-- utils.py # Helper functions
|
|-- visualization/ # Visualization plugin (BETA)
| |-- __init__.py
| `-- routing_plugin.py # Plugin for routing metrics plots
|
`-- tests/ # Unit tests
|-- __init__.py
|-- test_k_path_cache.py
`-- test_one_plus_one_protection.py
Data Flow
1. REQUEST ARRIVES
|
v
2. ALGORITHM SELECTION (via registry)
|
| create_algorithm("k_shortest_path", engine_props, sdn_props)
v
3. PATH COMPUTATION
|
| algorithm.route(source, destination, request)
v
4. RESULTS STORED IN route_props
|
| - paths_matrix: List of candidate paths
| - modulation_formats_matrix: Valid modulations per path
| - weights_list: Path weights (distance or hops)
v
5. CONSUMER READS route_props
|
| (SDNController or RoutingAdapter)
v
6. SPECTRUM ASSIGNMENT PROCEEDS
Components
registry.py
- Purpose:
Centralized registry for routing algorithm discovery and instantiation
- Key Classes:
RoutingRegistry,ROUTING_ALGORITHMS- Key Functions:
create_algorithm(),list_routing_algorithms(),get_routing_algorithm_info()
The registry pattern enables dynamic algorithm selection based on configuration:
from fusion.modules.routing import (
RoutingRegistry,
create_algorithm,
list_routing_algorithms,
)
# List available algorithms
algorithms = list_routing_algorithms()
# ['k_shortest_path', 'congestion_aware', 'least_congested', ...]
# Create an algorithm instance
router = create_algorithm("k_shortest_path", engine_props, sdn_props)
# Route a request
router.route(source="0", destination="5", request=request_obj)
# Access results
paths = router.route_props.paths_matrix
Registered Algorithms:
Name |
Description |
|---|---|
|
Basic K-shortest paths by hop count or length |
|
Routes avoiding congested links based on utilization |
|
Selects path with least congested bottleneck link |
|
Routes considering spectrum fragmentation levels |
|
Non-linear impairment aware routing |
|
Cross-talk aware routing for multi-core fiber |
|
Link-disjoint working and backup paths |
k_shortest_path.py
- Purpose:
Foundation algorithm - computes K shortest simple paths
- Key Class:
KShortestPath
This is the base routing algorithm that most others extend or call. It uses
NetworkX’s shortest_simple_paths for efficient path enumeration.
from fusion.modules.routing import KShortestPath
router = KShortestPath(engine_props, sdn_props)
router.route(source=0, destination=5, request=None)
# Results in route_props
paths = router.route_props.paths_matrix # [[0,1,3,5], [0,2,4,5], ...]
weights = router.route_props.weights_list # [120.5, 145.2, ...]
mods = router.route_props.modulation_formats_matrix # [['QPSK', '16-QAM'], ...]
Configuration Options:
k_paths: Number of paths to compute (default: 3)routing_weight: Weight metric -"length"orNonefor hops
k_path_cache.py
- Purpose:
Pre-compute and cache K shortest paths for all node pairs
- Key Class:
KPathCache
What it does:
The K-Path Cache pre-computes K shortest paths for all (source, destination) pairs at initialization time. This trades memory for latency - path lookups become O(1) dictionary access instead of on-demand computation.
When to use it:
Large-scale simulations with many requests
When the same node pairs are queried repeatedly
When path computation latency is a bottleneck
from fusion.modules.routing.k_path_cache import KPathCache
# Pre-compute at initialization
cache = KPathCache(topology=nx_graph, k=4, ordering="hops")
# O(1) path lookup during simulation
paths = cache.get_k_paths(source=0, destination=5)
# [[0,1,3,5], [0,2,4,5], [0,1,2,4,5], [0,3,4,5]]
# Get path features for RL
features = cache.get_path_features(
path=[0,1,3,5],
network_spectrum_dict=spectrum_state,
failure_manager=failure_mgr,
)
# {'path_hops': 3, 'min_residual_slots': 15, 'frag_indicator': 0.23, ...}
Ordering Options:
"hops": Order by hop count (unweighted)"length": Order by physical distance (km)"latency": Order by propagation latency
Memory Considerations:
# Check memory usage
print(f"Cached pairs: {cache.get_cache_size()}")
print(f"Memory estimate: {cache.get_memory_estimate_mb():.2f} MB")
one_plus_one_protection.py
- Purpose:
1+1 disjoint protection routing for network survivability
- Key Class:
OnePlusOneProtection
Note
Status: BETA
The 1+1 protection implementation is currently in BETA and under active development for a future v6.X release. The core algorithm works, but integration with failure recovery and spectrum sharing is still being refined.
How 1+1 Protection Works:
Find ALL link-disjoint paths using max-flow algorithm (Suurballe’s)
Try pairs until finding one where BOTH paths have feasible modulation
Allocate spectrum on BOTH paths simultaneously
Traffic transmitted on both; receiver monitors primary
On failure, fast switchover to backup (default: 50ms)
from fusion.modules.routing import OnePlusOneProtection
router = OnePlusOneProtection(engine_props, sdn_props)
router.route(source=0, destination=5, request=None)
# Results include both paths
primary = router.route_props.paths_matrix[0]
backup = router.route_props.backup_paths_matrix[0]
# Paths are guaranteed link-disjoint
primary_links = set(zip(primary[:-1], primary[1:]))
backup_links = set(zip(backup[:-1], backup[1:]))
assert primary_links.isdisjoint(backup_links)
Configuration Options:
engine_props = {
"protection_settings": {
"protection_switchover_ms": 50.0, # Switchover latency
"revert_to_primary": False, # Revert after repair
}
}
congestion_aware.py / least_congested.py / fragmentation_aware.py
These algorithms extend K-shortest path with dynamic weight updates based on current network state:
CongestionAwareRouting: Weights links by utilization percentage
LeastCongestedRouting: Selects path with least congested bottleneck
FragmentationAwareRouting: Considers spectrum fragmentation levels
All follow the same pattern:
Inherit from
AbstractRoutingAlgorithmOverride
update_weights()to adjust edge weightsCall K-shortest paths with updated weights
nli_aware.py / xt_aware.py
Physical-layer aware routing for advanced optical scenarios:
NLIAwareRouting: Considers non-linear impairment accumulation
XTAwareRouting: Cross-talk aware routing for multi-core fiber (MCF)
These are more specialized and require additional physical parameters in
engine_props.
visualization/ (BETA)
- Purpose:
Visualization plugin for routing algorithm analysis
- Status:
BETA - API may change in future releases
The visualization submodule provides a plugin that extends FUSION’s core visualization system with routing-specific plots:
hop_count_plot: Hop count distribution analysis
path_length_plot: Physical path length trends
computation_time_plot: Algorithm latency with CDF
See Visualization Submodule (BETA) for details.
Warning
The visualization plugin is in BETA. It requires the core visualization
system at fusion/visualization/ to be properly configured. The API
and available plot types may change in future releases.
Development Guide
Getting Started
Read the
AbstractRoutingAlgorithminterface infusion/interfaces/router.pyExamine
k_shortest_path.pyas the reference implementationUnderstand how results are stored in
route_propsLook at existing algorithms for patterns
Adding a New Routing Algorithm
Step 1: Create the algorithm file
# fusion/modules/routing/my_routing.py
"""My custom routing algorithm."""
from typing import Any
import networkx as nx
from fusion.interfaces.router import AbstractRoutingAlgorithm
class MyRouting(AbstractRoutingAlgorithm):
"""
My custom routing algorithm.
Implements [describe what makes it special].
"""
def __init__(self, engine_props: dict[str, Any], sdn_props: Any) -> None:
super().__init__(engine_props, sdn_props)
# Initialize algorithm-specific state
@property
def algorithm_name(self) -> str:
return "my_routing"
@property
def supported_topologies(self) -> list[str]:
return ["NSFNet", "USBackbone60", "Pan-European", "Generic"]
def validate_environment(self, topology: nx.Graph) -> bool:
return nx.is_connected(topology)
def route(self, source: Any, destination: Any, request: Any) -> None:
# Clear previous results
self.route_props.paths_matrix = []
self.route_props.modulation_formats_matrix = []
self.route_props.weights_list = []
# YOUR ROUTING LOGIC HERE
paths = self._compute_paths(source, destination)
# Populate route_props
for path in paths:
self.route_props.paths_matrix.append(path)
# ... modulation formats and weights
def get_paths(self, source: Any, destination: Any, k: int = 1) -> list[list[Any]]:
# Return k paths without populating route_props
return self._compute_paths(source, destination)[:k]
def update_weights(self, topology: nx.Graph) -> None:
# Update edge weights based on network state
pass
def get_metrics(self) -> dict[str, Any]:
return {"algorithm": self.algorithm_name}
Step 2: Register in registry.py
Add to the _register_default_algorithms method in RoutingRegistry:
from .my_routing import MyRouting
algorithm_classes = [
# ... existing algorithms
MyRouting,
]
algorithm_name_mapping = {
# ... existing mappings
MyRouting: "my_routing",
}
Step 3: Export in __init__.py
from .my_routing import MyRouting
__all__ = [
# ... existing exports
"MyRouting",
]
Step 4: Add tests
Create tests/test_my_routing.py following the AAA pattern.
Using K-Path Cache for Performance
If your algorithm needs frequent path lookups:
class MyRouting(AbstractRoutingAlgorithm):
def __init__(self, engine_props, sdn_props):
super().__init__(engine_props, sdn_props)
# Initialize cache once
topology = engine_props.get("topology", sdn_props.topology)
self._cache = KPathCache(
topology=topology,
k=engine_props.get("k_paths", 4),
ordering="hops"
)
def route(self, source, destination, request):
# O(1) lookup instead of on-demand computation
paths = self._cache.get_k_paths(source, destination)
# ... process paths
Configuration
The routing algorithm is selected via configuration:
Option |
Default |
Description |
|---|---|---|
|
|
Algorithm name from registry |
|
|
Number of candidate paths to compute |
|
|
Path weight metric: |
INI Configuration Example:
[simulation_settings]
route_method = congestion_aware
k_paths = 5
routing_weight = length
Testing
- Test Location:
fusion/modules/routing/tests/- Run Tests:
pytest fusion/modules/routing/tests/ -v
Existing Tests:
test_k_path_cache.py: Tests pre-computed path cachingtest_one_plus_one_protection.py: Tests disjoint path finding
Adding New Tests:
# tests/test_my_routing.py
import pytest
import networkx as nx
from fusion.modules.routing.my_routing import MyRouting
@pytest.fixture
def topology():
"""Create test topology."""
G = nx.Graph()
G.add_edges_from([(0, 1), (1, 2), (0, 2), (2, 3)])
return G
def test_my_routing_finds_path(topology):
"""Test that algorithm finds a valid path."""
engine_props = {"topology": topology, "k_paths": 3}
sdn_props = type("SDNProps", (), {"topology": topology})()
router = MyRouting(engine_props, sdn_props)
router.route(source=0, destination=3, request=None)
assert len(router.route_props.paths_matrix) > 0
assert router.route_props.paths_matrix[0][0] == 0
assert router.route_props.paths_matrix[0][-1] == 3
Troubleshooting
Issue: No paths found
- Symptom:
route_props.paths_matrixis empty after callingroute()- Cause:
Source and destination may not be connected, or topology is invalid
- Solution:
Verify topology connectivity with
nx.is_connected(topology)
Issue: All modulation formats are False
- Symptom:
modulation_formats_matrixcontains only[False, False, ...]- Cause:
Path length exceeds maximum reach of all modulation formats
- Solution:
Check
modulation_formats_dictconfiguration formax_lengthvalues
Issue: Protection paths not found
- Symptom:
1+1 routing returns no backup path
- Cause:
Network doesn’t have sufficient edge connectivity (< 2)
- Solution:
Verify
nx.edge_connectivity(topology) >= 2for 1+1 protection
Visualization Submodule (BETA)
Note
Status: BETA
The visualization submodule is in BETA and actively being developed. The API may evolve in future releases.
The visualization submodule provides a plugin that extends FUSION’s core
visualization system (fusion/visualization/) with routing-specific
plot types and metrics.
What It Provides:
hop_count_plot: Hop count analysis with distributionpath_length_plot: Path length trends vs. traffic volumecomputation_time_plot: Algorithm latency with CDF
Usage:
from fusion.visualization.plugins import get_global_registry
# Load the plugin
registry = get_global_registry()
registry.discover_plugins()
registry.load_plugin("routing")
# Generate plots via standard API
from fusion.visualization.application.use_cases.generate_plot import generate_plot
result = generate_plot(
config_path="my_experiment.yml",
plot_type="hop_count_plot",
output_path="plots/hop_count.png",
)
For full details, see the docstrings in routing_plugin.py.