Stochastic Network Utilities

Network analysis and transformation utilities.

The sn module contains utility functions for stochastic networks, including routing probability calculations, visit ratios, and network transformations.

Key function categories:

Service Network (SN) utilities.

Native Python implementations for stochastic network structure analysis, validation, and parameter extraction.

Key classes:

NetworkStruct: Data structure summarizing network characteristics SnGetDemandsResult: Result of sn_get_demands_chain calculation

Key functions:

sn_get_demands_chain: Aggregate class-level parameters into chain-level sn_has_*: Network property predicates sn_is_*: Model type checks sn_get_*: Parameter extraction sn_validate: Check a NetworkStruct against its contract (sn_contract)

class MatrixArray(input_array)[source]

Bases: ndarray

Numpy array subclass with .get() and .set() methods for API compatibility.

Provides compatibility with the wrapper mode that uses JLine’s Matrix class which has get(i, j) and set(i, j, value) methods.

Create MatrixArray from existing array.

static __new__(cls, input_array)[source]

Create MatrixArray from existing array.

__array_finalize__(obj)[source]

Handle view casting and new-from-template.

__getitem__(key)[source]

Override indexing to handle 2D indexing on 1D arrays.

This provides compatibility with MATLAB-style row/column vectors where a 1D array can be indexed as (0, j) or (i, 0).

__setitem__(key, value)[source]

Override item setting to handle 2D indexing on 1D arrays.

get(i, j=None)[source]

Get element at index (i, j) or just i if 1D.

Parameters:
  • i – Row index (or element index for 1D)

  • j – Column index (optional, for 2D arrays)

Returns:

Element value at the specified index

set(i, j, value=None)[source]

Set element at index (i, j) or just i if 1D.

Parameters:
  • i – Row index (or element index for 1D)

  • j – Column index or value (for 1D arrays)

  • value – Value to set (optional, for 2D arrays)

class NetworkStruct(nstations=0, nstateful=0, nnodes=0, nclasses=0, nchains=0, nclosedjobs=0, njobs=<factory>, nservers=<factory>, cap=None, classcap=None, rates=<factory>, scv=<factory>, phases=None, phasessz=None, phaseshift=None, visits=<factory>, nodevisits=<factory>, inchain=<factory>, chains=<factory>, refstat=<factory>, refclass=<factory>, sched=<factory>, schedparam=None, routing=<factory>, rt=None, rtnodes=None, nodetype=<factory>, isstation=<factory>, isstateful=<factory>, isstatedep=None, sdr=None, hassetup=<factory>, nodeToStation=<factory>, nodeToStateful=<factory>, stationToNode=<factory>, stationToStateful=<factory>, statefulToNode=<factory>, statefulToStation=<factory>, state=<factory>, stateprior=<factory>, space=<factory>, lldscaling=None, cdscaling=None, cdscalingpeak=None, jdscaling=None, jdscalingpeak=None, gdscaling=None, gdscalingpeak=None, gdscalingcutoff=None, classprio=None, classdeadline=None, classspawn=None, isslc=None, issignal=None, signaltarget=None, signaltype=None, syncreply=None, replyblock=None, immfeed=None, signalremdist=None, arrivalbatch=None, signalrempolicy=None, iscatastrophe=None, connmatrix=None, nodenames=<factory>, classnames=<factory>, mu=None, phi=None, proc=None, isph=None, pie=None, procid=None, lst=None, fj=None, fjsync=None, fjclassmap=None, isfjaugmented=False, fjauxclass=None, droprule=None, nregions=0, region=None, regionrule=None, regionweight=None, regionsz=None, regionmaxmem=None, regionmembers=None, regionlincon=None, sync=None, gsync=None, nodeparam=None, routingweights=None, reward=None, rtorig=None, rt_visits=None, csmask=None, nvars=None, isbasblocking=None, isbasdestination=None, impatienceType=None, impatienceMu=None, impatienceClass=None, impatiencePhi=None, impatiencePhases=None, impatienceProc=None, impatiencePie=None, impatienceDist=None, batchRejectProb=None, balkingStrategy=None, balkingThresholds=None, retrialType=None, retrialMu=None, retrialPhi=None, retrialProc=None, retrialMaxAttempts=None, retrialPolicy=None, orbitMaxJobs=None, orbitImpatience=None, hasbreakdown=None, breakdownMu=None, repairMu=None, breakdownProc=None, repairProc=None, downServiceRates=None, varsparam=None, markidx=None)[source]

