network module

class panco.descriptor.network.Network(servers: List[Server], flows: List[Flow], arrival_shaping=None)

Bases: object

The class Network encodes a network. A network is described by

  • a list of servers (with minimal and maximal service curves)

  • a list of flows (with a path and an arrival curve)

  • some shaping parameters: if flows are shaped at the entrance of the network.

Parameters:
  • servers – the list of server description

  • flows – the list of flows circulating in the network

  • arrival_shaping (List[(int, List[int], List[TokenBucket])] if not None) – the potential shaping for groups of flows (server, list of flows, maximum service curve)

  • self.num_servers – number of servers in the network (int)

  • self.num_flows – number of flows in the network (int)

  • self.path – list of paths of the flows (List[List[int]])

  • self.predecessors – list of predecessors of the servers (List[List[int]])

  • self.successors – list of successors of the servers (List[List[int]])

  • self.flows_in_server – list of flows crossing each server (List[List[int]])

>>> flows = [Flow([TokenBucket(2, 1)], [0, 1]), Flow([TokenBucket(3, 2)], [1, 0])]
>>> servers = [Server([RateLatency(5, 1)], []), Server([RateLatency(6, 2)], [])]
>>> arrival_shaping = [(0, [0], [TokenBucket(1, 6)])]
>>> network = Network(servers, flows, arrival_shaping)
>>> network.num_servers
2
>>> network.num_flows
2
>>> network.flows == flows
True
>>> network.servers == servers
True
>>> network.path
[[0, 1], [1, 0]]
>>> network.predecessors
[[1], [0]]
>>> network.successors
[[1], [0]]
>>> network.flows_in_server
[[0, 1], [0, 1]]
aggregate_aux(flow_list: List[int], foi: int) List[Flow]

Aggregates flows in flow_list, not being the foi having the same source and destination

Parameters:
  • flow_list – list of flows

  • foi – the flow of interest

Returns:

the list of aggregated flows

aggregate_network(foi: int)

Builds a new network where flows following the same path and having the same shaping / absence of shaping are aggretated together. The flow of interest is never aggregated to others. This method only applies to well-numbered trees

Parameters:

foi – the flow of interest

Returns:

the new network and the new number of the flow of interest

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(3, 4)], [0, 1, 2]),
...          Flow([TokenBucket(1, 2)], [0, 1]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2]), Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> arrival_shaping = [(0, [0, 1, 2, 3], [TokenBucket(0, 20)]), (1, [4, 5], [TokenBucket(0, 5)])]
>>> tandem = Network(servers, flows, arrival_shaping)
>>> tandem.aggregate_network(0)
(<Network:
Flows:
      0: α(t) = min [3 + 4t]; π = [0, 1, 2]
      1: α(t) = min [3 + 4t]; π = [0, 1, 2]
      2: α(t) = min [2 + 4t]; π = [0, 1]
      3: α(t) = min [4 + 2t]; π = [1, 2]
Servers:
      0: β(t) = max [8(t - 1)_+]
         σ(t) = min []
      1: β(t) = max [10(t - 3)_+]
         σ(t) = min [0 + 10t]
      2: β(t) = max [6(t - 0)_+]
         σ(t) = min []>, 0)
>>> tandem.aggregate_network(0)[0].arrival_shaping
[(0, [0, 1, 2], [0 + 20t]), (1, [3], [0 + 5t])]
decomposition(keep_edges: List[int, int])

Decomposition of the network by keeping edges in keep_edges, and cutting the flows. returns a new network with arrival curve[0] for all flows obtained from one flow returns list_first the list of the first path of the initial flows

Parameters:

keep_edges – the edges to keep in the network

Returns:

the new network, the list of the number of flows that are the start of the original flows, the list of edges removed in the network.

