It is difficult to see both the danger and the structural bottlenecks in a network at the same time. Most methods only show one or the other.
It calculates a score that combines the risk of a path with how many different ways there are to get there. It highlights which points in a network are both dangerous and hard to reach.
It allows you to see where a system is most vulnerable to both risk and lack of options.
It was run inside an isolated container with no network access. This is the exact command and the real output it produced — captured process output, not written by a model.
$ python3 risk_adjusted_reachability.py Risk-Adjusted Reachability Scores: A: 1000000.0 B: 0.499999750000125 D: 0.399999920000016 C: 0.33333322222225925
A screenshot of that run.
A clean run proves this does what is shown above, in a CPU-only sandbox. It is a small research demo — not a production tool, and nothing here was published anywhere.
All of it — 83 lines, one file, standard library only.
# Risk-Adjusted Reachability Score
import sys
from collections import deque, defaultdict
class RiskAdjustedReachability:
def __init__(self, graph, source):
self.graph = graph
self.source = source
self.critical_path_weights = {}
self.reachability_scores = {}
self.combined_scores = {}
def calculate_critical_path(self):
max_risk = {} # Tracks maximum cumulative risk to reach each node
visited = set()
def dfs(node, current_risk):
if node in visited:
return
visited.add(node)
max_risk[node] = max(max_risk.get(node, 0), current_risk)
for neighbor, weight in self.graph.get(node, []):
dfs(neighbor, current_risk + weight)
visited.remove(node)
dfs(self.source, 0)
self.critical_path_weights = max_risk
def calculate_reachability(self):
# Count number of shortest paths from source to each node
dist = {node: float('inf') for node in self.graph}
dist[self.source] = 0
paths = defaultdict(int)
paths[self.source] = 1
queue = deque([self.source])
while queue:
node = queue.popleft()
for neighbor, _ in self.graph.get(node, []):
if dist[neighbor] > dist[node] + 1:
dist[neighbor] = dist[node] + 1
paths[neighbor] = paths[node]
queue.append(neighbor)
elif dist[neighbor] == dist[node] + 1:
paths[neighbor] += paths[node]
self.reachability_scores = dict(paths)
def combine_scores(self):
# Combine as weighted product (adjust weights as needed)
combined = {}
for node in self.critical_path_weights:
# Normalize scores (example: divide by max value)
risk_norm = self.critical_path_weights[node]
reach_norm = self.reachability_scores.get(node, 0)
combined[node] = reach_norm / (risk_norm + 1e-6) # Simple multiplication
self.combined_scores = combined
def analyze(self):
self.calculate_critical_path()
self.calculate_reachability()
self.combine_scores()
return self.combined_scores
if __name__ == '__main__':
# Example graph definition
graph = {
'A': [('B', 2), ('C', 3)],
'B': [('D', 1)],
'C': [('D', 2)],
'D': []
}
source_node = 'A'
analyzer = RiskAdjustedReachability(graph, source_node)
analyzer.analyze()
print('Risk-Adjusted Reachability Scores:')
for node, score in analyzer.combined_scores.items():
print(f'{node}: {score}')
# Run with: python3 risk_adjusted_reachability.py