Bases: object

Data structure summarizing network characteristics.

Native Python equivalent of the MATLAB/JAR NetworkStruct. It contains all parameters needed by solvers to analyze a queueing network.

Variables:
  • nstations (int) – Number of stations (queues, delays, sources, joins, places)

  • nstateful (int) – Number of stateful nodes

  • nnodes (int) – Total number of nodes

  • nclasses (int) – Number of job classes

  • nchains (int) – Number of chains (routing chains)

  • nclosedjobs (int) – Total number of jobs in closed classes

  • njobs (numpy.ndarray) – (1, K) Population per class (inf for open classes)

  • nservers (numpy.ndarray) – (M, 1) Number of servers per station

  • rates (numpy.ndarray) – (M, K) Service rates

  • scv (numpy.ndarray) – (M, K) Squared coefficient of variation

  • visits (Dict[int, numpy.ndarray]) – Dict[int, ndarray] - Chain ID -> (M, K) visit ratios

  • inchain (Dict[int, numpy.ndarray]) – Dict[int, ndarray] - Chain ID -> class indices in chain

  • chains (numpy.ndarray) – (K, 1) Chain membership per class

  • refstat (numpy.ndarray) – (K, 1) Reference station per class

  • refclass (numpy.ndarray) – (1, C) Reference class per chain

  • sched (Dict[int, int]) – Dict[int, SchedStrategy] - Station ID -> scheduling strategy

  • routing (numpy.ndarray) – (N, K) Routing strategy matrix

  • rt (numpy.ndarray | None) – Routing probability matrix

  • nodetype (List[int]) – List[NodeType] - Node types, (N+R): one FINITE_CAPACITY_REGION entry per finite capacity region after the N physical nodes

  • isstation (numpy.ndarray) – (N+R,) Boolean mask for stations (False on region rows)

  • isstateful (numpy.ndarray) – (N+R,) Boolean mask for stateful nodes (False on region rows)

  • hassetup (numpy.ndarray) – (M, 1) Boolean mask, STATION-indexed: queue stations that carry setup/delay-off times (function stations)

  • nodeToStation (numpy.ndarray) – (N, 1) Node index -> station index mapping

  • nodeToStateful (numpy.ndarray) – (N, 1) Node index -> stateful index mapping

  • stationToNode (numpy.ndarray) – (M, 1) Station index -> node index mapping

  • stationToStateful (numpy.ndarray) – (M, 1) Station index -> stateful index mapping

  • statefulToNode (numpy.ndarray) – (S, 1) Stateful index -> node index mapping

  • statefulToStation (numpy.ndarray) – (S, 1) Stateful index -> station index mapping

  • state (Dict[int, numpy.ndarray]) – Dict State per stateful node

  • lldscaling (numpy.ndarray | None) – (M, Nmax) Load-dependent scaling matrix

  • cdscaling (Dict | None) – Class-dependent (product-form) scaling functions beta_{i,r}

  • jdscaling (Dict | None) – Joint-dependent (non-product-form) scaling functions eta_i

  • cap (numpy.ndarray | None) – (M, 1) Station capacities

  • classcap (numpy.ndarray | None) – (M, K) Per-class capacities

  • connmatrix (numpy.ndarray | None) – (N, N) Connection matrix

  • nodenames (List[str]) – List[str] - Node names, (N+R): region names after the nodes

  • classnames (List[str]) – List[str] - Class names