>>> flows = [Flow([TokenBucket(1, 1)], [3, 4]), Flow([TokenBucket(2, 2)], [0, 3]),
...          Flow([TokenBucket(3, 3)], [2, 4]), Flow([TokenBucket(4, 4)], [1, 3, 4])]
>>> servers = [Server([RateLatency(10, 1)], []), Server([RateLatency(20, 2)], []),
...            Server([RateLatency(30, 3)], []), Server([RateLatency(40, 3)], [TokenBucket(0, 40)]),
...            Server([RateLatency(50, 5)], [])]
>>> arrival_shaping = [(0, [1], [TokenBucket(10, 0)])]
>>> tree = Network(servers, flows, arrival_shaping)
>>> tree.decomposition([(0, 3), (1, 3), (2, 4)])
(<Network:
Flows:
      0: α(t) = min [1 + 1t]; π = [3]
      1: α(t) = min [1 + 1t]; π = [4]
      2: α(t) = min [2 + 2t]; π = [0, 3]
      3: α(t) = min [3 + 3t]; π = [2, 4]
      4: α(t) = min [4 + 4t]; π = [1, 3]
      5: α(t) = min [4 + 4t]; π = [4]
Servers:
      0: β(t) = max [10(t - 1)_+]
         σ(t) = min []
      1: β(t) = max [20(t - 2)_+]
         σ(t) = min []
      2: β(t) = max [30(t - 3)_+]
         σ(t) = min []
      3: β(t) = max [40(t - 3)_+]
         σ(t) = min [0 + 40t]
      4: β(t) = max [50(t - 5)_+]
         σ(t) = min []>, [0, 2, 3, 4], dict_keys([(3, 4)]))
>>> tree.decomposition([(0, 3), (1, 3), (2, 4)])[0].arrival_shaping
[(0, [2], [10 + 0t]), (4, [1, 5], [0 + 40t])]
property depth: List[int]

Returns the depth of each server in a rooted-forest (the root of each tree is the unique sink of the connected component). Sinks have depth 0 and a node has the depth of its successor + 1

Returns:

the list of depths for each server (List[int])

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.depth
[2, 1, 0]
property edges: defaultdict[Tuple[int, int], List[int]]

Builds the dictionary of the edges of the networks. The keys are a pair in integers and the value is the set of flows crossing that edge.

Returns:

The dictionary of the edges.

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.edges
defaultdict(<class 'list'>, {(0, 1): [0, 1], (1, 2): [0, 2]})
property is_elementary: bool

checks if the network has only paths of length 1

Returns:

True if the network has ony paths of length 1, False otherwise

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.is_elementary
False
>>> flows = [Flow([TokenBucket(3, 4)], [0]), Flow([TokenBucket(1, 2)], [1]),
...          Flow([TokenBucket(2, 1)], [2]),Flow([TokenBucket(2, 1)], [2]), ]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> elementary = Network(servers, flows)
>>> elementary.is_elementary
True
property is_feed_forward: bool

Checks is the network in feed-forward, and the servers numbered in increasing order according to the topological sort of the network

Returns:

True if the network is feed-forward, False otherwise

>>> flows = [Flow([TokenBucket(2, 1)], [0, 1]), Flow([TokenBucket(3, 2)], [1, 0])]
>>> servers = [Server([RateLatency(5, 1)], []), Server([RateLatency(6, 2)], [])]
>>> network = Network(servers, flows)
>>> network.is_feed_forward
False
>>> flows_ff = [Flow([TokenBucket(3, 4)], [0, 1, 3]), Flow([TokenBucket(1, 2)], [0, 2, 3])]
>>> servers_ff = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...               Server([RateLatency(10, 3)], [TokenBucket(0, 10)]), Server([RateLatency(6, 0)], [])]
>>> feed_forward = Network(servers_ff, flows_ff)
>>> feed_forward.is_feed_forward
True
is_sink(server) bool

Checks if the server is a sink (has no successor)

Parameters:

server – the server under consideration

Returns:

True if it is a sink, False otherwise

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.is_sink(2)
True
>>> tandem.is_sink(0)
False
property is_sink_tree: bool

Checks if self is a sink-tree (a tree and all flows end at the last server)

Returns:

True if self is a sink-tree, False otherwise

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.is_sink_tree
False
>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> sink_tree = Network(servers, flows)
>>> sink_tree.is_sink_tree
True
property is_tree: bool

checks if the network is a rooted-forest, and the servers numbered in increasing number

Returns:

True if the network is rooted-forest, False otherwise

>>> flows_ff = [Flow([TokenBucket(3, 4)], [0, 1, 3]), Flow([TokenBucket(1, 2)], [0, 2, 3])]
>>> servers_ff = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...               Server([RateLatency(10, 3)], [TokenBucket(0, 10)]), Server([RateLatency(6, 0)], [])]
>>> feed_forward = Network(servers_ff, flows_ff)
>>> feed_forward.is_tree
False
>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.is_tree
True
property list_loads: List[float]

Computes the load of the servers: the list of the ratio between the sum of arrival rates of flows crossing this server and the service rate of the server.

Returns:

the list of the usage of all servers

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.list_loads == [0.75, 0.7, 5/6]
True
property load: float

Computes the load of the network: the minimum on all servers of the ratio between the sum of arrival rates of flows crossing this server and the service rate of the server.

Returns:

the load / usage of the network

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.load == 5/6
True
make_feed_forward() Network

Transforms a network without cyclic dependencies into a feed-forward network with renumbered the nodes in the topological order.

Returns:

the same network with good numbering. The order of the flows is unchanged.

>>> flows_ff = [Flow([TokenBucket(3, 4)], [1, 0, 2]), Flow([TokenBucket(1, 2)], [1, 3, 2])]
>>> servers_ff = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...               Server([RateLatency(10, 3)], [TokenBucket(0, 10)]), Server([RateLatency(6, 0)], [])]
>>> feed_forward = Network(servers_ff, flows_ff)
>>> feed_forward.make_feed_forward()
<Network:
Flows:
      0: α(t) = min [3 + 4t]; π = [0, 2, 3]
      1: α(t) = min [1 + 2t]; π = [0, 1, 3]
Servers:
      0: β(t) = max [10(t - 3)_+]
         σ(t) = min [0 + 10t]
      1: β(t) = max [6(t - 0)_+]
         σ(t) = min []
      2: β(t) = max [8(t - 1)_+]
         σ(t) = min []
      3: β(t) = max [10(t - 3)_+]
         σ(t) = min [0 + 10t]>
property residual_network: List[Server]

” Computes the residual service curves of all servers (to be used for lower priority flows for example) This method only applies to feed-forward networks

Returns:

the list of residual service curves.

