From 0e2b25b8b1178d0269e1204dc00df0c9335e671d Mon Sep 17 00:00:00 2001 From: DRVeyl Date: Mon, 2 Feb 2026 11:29:04 -0500 Subject: [PATCH 1/3] Create Network Partitions --- Telecom/NetworkPartitioner.cs | 71 +++++++++++++++++++++++++++++++++++ Telecom/Telecom.csproj | 1 + Telecom/network.cs | 2 + 3 files changed, 74 insertions(+) create mode 100644 Telecom/NetworkPartitioner.cs diff --git a/Telecom/NetworkPartitioner.cs b/Telecom/NetworkPartitioner.cs new file mode 100644 index 0000000..1c0b06d --- /dev/null +++ b/Telecom/NetworkPartitioner.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; +using CommNet; + +namespace σκοπός { + public class NetworkPartitioner { + public NetworkPartitioner() { } + + public readonly List> partitions_ = new List>(); + public readonly HashSet disconnected_partition_ = new HashSet(); + public readonly Dictionary> node_to_partition_map_ = new Dictionary>(); + + private readonly HashSet nodesToCover_ = new HashSet(); + private readonly HashSet candidates_ = new HashSet(); + + private void ClearPartitions() { + foreach (var partition in partitions_) { + partition.Clear(); + } + } + + private void BuildPartition(CommNode start, HashSet partition, HashSet allNodes) { + candidates_.Add(start); + while (candidates_.Count > 0) { + var candidate = candidates_.First(); + candidates_.Remove(candidate); + partition.Add(candidate); + allNodes.Remove(candidate); + foreach (var node in candidate.Keys.Where(x => allNodes.Contains(x))) { + candidates_.Add(node); + } + } + } + private void MapNodesToPartitions() { + node_to_partition_map_.Clear(); + foreach (var partition in partitions_) { + foreach (var node in partition) { + node_to_partition_map_.Add(node, partition); + } + } + } + public void DiscoverPartitions(IEnumerable network) { + ClearPartitions(); + if (partitions_.Count == 0) { + partitions_.Add(disconnected_partition_); + } + + nodesToCover_.Clear(); + foreach (var n in network) { + nodesToCover_.Add(n); + } + + disconnected_partition_.Clear(); + foreach (var n in nodesToCover_.Where(x => x.Keys.Count == 0)) { + disconnected_partition_.Add(n); + } + nodesToCover_.RemoveWhere(x => x.Keys.Count == 0); + + int numUsedPartitions = 1; + while (nodesToCover_.Count > 0) { + if (++numUsedPartitions > partitions_.Count) { + partitions_.Add(new HashSet()); + } + var partition = partitions_[numUsedPartitions - 1]; + BuildPartition(nodesToCover_.First(), partition, nodesToCover_); + } + + MapNodesToPartitions(); + } + } +} diff --git a/Telecom/Telecom.csproj b/Telecom/Telecom.csproj index aac090e..6c5e145 100644 --- a/Telecom/Telecom.csproj +++ b/Telecom/Telecom.csproj @@ -104,6 +104,7 @@ + diff --git a/Telecom/network.cs b/Telecom/network.cs index 6331681..e153ed5 100644 --- a/Telecom/network.cs +++ b/Telecom/network.cs @@ -191,6 +191,7 @@ private void UpdateConnections() { from station in tx_only_ select station.Comm, from station in rx_only_ select station.Comm, from station in stations_.Values select station.Comm); + partioner_.DiscoverPartitions(network.Nodes); foreach (var connection in connections_.Values) { if (contracted_connections.Contains(connection)) { connection.AttemptConnection(routing_, this, Telecom.Instance.last_universal_time); @@ -245,6 +246,7 @@ public IEnumerable AllGround() { public readonly HashSet rx_only_ = new HashSet(); public string[] names_ = { }; public Routing routing_ = new Routing(); + public readonly NetworkPartitioner partioner_ = new NetworkPartitioner(); public Dictionary> connections_by_contract { get; } = new Dictionary>(); From ecb34f8d0eda618222407e9cdf3d47b9d54c83a7 Mon Sep 17 00:00:00 2001 From: DRVeyl Date: Mon, 2 Feb 2026 11:31:08 -0500 Subject: [PATCH 2/3] Add network partitioner to call parameters --- Telecom/connection.cs | 8 ++++---- Telecom/connection_inspector.cs | 18 +++++++++++++---- Telecom/routing.cs | 20 +++++++++++++++---- TelecomTests/routing_test.cs | 35 +++++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/Telecom/connection.cs b/Telecom/connection.cs index e616594..3d676fc 100644 --- a/Telecom/connection.cs +++ b/Telecom/connection.cs @@ -49,12 +49,12 @@ public void AttemptConnection(Routing routing, Network network, double t) { circuit = routing.FindAndUseAvailableCircuit( network.GetStation(trx_names[0]).Comm, network.GetStation(trx_names[1]).Comm, - latency_limit, data_rate, this); + latency_limit, data_rate, network.partioner_, this); } else { circuit = routing.FindCircuitInIsolation( network.GetStation(trx_names[0]).Comm, network.GetStation(trx_names[1]).Comm, - latency_limit, data_rate); + latency_limit, data_rate, network.partioner_); } basic_service.ReportAvailability(circuit != null, t); actual_latency = circuit?.round_trip_latency; @@ -127,10 +127,10 @@ public void AttemptConnection(Routing routing, Network network, double t) { Routing.Channel[] channels; if (exclusive) { routing.FindAndUseAvailableChannels( - tx, rx, latency_limit, data_rate, out channels, this); + tx, rx, latency_limit, data_rate, network.partioner_, out channels, this); } else { routing.FindChannelsInIsolation( - tx, rx, latency_limit, data_rate, out channels); + tx, rx, latency_limit, data_rate, network.partioner_, out channels); } for (int i = 0; i < channels.Length; ++i) { Routing.Channel channel = channels[i]; diff --git a/Telecom/connection_inspector.cs b/Telecom/connection_inspector.cs index 257a662..eec7eb7 100644 --- a/Telecom/connection_inspector.cs +++ b/Telecom/connection_inspector.cs @@ -276,6 +276,7 @@ protected override void RenderWindowContents(int window_id) { new[]{rx.Comm}, connection_.latency_limit, connection_.data_rate, + telecom_.network.partioner_, out channels); if (channels[0] != null) { capacity_limited = true; @@ -291,6 +292,7 @@ protected override void RenderWindowContents(int window_id) { new[]{rx.Comm}, latency_limit: double.PositiveInfinity, connection_.data_rate, + telecom_.network.partioner_, out channels); bool purely_latency_limited = channels[0] != null; if (purely_latency_limited) { @@ -304,6 +306,7 @@ protected override void RenderWindowContents(int window_id) { new[]{rx.Comm}, connection_.latency_limit, data_rate: 0, + telecom_.network.partioner_, out channels); bool purely_rate_limited = channels[0] != null; if (purely_rate_limited) { @@ -321,6 +324,7 @@ protected override void RenderWindowContents(int window_id) { new[]{rx.Comm}, latency_limit: double.PositiveInfinity, data_rate: 0, + telecom_.network.partioner_, out channels); if (channels[0] != null) { string max_data_rate = RATools.PrettyPrintDataRate( @@ -365,7 +369,8 @@ protected override void RenderWindowContents(int window_id) { trx0.Comm, trx1.Comm, connection_.latency_limit, - connection_.data_rate); + connection_.data_rate, + telecom_.network.partioner_); if (circuit != null) { capacity_limited = true; UnityEngine.GUILayout.Label( @@ -377,12 +382,14 @@ protected override void RenderWindowContents(int window_id) { new[] { trx1.Comm }, connection_.latency_limit, connection_.data_rate, + telecom_.network.partioner_, out Routing.Channel[] forward); telecom_.network.routing_.FindChannelsInIsolation( trx1.Comm, new[] { trx0.Comm }, connection_.latency_limit, connection_.data_rate, + telecom_.network.partioner_, out Routing.Channel[] backward); if (forward[0] != null && backward[0] != null) { capacity_limited = true; @@ -398,7 +405,8 @@ protected override void RenderWindowContents(int window_id) { trx0.Comm, trx1.Comm, round_trip_latency_limit: double.PositiveInfinity, - connection_.data_rate); + connection_.data_rate, + telecom_.network.partioner_); bool purely_latency_limited = circuit != null; if (purely_latency_limited) { UnityEngine.GUILayout.Label( @@ -410,7 +418,8 @@ protected override void RenderWindowContents(int window_id) { trx0.Comm, trx1.Comm, connection_.latency_limit, - one_way_data_rate: 0); + one_way_data_rate: 0, + telecom_.network.partioner_); bool purely_rate_limited = circuit != null; if (purely_rate_limited) { string max_data_rate = RATools.PrettyPrintDataRate( @@ -428,7 +437,8 @@ protected override void RenderWindowContents(int window_id) { trx0.Comm, trx1.Comm, round_trip_latency_limit: double.PositiveInfinity, - one_way_data_rate: 0); + one_way_data_rate: 0, + telecom_.network.partioner_); if (circuit != null) { string max_data_rate = RATools.PrettyPrintDataRate( Math.Min( diff --git a/Telecom/routing.cs b/Telecom/routing.cs index 6c2f6e9..b4a14f6 100644 --- a/Telecom/routing.cs +++ b/Telecom/routing.cs @@ -151,12 +151,14 @@ public Circuit FindCircuitInIsolation( RACommNode source, RACommNode destination, double round_trip_latency_limit, - double one_way_data_rate) { + double one_way_data_rate, + NetworkPartitioner partitioner) { return FindCircuit(source, destination, round_trip_latency_limit, one_way_data_rate, - NetworkUsage.None); + NetworkUsage.None, + partitioner); } public Circuit FindAndUseAvailableCircuit( @@ -164,13 +166,15 @@ public Circuit FindAndUseAvailableCircuit( RACommNode destination, double round_trip_latency_limit, double one_way_data_rate, + NetworkPartitioner partitioner, Connection connection) { Circuit circuit = FindCircuit( source, destination, round_trip_latency_limit, one_way_data_rate, - current_network_usage_); + current_network_usage_, + partitioner); if (circuit != null) { foreach (OrientedLink link in circuit.forward.links) { current_network_usage_.UseLinks( @@ -191,12 +195,14 @@ public PointToMultipointAvailability FindChannelsInIsolation( IList destinations, double latency_limit, double data_rate, + NetworkPartitioner partitioner, out Channel[] channels) { return FindChannels(source, destinations, latency_limit, data_rate, NetworkUsage.None, + partitioner, out channels); } @@ -205,6 +211,7 @@ public PointToMultipointAvailability FindAndUseAvailableChannels( IList destinations, double latency_limit, double data_rate, + NetworkPartitioner partitioner, out Channel[] channels, Connection connection) { PointToMultipointAvailability availability = FindChannels( @@ -213,6 +220,7 @@ public PointToMultipointAvailability FindAndUseAvailableChannels( latency_limit, data_rate, current_network_usage_, + partitioner, out channels); if (availability != Unavailable) { var links_by_tx_antenna = @@ -230,12 +238,14 @@ private Circuit FindCircuit(RACommNode source, RACommNode destination, double round_trip_latency_limit, double one_way_data_rate, - NetworkUsage usage) { + NetworkUsage usage, + NetworkPartitioner partitioner) { if (FindChannels(source, new[]{destination}, round_trip_latency_limit, one_way_data_rate, usage, + partitioner, out Channel[] forward) == Unavailable) { return null; } @@ -249,6 +259,7 @@ private Circuit FindCircuit(RACommNode source, round_trip_latency_limit - forward[0].latency, one_way_data_rate, usage_with_forward_channel, + partitioner, out Channel[] backward) == Unavailable) { return null; } @@ -261,6 +272,7 @@ private PointToMultipointAvailability FindChannels( double latency_limit, double data_rate, NetworkUsage usage, + NetworkPartitioner partitioner, out Channel[] channels) { const double c = 299792458; // TODO(egg): consider using the stock intrusive data structure. diff --git a/TelecomTests/routing_test.cs b/TelecomTests/routing_test.cs index 07e3a2b..0ff289c 100644 --- a/TelecomTests/routing_test.cs +++ b/TelecomTests/routing_test.cs @@ -86,11 +86,13 @@ public void OverlappingDuplex() { MakeLink(w, x, 20e6, 1e6); MakeLink(w, y, 1e6, 20e6); MakeLink(x, y, 20e6, 1e6); + partitioner_.DiscoverPartitions(new[] {v, w, x, y}); // We cannot get a circuit at 20 Mbps. Assert.IsNull(routing_.FindAndUseAvailableCircuit( v, w, round_trip_latency_limit: double.PositiveInfinity, one_way_data_rate: 20e6, + partitioner_, connection: null)); // But we could have simplex at 20 Mbps. @@ -100,6 +102,7 @@ public void OverlappingDuplex() { destinations: new[] {w}, latency_limit: double.PositiveInfinity, data_rate: 20e6, + partitioner_, out Routing.Channel[] v_w)); Assert.AreEqual( Routing.PointToMultipointAvailability.Available, @@ -107,6 +110,7 @@ public void OverlappingDuplex() { destinations: new[] {v}, latency_limit: double.PositiveInfinity, data_rate: 20e6, + partitioner_, out Routing.Channel[] w_v)); CollectionAssert.AreEqual(new[]{x, y, w}, v_w[0].ReceivingStations()); CollectionAssert.AreEqual(new[]{x, y, v}, w_v[0].ReceivingStations()); @@ -116,6 +120,7 @@ public void OverlappingDuplex() { v, w, round_trip_latency_limit: double.PositiveInfinity, one_way_data_rate: 10e6, + partitioner_, connection: null); Assert.IsNotNull(circuit); CollectionAssert.AreEqual(new[]{x, y, w}, @@ -167,12 +172,14 @@ public void AsymmetricBroadcast() { var y = MakeNode("y", 0, +1); MakeLink(v, x, 10e6, 0); MakeLink(v, y, 1e6, 0); + partitioner_.DiscoverPartitions(new[] {v, x, y}); Assert.AreEqual( Routing.PointToMultipointAvailability.Available, routing_.FindAndUseAvailableChannels(source: v, destinations: new[] {x, y}, latency_limit: double.PositiveInfinity, data_rate: 500e3, + partitioner_, out Routing.Channel[] channels, connection: null)); CollectionAssert.AllItemsAreNotNull(channels); @@ -185,6 +192,7 @@ public void AsymmetricBroadcast() { destinations: new[] {x, y}, latency_limit: double.PositiveInfinity, data_rate: 8e6, + partitioner_, out channels, connection: null)); // Another 500 kbps to x only costs us 5% of our power. @@ -194,6 +202,7 @@ public void AsymmetricBroadcast() { destinations: new[] {x}, latency_limit: double.PositiveInfinity, data_rate: 500e3, + partitioner_, out channels, connection: null)); CollectionAssert.AllItemsAreNotNull(channels); @@ -205,6 +214,7 @@ public void AsymmetricBroadcast() { destinations: new[] {x, y}, latency_limit: double.PositiveInfinity, data_rate: 4.5e6, + partitioner_, out channels, connection: null)); Assert.IsNotNull(channels[0]); @@ -236,12 +246,14 @@ public void YBroadcast() { MakeLink(v, w, 1e6, 0); MakeLink(w, x, 1e6, 0); MakeLink(w, y, 1e6, 0); + partitioner_.DiscoverPartitions(new[] {v, w, x, y}); Assert.AreEqual( Routing.PointToMultipointAvailability.Available, routing_.FindAndUseAvailableChannels(source: v, destinations: new[] {x, y}, latency_limit: double.PositiveInfinity, data_rate: 1e6, + partitioner_, out Routing.Channel[] channels, connection: null)); Assert.AreEqual( @@ -273,11 +285,13 @@ public void TradingLatencyForBandwidth() { MakeLink(v, w, 300, 300); MakeLink(v, x, 10e6, 10e6); MakeLink(w, x, 10e6, 10e6); + partitioner_.DiscoverPartitions(new[] {v, w, x}); Routing.Circuit low_latency_circuit = routing_.FindCircuitInIsolation( source: v, destination: w, round_trip_latency_limit: 1e-3, - one_way_data_rate: 110); + one_way_data_rate: 110, + partitioner_); Assert.IsNotNull(low_latency_circuit); CollectionAssert.AreEqual(new[]{w}, low_latency_circuit.forward.ReceivingStations()); @@ -287,12 +301,14 @@ public void TradingLatencyForBandwidth() { source: v, destination: w, round_trip_latency_limit: 400e-3, - one_way_data_rate: 1e6)); + one_way_data_rate: 1e6, + partitioner_)); Routing.Circuit high_bandwidth_circuit = routing_.FindCircuitInIsolation( source: v, destination: w, round_trip_latency_limit: 500e-3, - one_way_data_rate: 1e6); + one_way_data_rate: 1e6, + partitioner_); Assert.IsNotNull(high_bandwidth_circuit); CollectionAssert.AreEqual( new[]{x, w}, @@ -326,12 +342,14 @@ public void DecreasingTheBoundary() { MakeLink(x, t, 10e6, 10e6); MakeLink(t, z, 10e6, 10e6); MakeLink(z, u, 10e6, 10e6); + partitioner_.DiscoverPartitions(new[] {x, y, z, t, u}); Assert.AreEqual( Routing.PointToMultipointAvailability.Available, routing_.FindChannelsInIsolation(source: x, destinations: new[] {u}, latency_limit: double.PositiveInfinity, data_rate: 10e6, + partitioner_, out Routing.Channel[] x_u)); CollectionAssert.AreEqual(new[]{y, z, u}, x_u[0].ReceivingStations()); @@ -354,11 +372,13 @@ public void ShortestPath() { MakeLink(w, y, 10e6, 10e6); MakeLink(w, z, 10e6, 10e6); MakeLink(x, y, 10e6, 10e6); + partitioner_.DiscoverPartitions(new[] {v, w, x, y, z}); Routing.Circuit low_latency_circuit = routing_.FindAndUseAvailableCircuit( source: v, destination: w, round_trip_latency_limit: double.PositiveInfinity, one_way_data_rate: 5e6, + partitioner_, connection: null); Assert.IsNotNull(low_latency_circuit); CollectionAssert.AreEqual( @@ -372,6 +392,7 @@ public void ShortestPath() { destination: w, round_trip_latency_limit: double.PositiveInfinity, one_way_data_rate: 5e6, + partitioner_, connection: null); Assert.IsNotNull(high_latency_circuit); CollectionAssert.AreEqual( @@ -385,7 +406,8 @@ public void ShortestPath() { source: v, destination: w, round_trip_latency_limit: double.PositiveInfinity, - one_way_data_rate: 10e6); + one_way_data_rate: 10e6, + partitioner_); Assert.IsNotNull(mixed_latency_circuit); CollectionAssert.AreEqual( new[]{x, y, w}, @@ -408,11 +430,13 @@ public void BandwidthLimited() { MakeLink(v, x, 4e9, 4e9); MakeLink(v, y, 4e9, 4e9); MakeLink(v, z, 4e9, 4e9); + partitioner_.DiscoverPartitions(new[] {v, w, x, y, z}); Assert.IsNotNull(routing_.FindAndUseAvailableCircuit( source: w, destination: v, round_trip_latency_limit: double.PositiveInfinity, one_way_data_rate: 1e9, + partitioner_, connection: null)); Assert.AreEqual(0.25, routing_.usage.TxPowerUsage(v.FirstDigitalAntenna())); Assert.AreEqual(2e9, routing_.usage.SpectrumUsage(v.FirstDigitalAntenna())); @@ -421,6 +445,7 @@ public void BandwidthLimited() { destination: v, round_trip_latency_limit: double.PositiveInfinity, one_way_data_rate: 1e9, + partitioner_, connection: null)); Assert.AreEqual(0.5, routing_.usage.TxPowerUsage(v.FirstDigitalAntenna())); Assert.AreEqual(4e9, routing_.usage.SpectrumUsage(v.FirstDigitalAntenna())); @@ -431,6 +456,7 @@ public void BandwidthLimited() { destination: v, round_trip_latency_limit: double.PositiveInfinity, one_way_data_rate: 1e9, + partitioner_, connection: null)); } @@ -474,5 +500,6 @@ RACommLink MakeLink( } private Routing routing_ = new Routing(); + private NetworkPartitioner partitioner_ = new NetworkPartitioner(); } } From a8a7ae4211df6a72e19461a757adaca7e2272949 Mon Sep 17 00:00:00 2001 From: DRVeyl Date: Sun, 22 Feb 2026 13:59:37 -0500 Subject: [PATCH 3/3] FindChannels uses partition info --- Telecom/routing.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Telecom/routing.cs b/Telecom/routing.cs index b4a14f6..7d3cfd1 100644 --- a/Telecom/routing.cs +++ b/Telecom/routing.cs @@ -281,23 +281,27 @@ private PointToMultipointAvailability FindChannels( var boundary = new PriorityQueue(); var interior = new HashSet(); + channels = new Channel[destinations.Count]; + int num_channels_to_find = destinations.Where(x => !partitioner.disconnected_partition_.Contains(x) && partitioner.node_to_partition_map_[source] == partitioner.node_to_partition_map_[x]).Count(); + if (partitioner.disconnected_partition_.Contains(source) || num_channels_to_find == 0) { + return PointToMultipointAvailability.Unavailable; + } // Dijkstra’s algorithm without DecreaseKey. distances[source] = 0; boundary.Enqueue(source, 0); previous[source] = null; int rx_found = 0; - channels = new Channel[destinations.Count]; bool is_point_to_multipoint = destinations.Count > 1; while (boundary.TryDequeue(out RACommNode tx, out double tx_distance)) { if (tx_distance != distances[tx]) { // We have already considered `tx` through a shorter path. continue; } + int i = destinations.IndexOf(tx); if (tx_distance > latency_limit * c) { // We have run out of latency, no need to keep searching. return rx_found == 0 ? Unavailable : Partial; - } else if (destinations.Contains(tx)) { - int i = destinations.IndexOf(tx); + } else if (i > -1) { channels[i] = new Channel(); for (OrientedLink link = previous[tx]; link != null; @@ -307,7 +311,7 @@ private PointToMultipointAvailability FindChannels( channels[i].links.Reverse(); channels[i].latency = tx_distance / c; ++rx_found; - if (rx_found == channels.Length) { + if (rx_found == num_channels_to_find) { return PointToMultipointAvailability.Available; } }