nstations: int = 0
nstateful: int = 0
nnodes: int = 0
nclasses: int = 0
nchains: int = 0
nclosedjobs: int = 0
njobs: ndarray
nservers: ndarray
cap: ndarray | None = None
classcap: ndarray | None = None
rates: ndarray
scv: ndarray
phases: ndarray | None = None
phasessz: ndarray | None = None
phaseshift: ndarray | None = None
visits: Dict[int, ndarray]
nodevisits: Dict[int, ndarray]
inchain: Dict[int, ndarray]
chains: ndarray
refstat: ndarray
refclass: ndarray
sched: Dict[int, int]
schedparam: ndarray | None = None
routing: ndarray
rt: ndarray | None = None
rtnodes: ndarray | None = None
nodetype: List[int]
isstation: ndarray
isstateful: ndarray
isstatedep: ndarray | None = None
sdr: dict | None = None
hassetup: ndarray
nodeToStation: ndarray
nodeToStateful: ndarray
stationToNode: ndarray
stationToStateful: ndarray
statefulToNode: ndarray
statefulToStation: ndarray
state: Dict[int, ndarray]
stateprior: Dict[int, ndarray]
space: Dict[int, ndarray]
lldscaling: ndarray | None = None
cdscaling: Dict | None = None
cdscalingpeak: ndarray | None = None
jdscaling: Dict | None = None
jdscalingpeak: ndarray | None = None
gdscaling: Any | None = None
gdscalingpeak: ndarray | None = None
gdscalingcutoff: int | None = None
classprio: ndarray | None = None
classdeadline: ndarray | None = None
classspawn: ndarray | None = None
isslc: ndarray | None = None
issignal: ndarray | None = None
signaltarget: ndarray | None = None
signaltype: List | None = None
syncreply: ndarray | None = None
replyblock: ndarray | None = None
immfeed: ndarray | None = None
signalremdist: List | None = None
arrivalbatch: List | None = None
signalrempolicy: List | None = None
iscatastrophe: ndarray | None = None
connmatrix: ndarray | None = None
nodenames: List[str]
classnames: List[str]
mu: Dict | None = None
phi: Dict | None = None
proc: Dict | None = None
isph: ndarray | None = None
pie: Dict | None = None
procid: Dict | None = None
lst: Dict | None = None
fj: ndarray | None = None
fjsync: List | None = None
fjclassmap: ndarray | None = None
isfjaugmented: bool = False
fjauxclass: ndarray | None = None
droprule: Dict | None = None
nregions: int = 0
region: List | None = None
regionrule: ndarray | None = None
regionweight: ndarray | None = None
regionsz: ndarray | None = None
regionmaxmem: List | None = None
regionmembers: List | None = None
regionlincon: List | None = None
sync: Dict | None = None
gsync: Dict | None = None
nodeparam: Dict | None = None
routingweights: Dict | None = None
reward: Dict | None = None
rtorig: Dict | None = None
rt_visits: ndarray | None = None
csmask: ndarray | None = None
nvars: ndarray | None = None
isbasblocking: ndarray | None = None
isbasdestination: ndarray | None = None
impatienceType: ndarray | None = None
impatienceMu: ndarray | None = None
impatienceClass: ndarray | None = None
impatiencePhi: ndarray | None = None
impatiencePhases: ndarray | None = None
impatienceProc: List[List[Any]] | None = None
impatiencePie: List[List[Any]] | None = None
impatienceDist: List[List[Any]] | None = None
batchRejectProb: ndarray | None = None
balkingStrategy: ndarray | None = None
balkingThresholds: List[List[Any]] | None = None
retrialType: ndarray | None = None
retrialMu: ndarray | None = None
retrialPhi: ndarray | None = None
retrialProc: List[List[Any]] | None = None
retrialMaxAttempts: ndarray | None = None
retrialPolicy: ndarray | None = None
orbitMaxJobs: ndarray | None = None
orbitImpatience: List[List[Any]] | None = None
hasbreakdown: ndarray | None = None
breakdownMu: ndarray | None = None
repairMu: ndarray | None = None
breakdownProc: List[Any] | None = None
repairProc: List[Any] | None = None
downServiceRates: ndarray | None = None
varsparam: ndarray | None = None
markidx: ndarray | None = None
__setattr__(name, value)[source]

