Source code for line_solver.api.npfqn.split_rr

"""
Deterministic (round-robin) traffic splitting degrees.

Port of matlab/src/api/npfqn/npfqn_traffic_split_rr.m.
"""

import numpy as np

from ...constants import RoutingStrategy, GlobalConstants


[docs] def npfqn_traffic_split_rr(sn) -> np.ndarray: """ Deterministic (round-robin) split degree of the departure stream of each station-class. kRR[i, r] = k > 1 means that the class-r departures of station i are dispatched one-in-k by a round-robin node, so that a downstream flow carrying a fraction p of them is the k-fold convolution thinned with probability q = k*p and has SCV 1+p*(d2-k), against the Markovian 1+p*(d2-1). kRR[i, r] = 1 marks an ordinary probabilistic split. Only two topologies admit the deterministic rule: the station dispatches round-robin itself, or it feeds with probability one a router that does and whose pointer no other flow advances. Anything else falls back to k=1. Args: sn: NetworkStruct Returns: (M, K) array of split degrees """ M = sn.nstations K = sn.nclasses kRR = np.ones((M, K)) routing = getattr(sn, 'routing', None) if routing is None or np.asarray(routing).size == 0: return kRR routing = np.asarray(routing) rr_val = int(RoutingStrategy.RROBIN.value) if hasattr(RoutingStrategy.RROBIN, 'value') \ else int(RoutingStrategy.RROBIN) if not np.any(routing == rr_val): return kRR rtnodes = getattr(sn, 'rtnodes', None) rtnodes = np.asarray(rtnodes) if rtnodes is not None else np.zeros((0, 0)) stationToNode = np.asarray(sn.stationToNode).flatten().astype(int) isstation = np.asarray(sn.isstation).flatten() for ist in range(M): ind = int(stationToNode[ist]) for r in range(K): if int(routing[ind, r]) == rr_val: kRR[ist, r] = _rr_degree(sn, ind, r) continue if rtnodes.size == 0: continue # a sure transition into a router that dispatches round-robin dest = np.where(rtnodes[ind * K + r, :] > 0)[0] if dest.size != 1: continue dest = int(dest[0]) jnd = dest // K s = dest - jnd * K if isstation[jnd] > 0 or int(routing[jnd, s]) != rr_val: continue if rtnodes[ind * K + r, dest] < 1 - GlobalConstants.FineTol: continue # the round-robin pointer must be advanced by this stream alone if np.count_nonzero(rtnodes[:, dest] > 0) != 1: continue kRR[ist, r] = _rr_degree(sn, jnd, s) return kRR
def _rr_degree(sn, ind, r) -> int: """Number of destinations the round-robin pointer of (ind, r) cycles through.""" nodeparam = getattr(sn, 'nodeparam', None) if nodeparam is not None and ind in nodeparam: nparam = nodeparam[ind] ol = nparam.get('outlinks', None) if isinstance(nparam, dict) else getattr(nparam, 'outlinks', None) if ol is not None and r < len(ol) and ol[r] is not None and len(np.atleast_1d(ol[r])) > 0: return max(1, len(np.atleast_1d(ol[r]))) connmatrix = getattr(sn, 'connmatrix', None) if connmatrix is not None: cm = np.asarray(connmatrix) if ind < cm.shape[0]: return max(1, int(np.count_nonzero(cm[ind, :] > 0))) return 1