>>> flows = [Flow([TokenBucket(3, 4)], [0]), Flow([TokenBucket(1, 2)], [1]),
...          Flow([TokenBucket(2, 1)], [2]),Flow([TokenBucket(2, 1)], [2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> elementary = Network(servers, flows)
>>> elementary.residual_network
[<Server: β(t) = max [4(t - 2.75)_+]
         σ(t) = min []>
, <Server: β(t) = max [8(t - 3.875)_+]
         σ(t) = min [0 + 10t]>
, <Server: β(t) = max [4(t - 1.0)_+]
         σ(t) = min []>
]
residual_rate(foi: int) float

Computes the residual rate of flow foi: the minimum on all the servers of its path of the difference between the service rate and the other flow’s arrival rate

Parameters:

foi – flow of interest

Returns:

the residual rate

>>> flows = [Flow([TokenBucket(3, 4)], [0, 1, 2]), Flow([TokenBucket(1, 2)], [0, 1]),
...          Flow([TokenBucket(2, 1)], [1, 2])]
>>> servers = [Server([RateLatency(8, 1)], []), Server([RateLatency(10, 3)], [TokenBucket(0, 10)]),
...            Server([RateLatency(6, 0)], [])]
>>> tandem = Network(servers, flows)
>>> tandem.residual_rate(0)
5
sub_network(foi: int)

Builds a sub-network with sink the last server crossed by the flow foi This methods applies only to well-numbered feed-forward networks

Parameters:

foi – the flow of interest

Returns:

the sub-network, the flow number of the foi in this new network, the list of the number

of flows of the original network and the list of the number of servers of the original network.

>>> fls = [Flow([TokenBucket(1, 1)], [3, 4]), Flow([TokenBucket(2, 2)], [0, 3]),
...          Flow([TokenBucket(3, 3)], [2, 4]), Flow([TokenBucket(4, 4)], [1, 3, 4])]
>>> sers = [Server([RateLatency(10, 1)], []), Server([RateLatency(20, 2)], []),
...            Server([RateLatency(30, 3)], []), Server([RateLatency(40, 3)], []),
...            Server([RateLatency(50, 5)], [])]
>>> arr_shaping = [(3, [0], [TokenBucket(0, 10)]), (1, [3], [TokenBucket(0, 20)]),
...                    (2, [2], [TokenBucket(0, 30)]), (0, [1], [TokenBucket(0, 40)])]
>>> tree = Network(sers, fls, arr_shaping)
>>> tree.sub_network(1)
(<Network:
Flows:
      0: α(t) = min [1 + 1t]; π = [2]
      1: α(t) = min [2 + 2t]; π = [0, 2]
      2: α(t) = min [4 + 4t]; π = [1, 2]
Servers:
      0: β(t) = max [10(t - 1)_+]
         σ(t) = min []
      1: β(t) = max [20(t - 2)_+]
         σ(t) = min []
      2: β(t) = max [40(t - 3)_+]
         σ(t) = min []>, 1, [0, 1, 3], [0, 1, 3])
>>> tree.sub_network(1)[0].arrival_shaping
[(2, [0], [0 + 10t]), (1, [2], [0 + 20t]), (0, [1], [0 + 40t])]
unfold(foi: int) Tuple[Network, int]

Unfolds a feed-forward network into a tree, from the last server visited by the flows of interest. This method can be aplied to any acyclic network (not necessarily well-numbered), and will not end if there are cycles.

Parameters:

foi – flow of interest

Returns:

the unfolded network and the new number of the flows of interest

>>> flows = [Flow([TokenBucket(1, 1)], [2, 1, 0, 3]), Flow([TokenBucket(2, 2)], [1, 3])]
>>> servers = [Server([RateLatency(10, 1)], []), Server([RateLatency(20, 2)], [TokenBucket(0, 20)]),
...            Server([RateLatency(30, 3)], []), Server([RateLatency(40, 3)], [])]
>>> arrival_shaping = [(2, [0], [TokenBucket(0, 10)])]
>>> tree = Network(servers, flows, arrival_shaping)
>>> tree.unfold(0)
(<Network:
Flows:
      0: α(t) = min [1 + 1t]; π = [0, 2, 4, 5]
      1: α(t) = min [1 + 1t]; π = [1, 3]
      2: α(t) = min [2 + 2t]; π = [2]
      3: α(t) = min [2 + 2t]; π = [3, 5]
Servers:
      0: β(t) = max [30(t - 3)_+]
         σ(t) = min []
      1: β(t) = max [30(t - 3)_+]
         σ(t) = min []
      2: β(t) = max [20(t - 2)_+]
         σ(t) = min [0 + 20t]
      3: β(t) = max [20(t - 2)_+]
         σ(t) = min [0 + 20t]
      4: β(t) = max [10(t - 1)_+]
         σ(t) = min []
      5: β(t) = max [40(t - 3)_+]
         σ(t) = min []>, 0)
>>> tree.unfold(0)[0].arrival_shaping
[(0, [0], [0 + 10t]), (1, [1], [0 + 10t])]

Performs the backward search of the servers having a path directed to sink.

Parameters:
  • sink – the server from which the backward search is done

  • predecessors – list of the list of predecessors of each server

Returns:

the list of servers having a path to sink (List[int])

>>> backward_search(3, [[2], [0], [1, 3], [1]])
[0, 1, 2, 3]
>>> backward_search(3, [[1], [0], [1, 3], [1]])
[0, 1, 3]
panco.descriptor.network.dfs(u: int, num_servers: int, successors: List[List[int]], state: List[int], queue: List[int], sort: List[int]) Tuple[List[int], List[int], List[int]] | None

Depth-first-search implementation of a graph without cycles

Parameters:
  • u – source node

  • num_servers – size of the network

  • successors – adjacency list of the nodes (list of successors)

  • state – state of the nodes (‘white’/’gray’/’black’)

  • queue – set of nodes queued for analysis

  • sort – list of nodes in the reversed order of end of discovery (when they become ‘black’)

Returns:

the new state after exploration from u, new queue, update order

>>> dfs(4, 6, [[], [0], [1, 4], [], [0, 3, 5], [3]], [0, 0, 0, 0, 0, 0], [], [])
(array([2, 0, 0, 2, 2, 2]), [], [4, 5, 3, 0])
panco.descriptor.network.inverse_permutation(tab: List[int]) List[int]

Function that inverses a list. It gives the position in the decreasing order.

Parameters:

tab – the inverse of the list

Returns:

the permutation of th ordering: tab[inverse_tab[i]] = i

>>> inverse_permutation([1, 3, 0, 2])
[2, 0, 3, 1]
panco.descriptor.network.list_to_str(lis: list) str
panco.descriptor.network.print_list(lis: list)
panco.descriptor.network.reindexing(lis: List[int]) Dict[int, int]

Builds a disciotany from a list. dict[key] is the place of key/

Parameters:

lis – list

Returns:

the dicionnart of the indices of the list

>>> reindexing([3, 1, 4, 6])
{3: 0, 1: 1, 4: 2, 6: 3}
panco.descriptor.network.server_depth(num_servers: int, successors: List[List[int]]) List[int]

Returns the depth of each server in a rooted-forest (the root of each tree is the unique sink of the connected component). Sinks have depth 0 and a node has the depth of its successor + 1 The servers must be ordered by their depth (a node has a successor with higher number)

Parameters:
  • num_servers – the number of servers in the network

  • successors – The list of successors in of each server

Returns:

the list of depths for each server (List[int])

>>> server_depth(8, [[2], [3], [7], [4], [7], [7], [7], []])
[2, 3, 1, 2, 1, 1, 1, 0]
panco.descriptor.network.topological_sort(successors: List[List[int]], num_servers: int) List[int]

Function that sorts the servers in a topological order given by its lists of successors (adjacency)

Parameters:
  • successors – the list of successors of each node

  • num_servers – the number of nodes of the graph

Returns:

the topological order of the nodes, and an error if the graph has cycles.

>>> topological_sort([[], [0], [1, 4], [], [0, 3, 5], [3]], 6)
[2, 4, 5, 3, 1, 0]
panco.descriptor.network.topology(num_servers: int, num_flows: int, path: List[List[int]])

” Topology builds the set of successors and predecessors of the network, also builds for each server the list of flows crossing it.

Parameters:
  • num_servers (int) – the number of servers in the network

  • num_flows (int) – the number of flows in the network

  • path (List[List[int]]) – list of paths followed by the flows

Return predecessors:

the list of the predecessor’s list for each server (List[List[int]])

Return successors:

the list of the successor’s list for each server (List[List[int]])

Return flows_in_server:

the list of flows that cross each server (List[List[int]])

>>> paths = [[0, 1, 3], [3, 2, 0], [1, 2]]
>>> topology(4, 3, paths)
([[2], [0], [1, 3], [1]], [[1], [2, 3], [0], [2]], [[0, 1], [0, 2], [1, 2], [0, 1]])
panco.descriptor.network.trunc_path(path: List[List[int]], list_servers: [List[int]]) List[List[int]]

Computes the sub-path of servers in list_servers for each path in path

Parameters:
  • path – list of paths

  • list_servers – list of servers

Returns:

the list of sub_paths (List[List[int]])

>>> trunc_path([[0, 1, 2, 3], [2, 3, 4], [3, 2, 5, 1]], [0, 1, 3])
[[0, 1, 3], [3], [3, 1]]