Override to convert numpy arrays to MatrixArray for API compatibility.

__post_init__()[source]

Ensure arrays are MatrixArray (numpy arrays with .get()/.set() methods).

validate()[source]

Validate structural consistency.

Raises:

ValueError – If structural consistency is violated

is_valid()[source]

Check if structure is valid.

Returns:

True if structure passes validation, False otherwise

Return type:

bool

get_chain_population(chain_id)[source]

Get total population in a chain.

Parameters:

chain_id (int) – Chain index (0-based)

Returns:

Total number of jobs in the chain

Return type:

float

is_closed_chain(chain_id)[source]

Check if a chain is closed (finite population).

Parameters:

chain_id (int) – Chain index (0-based)

Returns:

True if chain is closed, False if open

Return type:

bool

is_open_chain(chain_id)[source]

Check if a chain is open (infinite population).

Parameters:

chain_id (int) – Chain index (0-based)

Returns:

True if chain is open, False if closed

Return type:

bool

get_station_indices()[source]

Get indices of station nodes.

Returns:

Array of node indices that are stations

Return type:

ndarray

get_stateful_indices()[source]

Get indices of stateful nodes.

Returns:

Array of node indices that are stateful

Return type:

ndarray

get_scheduling_at_station(station_id)[source]

Get scheduling strategy at a station.

Parameters:

station_id (int) – Station index (0-based)

Returns:

SchedStrategy value

Return type:

int

has_multi_server()[source]

Check if any station has multiple servers.

Infinite servers are delays, not multiserver queues: counting them made every model with a Delay read as multiserver (MATLAB sn_has_multi_server filters them out).

has_load_dependence()[source]

Check if model has load-dependent service rates.

has_class_dependence()[source]

Check if model has class-dependent scaling.

has_joint_dependence()[source]

Check if model has joint-dependent (non-product-form) scaling.

has_open_classes()[source]

Check if model has open (infinite population) classes.

has_closed_classes()[source]

Check if model has closed (finite population) classes.

get_total_population()[source]

Get total population across all closed classes.

get_open_class_indices()[source]

Get indices of open classes.

get_closed_class_indices()[source]

Get indices of closed classes.

copy()[source]

Create a deep copy of this NetworkStruct.

__repr__()[source]

String representation.

property obj

Return self for compatibility with wrapper code that accesses .obj

class NodeType(*values)[source]

Bases: IntEnum

Node types in a queueing network.

THE single definition; lang/base.py and api/sn/network_struct.py import it, and it is the numbering sn.nodetype carries. All three used to declare it: the two above agreed, with a note asking the next maintainer to keep them that way, while this module declared a CamelCase plain Enum with auto() values that matched neither – and a plain Enum member never compares equal to an IntEnum one whatever the numbers say, so the five sites that imported it silently answered False. They are fixed with this collapse: the console’s model kind never reported an SPN or a cache, SolverCTMC’s WAITQ-plus-SPN refusal never fired, SolverFLD’s hide_immediate treated every SPN as a plain queueing network (int() on a plain Enum raises, and the raise was swallowed), and SolverAG’s is_source was always False.

The NUMBERING is native python’s own and deliberately not MATLAB’s, which runs Queue=0, Source=1, …, Sink=-1, Join=-2. Nothing crosses a codebase boundary as an integer here: the JSON wire carries the node type as the STRING toText returns.

SOURCE = 0
SINK = 1
QUEUE = 2
DELAY = 3
JOIN = 5
CACHE = 6
ROUTER = 7
CLASSSWITCH = 8
PLACE = 9
TRANSITION = 10
LOGGER = 11
FINITE_CAPACITY_REGION = 12
static toText(node_type)[source]

Convert node type to text representation.

class SchedStrategy(*values)[source]

Bases: IntEnum

Scheduling strategies for service stations.

THE single definition. lang/base.py and api/sn/network_struct.py import this class; until 2026-09-16 each declared a twin of its own and seven members carried different integers here than there (PSJF, FB, LAS, LRPT, FSP, PAS, OI), so an Enum member from this module never compared equal to the IntEnum member of the same name. The numbering kept below is the one lang/base.py and network_struct.py already agreed on, because that is the one every FSP/PAS/OI caller was reading.

Declared in ascending value order on purpose: toID is positional.

LCFSPI = 3
HOL = 9
LPS = 17
SETF = 18
FCFSPR = 22
EDF = 23
JOIN = 25
EDD = 27
SRPT = 28
SRPTPRIO = 29
LCFSPRIO = 30
LCFSPRPRIO = 31
LCFSPIPRIO = 32
FCFSPRPRIO = 33
FCFSPIPRIO = 34
FSP = 36
PAS = 37
OI = 38
PSJF = 39
FB = 40
LAS = 40
LRPT = 42
FCFSPI = 43
static fromString(obj)[source]
static fromLINEString(sched)[source]
static toID(sched)[source]
static to_feature(strategy)[source]

Convert a scheduling strategy to its feature name for solver support checking.

class RoutingStrategy(*values)[source]

Bases: IntEnum

Strategies for routing jobs between network nodes.

THE single definition. lang/base.py and api/sn/network_struct.py import this class; until 2026-09-17 each declared a twin of its own. All three agreed on every member AND every value, so the two IntEnum twins did compare equal to each other – but this one was a plain Enum, which compares equal to neither, and it is the copy the package exports (from line_solver import RoutingStrategy). So a member handed in by a caller tested False against every RoutingStrategy.X inside lang/ and api/sn/, silently.

Values must match MATLAB’s RoutingStrategy constants for JMT compatibility.

SQ = 6
SDR = 7
static to_feature(strategy)[source]

Convert a routing strategy to its feature name for solver support checking.

class DropStrategy(*values)[source]

Bases: IntEnum

Strategies for handling queue overflow and capacity limits.

THE single definition; lang/base.py and api/sn/network_struct.py import it. All three used to declare it, each carrying a docstring asking the next maintainer to keep them numerically identical – and two of the three were plain Enum against IntEnum here, which never compare equal whatever the numbers say. Values are MATLAB’s DropStrategy constants and the JAR jline.lang.constant.DropStrategy ids, which are the interchange encoding of sn.droprule and sn.regionrule.

The short names are MATLAB’s spelling and the CamelCase ones the JAR’s, and both are kept as MATLAB keeps both: they are the same member, so a name from either codebase reads the same model.

WAITQ = -1
DROP = 1
BAS = 2
BBS = 3
BlockingBeforeService = 3
RSRD = 4
ReServiceOnRejection = 4
RETRIAL = 5
Retrial = 5
RETRIAL_WITH_LIMIT = 6
RetrialWithLimit = 6
sn_get_demands_chain(sn)[source]

Calculate new queueing network parameters after aggregating classes into chains.

Computes chain-level demands, service times, visit ratios, and other parameters by aggregating class-level data based on chain membership.

Parameters:

sn (NetworkStruct) – NetworkStruct object for the queueing network model

Returns:

  • Lchain: (M, C) chain-level demand matrix

  • STchain: (M, C) chain-level service time matrix

  • Vchain: (M, C) chain-level visit ratio matrix

  • alpha: (M, K) class-to-chain weighting matrix

  • Nchain: (1, C) population per chain

  • SCVchain: (M, C) chain-level squared coefficient of variation

  • refstatchain: (C, 1) reference station per chain

Return type:

SnGetDemandsResult containing chain parameters

sn_deaggregate_chain_results(sn, Lchain, ST, STchain, Vchain, alpha, Qchain, Uchain, Rchain, Tchain, Cchain, Xchain)[source]

Calculate class-based performance metrics from chain-level performance measures.

Disaggregates chain-level performance metrics (queue lengths, utilizations, response times, throughputs) to class-level metrics using the aggregation factors (alpha).

Parameters:
  • sn (NetworkStruct) – NetworkStruct object for the queueing network model

  • Lchain (ndarray) – (M, C) Service demands per chain

  • ST (ndarray | None) – (M, K) Mean service times per class (optional, computed from rates if None)

  • STchain (ndarray) – (M, C) Mean service times per chain

  • Vchain (ndarray) – (M, C) Mean visits per chain

  • alpha (ndarray) – (M, K) Class aggregation coefficients

  • Qchain (ndarray | None) – (M, C) Mean queue-lengths per chain (optional)

  • Uchain (ndarray | None) – (M, C) Mean utilization per chain (optional)

  • Rchain (ndarray) – (M, C) Mean response time per chain

  • Tchain (ndarray) – (M, C) Mean throughput per chain

  • Cchain (ndarray | None) – (1, C) Mean system response time per chain. Must be None or empty, as in MATLAB: C is always derived from Little’s law (njobs/X) and a non-empty Cchain raises ValueError.

  • Xchain (ndarray) – (1, C) Mean system throughput per chain

Returns:

  • Q: (M, K) queue lengths

  • U: (M, K) utilizations

  • R: (M, K) response times

  • T: (M, K) throughputs

  • C: (1, K) system response times (Little’s law: njobs/X)

  • X: (1, K) system throughputs

Return type:

SnDeaggregateResult containing class-level performance metrics

Raises:

ValueError – if a non-empty Cchain is supplied (as MATLAB errors).

sn_rt_stations(sn)[source]

Station-to-station routing probabilities and per-station visits.

sn.rt and sn.visits are indexed by STATEFUL node, so a solver that writes traffic equations over stations and indexes them by station index silently reads the wrong rows as soon as the model owns a stateful node that is not a station (Router, Cache, stateful class switch). The returned routing matrix absorbs those nodes,

Pst = P_AA + P_AB * (I - P_BB)^-1 * P_BA,

with A the station rows in station order and B the remaining stateful rows, which is exact because a non-station stateful node holds no jobs: it passes every arrival on instantaneously. When every stateful node is a station the result is sn.rt unchanged.

Parameters:

sn (NetworkStruct) – NetworkStruct

Returns:

Tuple (rt_stations, visits_stations) of shapes (M*K, M*K) and (M, K).

sn_get_arvr_from_tput(sn, TN, TH=None)[source]

Compute average arrival rates at stations from throughputs.

Calculates the average arrival rate at each station in steady-state from the station throughputs and routing matrix.

Parameters:
  • sn (NetworkStruct) – Network structure

  • TN (ndarray) – Average throughputs at stations (M x R)

  • TH (ndarray | None) – Throughput handles (optional)

Returns:

Average arrival rates at stations (M x R)

Return type:

AN

References

Original MATLAB: matlab/src/api/sn/sn_get_arvr_from_tput.m

sn_is_closed_model(sn)[source]

Check if the network model is closed (all finite populations).

A closed model has all finite job populations.

Parameters:

sn (NetworkStruct) – NetworkStruct object

Returns:

True if the network is a closed model

Return type:

bool

sn_is_open_model(sn)[source]

Check if the network model is open (all infinite populations).

An open model has only infinite (open) job classes.

Parameters:

sn (NetworkStruct) – NetworkStruct object

Returns:

True if the network is an open model

Return type:

bool