Core Modules

Networks, nodes, classes, distributions, solvers.

These modules provide the fundamental building blocks for creating and solving queueing models.

Network Models (line_solver.lang)

The lang module contains the core classes for building queueing network models.

Main Classes

class Network(name='LineNetwork')[source]

Bases: Network, Element

Queueing network model for LINE.

The Network class represents a complete queueing network model with nodes, job classes, and routing. It compiles to a NetworkStruct for solver consumption.

Initialize a network.

Parameters:

name (str) – Network name (default: “LineNetwork”)

__init__(name='LineNetwork')[source]

Initialize a network.

Parameters:

name (str) – Network name (default: “LineNetwork”)

add_node(node)[source]

Add a node to the network.

Parameters:

node (Node) – Node to add (Queue, Source, Sink, Delay, Fork, Join, etc.)

Raises:

ValueError – If node already in network (when allow_replace is False)

add_class(jobclass)[source]

Add a job class to the network.

Parameters:

jobclass (JobClass) – Job class to add (OpenClass or ClosedClass)

Raises:

ValueError – If class already in network

addClass(jobclass)[source]

MATLAB-compatible alias for add_class().

remove_class(jobclass)[source]

Remove a job class from the network, in place.

Every node drops the per-class configuration referencing the class (see Node.remove_job_class), the class is dropped from the model, the remaining classes are re-indexed and the struct is invalidated so that the next solve recompiles it. Mirrors MATLAB @MNetwork/removeClass.m and Network.removeClass in the JAR. Use ModelAdapter.remove_class (or Network.without_class) for the non-mutating variant.

Parameters:

jobclass – JobClass to remove, or its 0-based index

Raises:

RuntimeError – if the class is the only class of the network, or the model contains a Cache node

removeClass(jobclass)

Remove a job class from the network, in place.

Every node drops the per-class configuration referencing the class (see Node.remove_job_class), the class is dropped from the model, the remaining classes are re-indexed and the struct is invalidated so that the next solve recompiles it. Mirrors MATLAB @MNetwork/removeClass.m and Network.removeClass in the JAR. Use ModelAdapter.remove_class (or Network.without_class) for the non-mutating variant.

Parameters:

jobclass – JobClass to remove, or its 0-based index

Raises:

RuntimeError – if the class is the only class of the network, or the model contains a Cache node

without_class(jobclass)[source]

Return a copy of this network with a job class removed.

The original model is left untouched, so it stays solvable; use this for ablation studies or a per-class decomposition. Mirrors ModelAdapter.removeClass in MATLAB and the JAR.

Parameters:

jobclass – JobClass to remove, or its 0-based index

Returns:

a copy without the specified class

Return type:

Network

withoutClass(jobclass)

Return a copy of this network with a job class removed.

The original model is left untouched, so it stays solvable; use this for ablation studies or a per-class decomposition. Mirrors ModelAdapter.removeClass in MATLAB and the JAR.

Parameters:

jobclass – JobClass to remove, or its 0-based index

Returns:

a copy without the specified class

Return type:

Network

aggregate_chains(suffix='')[source]

Return a copy of this model with one aggregate class per chain.

All classes belonging to the same chain are merged into a single class, which eliminates class switching. The aggregation is exact for product-form models and approximate otherwise, since one aggregate service process replaces the per-class ones weighted by alpha.

Parameters:

suffix (str) – suffix appended to the aggregate class names

Returns:

(chain_model, alpha, deagg_info), where alpha is the

(nstations, nclasses) matrix of aggregation factors and deagg_info carries the data needed by sn_deaggregate_chain_results to map chain-level metrics back to class-level metrics

Return type:

tuple

aggregateChains(suffix='')

Return a copy of this model with one aggregate class per chain.

All classes belonging to the same chain are merged into a single class, which eliminates class switching. The aggregation is exact for product-form models and approximate otherwise, since one aggregate service process replaces the per-class ones weighted by alpha.

Parameters:

suffix (str) – suffix appended to the aggregate class names

Returns:

(chain_model, alpha, deagg_info), where alpha is the

(nstations, nclasses) matrix of aggregation factors and deagg_info carries the data needed by sn_deaggregate_chain_results to map chain-level metrics back to class-level metrics

Return type:

tuple

Add a link between two nodes.

Parameters:
  • source (Node) – Source node

  • dest (Node) – Destination node

Raises:

ValueError – If nodes not in network

Add multiple links via routing matrix.

Parameters:

routing_matrix (RoutingMatrix | ndarray) – RoutingMatrix or numpy array with probabilities

Create serial links between nodes.

Parameters:

nodes (Node) – Variable number of nodes to link in sequence

add_region(name_or_node, *nodes)[source]

Add a finite capacity region containing the specified nodes.

A finite capacity region constrains the total number of jobs that can be present in a group of nodes simultaneously.

Parameters:
  • name_or_node (str | Node) – Either a name for the region (str) or the first node. If a string is provided, it’s used as the region name. If a node is provided, an auto-generated name is used.

  • *nodes (Node) – Additional nodes to include in the region

Returns:

The created Region object for further configuration

Return type:

Region

Examples

# With auto-generated name (FCR1, FCR2, etc.) fcr = model.add_region(queue1) fcr = model.add_region(queue1, queue2)

# With custom name fcr = model.add_region(‘MyRegion’, queue1) fcr = model.add_region(‘MyRegion’, queue1, queue2)

addRegion(name_or_node, *nodes)[source]

MATLAB-compatible alias for add_region().

property regions: List[Region]

Get the list of finite capacity regions.

get_regions()[source]

Get the list of finite capacity regions.

getRegions()[source]

MATLAB-compatible alias for get_regions().

init_routing_matrix()[source]

Initialize an empty routing matrix for all nodes and classes.

Returns:

RoutingMatrix with zeros (no routing initially)

Return type:

RoutingMatrix

set_checks(val)[source]

Enable or disable link-time model checks (e.g. the OI permutation- invariance check). Mirrors MATLAB model.setChecks.

setChecks(val)

Enable or disable link-time model checks (e.g. the OI permutation- invariance check). Mirrors MATLAB model.setChecks.

Set the routing matrix for the network.

Parameters:

routing_matrix – RoutingMatrix, dict with (from_class, to_class) keys, or 2D list/array (applies same routing to all classes)

Raises:

ValueError – If routing matrix dimensions don’t match network, if any routing probability is negative, or if a node’s outgoing total in some class exceeds 1.

is_routing_ergodic(P=None)[source]

Check if the queueing network routing matrix is ergodic (irreducible).

This checks only the routing structure, not the full CTMC state space. A routing is ergodic if all stations communicate, meaning the routing matrix does not create absorbing states or disconnected components.

Parameters:

P – Optional routing matrix (list of lists). If not provided, it will be retrieved via get_linked_routing_matrix().

Returns:

  • absorbingStations: list of station names that are absorbing

  • transientStations: list of station names that are transient

  • numSCCs: number of strongly connected components

  • isReducible: True if the routing creates a reducible structure

Return type:

Tuple of (is_ergodic, info) where info is a dict containing

get_reducibility_info()[source]

Reducibility structure of the routing, with a suggested repair each.

Twin of the MATLAB @MNetwork/getReducibilityInfo. Builds on is_routing_ergodic, which is the one adjacency build and SCC decomposition; this adds isRoutingErgodic and suggestedFixes.

Returns:

dict with isRoutingErgodic, isReducible, absorbingStations, transientStations, numSCCs and suggestedFixes.

get_absorbing_stations()[source]

The stations that are absorbing: once a job enters, it never leaves.

Twin of the MATLAB @MNetwork/getAbsorbingStations. The Sink is excluded, being legitimately absorbing in an open network.

Returns:

Tuple of (stations, node_indices).

make_ergodic(target_node=None)[source]

A routing matrix that makes the network ergodic.

Twin of the MATLAB @MNetwork/makeErgodic. Every absorbing station is redirected to target_node, defaulting to the first Delay and then to the first non-absorbing station.

Does NOT relink: apply the result with model.link(P). That is deliberate – relinking rebuilds the struct, and the modeller should see the repair before it is applied.

get_routing_matrix()[source]

Get the current routing matrix.

Returns:

RoutingMatrix or None if not set

Return type:

RoutingMatrix | None

get_linked_routing_matrix()[source]

Get the linked routing matrix (rtorig from NetworkStruct).

This returns the original routing matrix in the format used by the compiled NetworkStruct. The matrix is structured as a list of lists: P[r][s] is the routing matrix from class r to class s.

Returns:

List of routing matrices, or None if not available

reset_network(hard=True)[source]

Reset the network configuration.

This clears the routing matrix and struct, allowing the network to be reconfigured. Used by model transformations.

Parameters:

hard (bool) – If True, also clears the routing matrix

get_log_path()[source]

Get the path for logger output files.

Returns:

Path for log files, or temp directory if not set

Return type:

str

set_log_path(path)[source]

Set the path for logger output files.

Parameters:

path (str) – Directory path for log files

getLogPath()

Get the path for logger output files.

Returns:

Path for log files, or temp directory if not set

Return type:

str

setLogPath(path)

Set the path for logger output files.

Parameters:

path (str) – Directory path for log files

Link the network with logging enabled for specified nodes.

This method modifies the network by inserting Logger nodes before and after the logged nodes to capture arrival and departure timestamps.

Ported from MATLAB’s MNetwork.linkAndLog.

Parameters:
  • P – Routing matrix (dict of dicts of numpy arrays)

  • is_node_logged (list) – Boolean list indicating which nodes should be logged

  • log_path (str) – Path for log files (optional, uses model’s log path if not set)

Returns:

Tuple of (loggerBefore, loggerAfter) - lists of Logger nodes created

linkAndLog(P, is_node_logged, log_path=None)

Link the network with logging enabled for specified nodes.

This method modifies the network by inserting Logger nodes before and after the logged nodes to capture arrival and departure timestamps.

Ported from MATLAB’s MNetwork.linkAndLog.

Parameters:
  • P – Routing matrix (dict of dicts of numpy arrays)

  • is_node_logged (list) – Boolean list indicating which nodes should be logged

  • log_path (str) – Path for log files (optional, uses model’s log path if not set)

Returns:

Tuple of (loggerBefore, loggerAfter) - lists of Logger nodes created

get_connection_matrix()[source]

Get the network connection (adjacency) matrix.

Returns:

(N x N) binary matrix where 1 indicates a connection

Return type:

ndarray

nodes()[source]

Get list of all nodes (method alias for compatibility).

get_stations()[source]

Get list of all station nodes.

property classes: List[JobClass]

Get list of all job classes (property for compatibility with MATLAB API).

get_class_names()[source]

Return the list of job-class names, indexed by class.

References

MATLAB: matlab/src/lang/@MNetwork/getClassNames.m

get_node_names()[source]

Return the list of node names, indexed by node.

References

MATLAB: matlab/src/lang/@MNetwork/getNodeNames.m

get_station_names()[source]

Return the list of station names, indexed by station.

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (getStationNames)

get_class_switching_mask()[source]

Return the class-switching mask, a boolean matrix whose entry (r, s) is true only if jobs in class r can switch into class s at some node in the network.

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (getClassSwitchingMask)

has_product_form_solution()[source]

Return whether the model admits a product-form solution.

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (hasProductFormSolution)

get_node_by_name(name)[source]

Get node by name.

Parameters:

name (str) – Node name

Returns:

Node or None if not found

Return type:

Node | None

get_station_by_name(name)[source]

Get station by name.

Parameters:

name (str) – Station name

Returns:

Station or None if not found

Return type:

Station | None

get_class_by_name(name)[source]

Get job class by name.

Parameters:

name (str) – Class name

Returns:

JobClass or None if not found

Return type:

JobClass | None

get_class_index(jobclass)[source]

Get 1-based index of a job class.

Parameters:

jobclass (JobClass | str) – JobClass instance or class name

Returns:

1-based class index

Return type:

int

get_source()[source]

Get Source node (if present).

get_sink()[source]

Get Sink node (if present).

get_index_source_node()[source]

Get the index of the Source node (-1 if not present).

get_index_sink_node()[source]

Get the index of the Sink node (-1 if not present).

has_open_classes()[source]

Check if network has any open classes.

has_closed_classes()[source]

Check if network has any closed classes.

has_fork()[source]

Check if network has any fork nodes.

has_join()[source]

Check if network has any join nodes.

init_used_features()[source]

Initialize the used features set.

set_used_lang_feature(feature)[source]

Set a feature as used in this model.

findSolver(metric='', showAll=False)[source]

Which solvers and solver methods can analyze THIS model.

model.findSolver() # every (solver, method) pair that runs model.findSolver(‘cdf’) # … that returns a passage-time law model.findSolver(‘getCdfRespT’) # the same question, asked by accessor model.findSolver(‘’, True) # also the pairs that are refused, and why

The returned DataFrame has one row per pair, with columns Solver, Method, Runnable, Class (‘exact’, ‘approx’, ‘bound’ or ‘simulation’), Metrics and Reason. Method is the method name to pass as a solver method, so a row can be acted on directly:

T = model.findSolver('cdf')
solver = LINE(model, T.Method[0])

findMethod and help are aliases of this method.

Parameters:
  • metric (str) – measure group (‘cdf’) or accessor (‘getCdfRespT’) to narrow the report to; ‘’ or ‘any’ keeps every pair.

  • showAll (bool) – also list the refused pairs, with the reason each was refused.

Returns:

pandas.DataFrame with the six columns above.

findMethod(metric='', showAll=False)[source]

Alias of findSolver: which solvers and solver methods can analyze this model.

The two names exist because the question is asked both ways round – “which solver do I use” and “which method do I pass” – and the answer is the same table, whose Method column carries the method name either caller needs.

help(metric='', showAll=False)[source]

Alias of findSolver: what can this model be solved with?

find_solver(metric='', showAll=False)

Which solvers and solver methods can analyze THIS model.

model.findSolver() # every (solver, method) pair that runs model.findSolver(‘cdf’) # … that returns a passage-time law model.findSolver(‘getCdfRespT’) # the same question, asked by accessor model.findSolver(‘’, True) # also the pairs that are refused, and why

The returned DataFrame has one row per pair, with columns Solver, Method, Runnable, Class (‘exact’, ‘approx’, ‘bound’ or ‘simulation’), Metrics and Reason. Method is the method name to pass as a solver method, so a row can be acted on directly:

T = model.findSolver('cdf')
solver = LINE(model, T.Method[0])

findMethod and help are aliases of this method.

Parameters:
  • metric (str) – measure group (‘cdf’) or accessor (‘getCdfRespT’) to narrow the report to; ‘’ or ‘any’ keeps every pair.

  • showAll (bool) – also list the refused pairs, with the reason each was refused.

Returns:

pandas.DataFrame with the six columns above.

find_method(metric='', showAll=False)

Alias of findSolver: which solvers and solver methods can analyze this model.

The two names exist because the question is asked both ways round – “which solver do I use” and “which method do I pass” – and the answer is the same table, whose Method column carries the method name either caller needs.

find_binding_capacity()[source]

The first station whose finite capacity can actually BIND, as (binds, node, cap, r, is_open), or binds=False when no buffer in the model can refuse a job. Port of MATLAB MNetwork.findBindingCapacity.

ONE PREDICATE, TWO CALLERS. NetworkSolver.checkBindingCapacity turns the answer into the refusal the product-form solvers raise, and get_used_lang_features marks the registry name ‘FiniteCapacity’ on it, so a solver that does not declare the name refuses exactly the models the structural gate refuses.

The test reads the node-level capacity / class capacity the user set and the class populations from the CLASS OBJECTS, never sn.cap / sn.classcap: _refresh_capacity derives a FINITE classcap (the chain population) for every closed model, so an sn-level test would call every closed model capped, and reading the struct from the recorder would trigger a refresh on every feature query.

Only a capacity that can bind counts. A closed model whose station capacity is at least the total population can never block a job, so the declaration is a no-op (set_capacity(N) on a station of an N-job closed model is a common idiom). The population of an open class is inf, so any finite capacity an open class can reach binds. A Cache model is exempt: Cache sets class_capacity=1 on the retrieval queues it builds, and the cache analyzers solve those rather than treating them as a buffer constraint.

r is the 0-BASED class index of a per-class buffer and -1 for a station-level one; is_open says whether an open class reaches the buffer, which decides the fallback advice.

findBindingCapacity()

The first station whose finite capacity can actually BIND, as (binds, node, cap, r, is_open), or binds=False when no buffer in the model can refuse a job. Port of MATLAB MNetwork.findBindingCapacity.

ONE PREDICATE, TWO CALLERS. NetworkSolver.checkBindingCapacity turns the answer into the refusal the product-form solvers raise, and get_used_lang_features marks the registry name ‘FiniteCapacity’ on it, so a solver that does not declare the name refuses exactly the models the structural gate refuses.

The test reads the node-level capacity / class capacity the user set and the class populations from the CLASS OBJECTS, never sn.cap / sn.classcap: _refresh_capacity derives a FINITE classcap (the chain population) for every closed model, so an sn-level test would call every closed model capped, and reading the struct from the recorder would trigger a refresh on every feature query.

Only a capacity that can bind counts. A closed model whose station capacity is at least the total population can never block a job, so the declaration is a no-op (set_capacity(N) on a station of an N-job closed model is a common idiom). The population of an open class is inf, so any finite capacity an open class can reach binds. A Cache model is exempt: Cache sets class_capacity=1 on the retrieval queues it builds, and the cache analyzers solve those rather than treating them as a buffer constraint.

r is the 0-BASED class index of a per-class buffer and -1 for a station-level one; is_open says whether an open class reaches the buffer, which decides the fallback advice.

get_used_lang_features()[source]

Get the features used by this model.

Returns:

The set of features used by this model.

Return type:

SolverFeatureSet

initUsedFeatures()

Initialize the used features set.

setUsedLangFeature(feature)

Set a feature as used in this model.

getUsedLangFeatures()

Get the features used by this model.

Returns:

The set of features used by this model.

Return type:

SolverFeatureSet

refresh_struct()[source]

Compile model to NetworkStruct for solver consumption.

Always performs a full rebuild of the struct from scratch.

refresh_rates()[source]

Refresh only the service/arrival rates in the cached NetworkStruct.

This is a lightweight alternative to refresh_struct() when only service times have changed (e.g., during iterative solver updates). It updates rates, scv, proc, and procid without recomputing routing, visits, or other structural data.

If no cached struct exists, falls back to full refresh_struct().

refreshRates()

Refresh only the service/arrival rates in the cached NetworkStruct.

This is a lightweight alternative to refresh_struct() when only service times have changed (e.g., during iterative solver updates). It updates rates, scv, proc, and procid without recomputing routing, visits, or other structural data.

If no cached struct exists, falls back to full refresh_struct().

getStateSpace(*args)[source]

Get the CTMC state space of the model.

Delegates to SolverCTMC; results are not cached. For repeated use, build a SolverCTMC(model, …) and call getStateSpace(…) on it.

Returns:

(stateSpace, nodeStateSpace)

get_state_space(*args)[source]

Get the CTMC state space (snake_case alias for getStateSpace).

stateSpace(*args)[source]

Get the CTMC state space (alias for getStateSpace).

reset_struct()[source]

Reset struct compilation flag.

reset()[source]

Reset the network to allow re-configuration.

This resets the routing matrix and struct, allowing nodes to be reconfigured (e.g., changing routing strategies) before re-solving. Also clears solver results from cache nodes to prevent stale hit/miss probabilities from affecting subsequent solver runs.

struct()[source]

Get compiled NetworkStruct (alias for get_struct).

Returns:

NetworkStruct for solver use

Return type:

NetworkStruct

print_routing_matrix(onlyclass=None)[source]

Print the routing matrix of the network.

Displays routing probabilities between nodes and classes in a human-readable format, including class-switching routes.

Parameters:

onlyclass – Optional filter for a specific class

References

MATLAB: MNetwork.printRoutingMatrix Java: Network.printRoutingMatrix

Re-link the network with a new routing matrix.

This is similar to link() but resets the struct first to allow iterative optimization of routing parameters.

Parameters:

routing_matrix (RoutingMatrix) – New RoutingMatrix specifying routing probabilities

set_global_dependence(phi, peak, cutoff=10)[source]

Declare a globally state-dependent service-rate scaling phi(n).

n is the FULL (nstations, nclasses) population matrix, not the population local to one station. This is the Whittle-network primitive: when phi satisfies phi_s(n) phi_t(n-e_s) = phi_t(n) phi_s(n-e_t) the chain is reversible with pi(n) ~ Phi(n) prod rho_s**n_s and is insensitive. It also expresses bandwidth sharing, where one route holds several links at once and no per-station scaling can reproduce the coupling.

phi returns a scalar (broadcast), an (nstations,) column (per station) or an (nstations, nclasses) matrix. The effective rate of class r at station i is its base rate times phi[i, r], composing multiplicatively with any load-, class- or joint-dependence. Only SolverCTMC, SolverSSA and SolverLDES support it.

Parameters:
  • phi – callable over the full population matrix

  • peak – REQUIRED peak scaling (scalar, (nstations,) or (nstations, nclasses)), normalizing utilization as Util = T*S/peak

  • cutoff – per-slot OPEN-class truncation used when phi is materialized onto the JSON wire (closed classes are tabulated up to their own population). It plays no part in solving, and exists because a handle cannot cross a language boundary: the writer needs to know how far the lattice extends. Set it to the cutoff the model is solved at.

get_global_dependence()[source]

Network-level global dependence handle, or None if the model declares none.

get_global_dependence_cutoff()[source]

Per-slot open-class wire truncation of the global dependence, or None if none is declared.

get_global_dependence_peak()[source]

(nstations, nclasses) peak of the global dependence, or None if none is declared.

set_reward(name, reward_fn)[source]

Add a reward function to the network.

Parameters:
  • name (str) – Reward name

  • reward_fn – Callable that takes RewardState and returns value

get_reward(name)[source]

Get reward function by name.

get_rewards()[source]

Get all rewards.

init_default()[source]

Initialize network with default state.

For closed classes, all jobs start at their reference station. For open classes, sources start with potential arrivals.

Delegates to initFromMarginal to generate proper per-node state spaces and state priors, which are needed for CTMC transient analysis.

has_init_state()[source]

Check if network has initialized state.

Mirrors MATLAB MNetwork.hasInitState: the model counts as initialized only when EVERY stateful node carries a state, so a partially initialized model still triggers init_default.

set_state(state)[source]

Set state for nodes.

Parameters:

state (Dict[str, ndarray]) – Dict mapping node names to state vectors

state()[source]

Get current state of all stateful nodes (alias for get_state).

Returns:

Dict mapping node names to state vectors

Return type:

Dict[str, ndarray]

get_state_marginal()[source]

Get the stored marginal state used for CTMC initial state matching.

Returns:

Flat array of marginal job counts [n[0,0], n[0,1], …, n[M-1,K-1]] where n[i,k] is number of jobs of class k at station i. Returns None if no marginal has been set.

Return type:

ndarray

getStateMarginal()

Get the stored marginal state used for CTMC initial state matching.

Returns:

Flat array of marginal job counts [n[0,0], n[0,1], …, n[M-1,K-1]] where n[i,k] is number of jobs of class k at station i. Returns None if no marginal has been set.

Return type:

ndarray

init_from_marginal_and_started(n, s)[source]

Initialize network state from marginal queue lengths and started jobs.

This method generates the full state space for each station based on the marginal job counts, sets the state prior (first state with probability 1 by default), and sets the current state.

Parameters:
  • n – Marginal queue lengths matrix (nstations x nclasses). n[i][r] is the number of jobs of class r at station i.

  • s – Started jobs matrix (nstations x nclasses). s[i][r] is the number of jobs of class r in service at station i.

init_from_marginal_and_running(n, s, options=None)[source]

Initialize network state from marginal queue lengths and RUNNING jobs.

Mirrors MATLAB @MNetwork/initFromMarginalAndRunning. It is not initFromMarginalAndStarted: ‘started’ counts the jobs that have BEGUN service (so a preempted job still counts), while ‘running’ counts the jobs holding a server right now, which is what a load-dependent or multiserver station needs to reproduce a mid-service snapshot.

Parameters:
  • n – Marginal queue lengths (nstations x nclasses, or nnodes rows); n[i][r] is the number of jobs of class r at station i.

  • s – Running jobs, same shape as n.

  • options – unused, accepted for MATLAB call compatibility.

Raises:

ValueError – if the pair (n, s) is not a valid state of this model, or if a stateful node ends up with no state.

initFromMarginalAndRunning(n, s, options=None)

Initialize network state from marginal queue lengths and RUNNING jobs.

Mirrors MATLAB @MNetwork/initFromMarginalAndRunning. It is not initFromMarginalAndStarted: ‘started’ counts the jobs that have BEGUN service (so a preempted job still counts), while ‘running’ counts the jobs holding a server right now, which is what a load-dependent or multiserver station needs to reproduce a mid-service snapshot.

Parameters:
  • n – Marginal queue lengths (nstations x nclasses, or nnodes rows); n[i][r] is the number of jobs of class r at station i.

  • s – Running jobs, same shape as n.

  • options – unused, accepted for MATLAB call compatibility.

Raises:

ValueError – if the pair (n, s) is not a valid state of this model, or if a stateful node ends up with no state.

init_from_avg_qlen(AvgQLen)[source]

Initialize the state from mean queue lengths, rounded to integers.

Mirrors MATLAB @MNetwork/initFromAvgQLen. Rounding each entry independently can overshoot the closed population – round([0.5,0.5]) is [1,1] – so a class whose rounded total exceeds its mean total gives one job back at its fullest station. A marginal that still does not validate falls back to the default initialization, as MATLAB’s does.

Parameters:

AvgQLen – mean queue lengths (nstations x nclasses).

initFromAvgQLen(AvgQLen)

Initialize the state from mean queue lengths, rounded to integers.

Mirrors MATLAB @MNetwork/initFromAvgQLen. Rounding each entry independently can overshoot the closed population – round([0.5,0.5]) is [1,1] – so a class whose rounded total exceeds its mean total gives one job back at its fullest station. A marginal that still does not validate falls back to the default initialization, as MATLAB’s does.

Parameters:

AvgQLen – mean queue lengths (nstations x nclasses).

init_from_avg_table_qlen(AvgTable)[source]

Initialize the state from the QLen column of an average table.

Mirrors MATLAB @MNetwork/initFromAvgTableQLen: the column is stored class-major, so it reshapes to (nclasses, nstations) and transposes.

Parameters:

AvgTable – a DataFrame carrying a ‘QLen’ column, as getAvgTable returns.

initFromAvgTableQLen(AvgTable)

Initialize the state from the QLen column of an average table.

Mirrors MATLAB @MNetwork/initFromAvgTableQLen: the column is stored class-major, so it reshapes to (nclasses, nstations) and transposes.

Parameters:

AvgTable – a DataFrame carrying a ‘QLen’ column, as getAvgTable returns.

init_from_marginal(n, options=None)[source]

Initialize network state from marginal queue lengths only.

Mirrors MATLAB @MNetwork/initFromMarginal, which is NOT initFromMarginalAndStarted with a zero started matrix: it validates the marginal first, and it admits a PURPOSELY FRACTIONAL one.

A fractional row is a fluid initial condition, and it is kept as the state verbatim. Routing it through the discrete state-space generator instead truncates it per station (astype(int)), which silently DROPS JOBS: SolverENV hands a fluid stage the fractional Qentry, so a closed model of 5 jobs was written out holding 4, and MATLAB then refused the document with “Chain 1 is initialized with an incorrect number of jobs”.

Parameters:

n – Marginal queue lengths. Can be: - 1D list/array with one value per station (single class) - 2D list/array (nstations x nclasses)

to_java()[source]

Convert Network for JVM interoperability.

Not available in the native Python implementation. The native Python solver operates independently of the JVM. For JVM interoperability call the canonical JAR (common/jline.jar) directly.

copy()[source]

Create a deep copy of the network.

This method creates a new Network instance with copies of all nodes, classes, and routing. The copied network is independent of the original and can be modified without affecting the original.

This is required for model transformations like Heidelberger-Trivedi (H-T) for fork-join networks.

Returns:

A deep copy of this network

Return type:

Network

Example

>>> model = Network('Original')
>>> # ... build model ...
>>> model_copy = model.copy()
>>> # Modify model_copy without affecting model
getRoutingMatrix()

Get the current routing matrix.

Returns:

RoutingMatrix or None if not set

Return type:

RoutingMatrix | None

getStations()

Get list of all station nodes.

getNodeByName(name)

Get node by name.

Parameters:

name (str) – Node name

Returns:

Node or None if not found

Return type:

Node | None

getSource()

Get Source node (if present).

getSink()

Get Sink node (if present).

getClassNames()

Return the list of job-class names, indexed by class.

References

MATLAB: matlab/src/lang/@MNetwork/getClassNames.m

getNodeNames()

Return the list of node names, indexed by node.

References

MATLAB: matlab/src/lang/@MNetwork/getNodeNames.m

getStationNames()

Return the list of station names, indexed by station.

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (getStationNames)

getClassSwitchingMask()

Return the class-switching mask, a boolean matrix whose entry (r, s) is true only if jobs in class r can switch into class s at some node in the network.

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (getClassSwitchingMask)

hasProductFormSolution()

Return whether the model admits a product-form solution.

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (hasProductFormSolution)

resetStruct()

Reset struct compilation flag.

setReward(name, reward_fn)

Add a reward function to the network.

Parameters:
  • name (str) – Reward name

  • reward_fn – Callable that takes RewardState and returns value

getReward(name)

Get reward function by name.

getRewards()

Get all rewards.

version()

Get the LINE solver version string.

print_struct()[source]

Print a summary of the compiled NetworkStruct (wrapper-compatible).

chain_index(jobclass)

Get the 1-based index of the chain containing a class (object or name).

stateful_index(node)

Get the 1-based index of a node among the stateful nodes (-1 if not stateful).

plot(graph_type='station', method='names', **kwargs)[source]

Plot the network as a directed graph (see lang/viz.py).

static serial_routing(*args)[source]

Create a routing probability matrix for serial (tandem) routing through nodes.

Jobs flow from each node to the next in the provided order. For closed networks (last node is not a Sink), the last node automatically routes back to the first node to form a cycle.

If a node appears multiple times in the sequence (e.g., for cyclic routing where the first node is repeated at the end), the duplicate is mapped back to the original node index to create a properly sized routing matrix.

Parameters:

*args – Either a single list of nodes, or nodes passed as separate arguments

Returns:

2D list of routing probabilities, where result[i][j] is the probability of routing from node i to node j. The matrix is sized for all nodes in the model, with indices matching model.get_nodes() order.

static serialRouting(*args)

Create a routing probability matrix for serial (tandem) routing through nodes.

Jobs flow from each node to the next in the provided order. For closed networks (last node is not a Sink), the last node automatically routes back to the first node to form a cycle.

If a node appears multiple times in the sequence (e.g., for cyclic routing where the first node is repeated at the end), the duplicate is mapped back to the original node index to create a properly sized routing matrix.

Parameters:

*args – Either a single list of nodes, or nodes passed as separate arguments

Returns:

2D list of routing probabilities, where result[i][j] is the probability of routing from node i to node j. The matrix is sized for all nodes in the model, with indices matching model.get_nodes() order.

static cyclic(N, D, strategy, S=None)[source]

Create a cyclic queueing network with specified scheduling strategies.

Creates a closed queueing network where jobs cycle through stations in a round-robin fashion (1 -> 2 -> … -> M -> 1).

Parameters:
  • N – Population vector [1 x R] or list - number of jobs per class

  • D – Service demand matrix [M x R] - service demands at each station per class

  • strategy – List of scheduling strategies for each station [M] (e.g., SchedStrategy.FCFS, SchedStrategy.PS, SchedStrategy.INF)

  • S – Number of servers per station [M x 1] or list (default: 1 for each station)

Returns:

Configured closed queueing network

Return type:

Network

Example

>>> N = [10]  # 10 jobs of class 1
>>> D = [[0.5], [1.0]]  # Service demands at 2 stations
>>> strategy = [SchedStrategy.PS, SchedStrategy.FCFS]
>>> model = Network.cyclic(N, D, strategy)

References

MATLAB: matlab/src/lang/JNetwork.m Java: jar/src/main/java/jline/lang/Network.java

jsimg_view()

Open the model in JMT’s JSIMgraph graphical editor.

This method exports the network to JSIMG format (JMT simulation model) and opens it in JSIMgraph for viewing and editing.

Returns:

True if JMT was launched successfully, False otherwise

Raises:

ImportError – If io module is not available

Return type:

bool

Example

>>> model = Network('MyModel')
>>> # ... build model ...
>>> model.jsimgView()  # Opens in JMT graphical editor

References

MATLAB: matlab/src/lang/@MNetwork/jsimgView.m

jsimw_view()

Open the model in JMT’s JSIMwiz wizard interface.

This method exports the network to JSIMG format and opens it in JSIMwiz for wizard-style configuration and simulation.

Returns:

True if JMT was launched successfully, False otherwise

Return type:

bool

Example

>>> model = Network('MyModel')
>>> # ... build model ...
>>> model.jsimwView()  # Opens in JMT wizard

References

MATLAB: matlab/src/lang/@MNetwork/jsimwView.m

view()[source]

Open the model in JMT’s graphical editor (alias for jsimgView).

This is a convenience alias that opens the model in JSIMgraph, providing a visual representation of the queueing network.

Returns:

True if JMT was launched successfully, False otherwise

Return type:

bool

Example

>>> model = Network('MyModel')
>>> # ... build model ...
>>> model.view()  # Opens in JMT graphical editor

References

MATLAB: matlab/src/lang/@MNetwork/view.m

modelView()[source]

Open the model in JSIMgraph viewer.

Exports the network to JSIMG format and launches JMT’s JSIMgraph as a subprocess to display an interactive visualization.

Returns:

True if the viewer was launched successfully, False otherwise

Return type:

bool

References

MATLAB: matlab/src/lang/@MNetwork/modelView.m

model_view()

Open the model in JSIMgraph viewer.

Exports the network to JSIMG format and launches JMT’s JSIMgraph as a subprocess to display an interactive visualization.

Returns:

True if the viewer was launched successfully, False otherwise

Return type:

bool

References

MATLAB: matlab/src/lang/@MNetwork/modelView.m

static tandem(lambda_rates, D, strategies)[source]

Create a tandem network with specified scheduling strategies.

Creates an open queueing network in tandem configuration.

Parameters:
  • lambda_rates (ndarray) – Array of arrival rates for each class (R,)

  • D (ndarray) – Service time matrix (M x R), where D[i,r] is mean service time of class r at station i

  • strategies (List) – List of scheduling strategies for each station

Returns:

Configured tandem queueing network

Return type:

Network

References

MATLAB: matlab/src/lang/@MNetwork/tandem.m

static cluster_ps(lambda_rates, D, S=None, dispatching=None)[source]

Open PS cluster (single-server queues unless S overrides multiplicity).

static cluster_fcfs(lambda_rates, D, S=None, dispatching=None)[source]

Open FCFS cluster.

static cluster_closed(N, Z, D, strategies, S=None, dispatching=None)[source]

Create a closed server-farm network: Think -> Dispatcher -> Servers -> Think.

Parameters:
  • N (ndarray) – Per-class population (1, R) or (R,)

  • Z (ndarray) – Per-class think times (1, R) or (R,)

  • D (ndarray) – Service time matrix (M, R)

  • strategies (List) – Per-server scheduling strategies (length M)

  • S (ndarray | None) – Optional per-server multiplicity (M,)

  • dispatching (RoutingStrategy) – Dispatching policy (defaults to RAND)

static cluster_mixed(lambda_rates, N, Z, D, strategies, S=None, dispatching=None)[source]

Create a mixed cluster network sharing dispatcher and servers between open and closed classes: open classes flow Source -> Dispatcher -> Servers -> Sink, closed classes cycle Think -> Dispatcher -> Servers -> Think.

Classes are ordered open first, so columns 0..Ro-1 of D refer to the open classes and columns Ro..Ro+Rc-1 to the closed ones.

Parameters:
  • lambda_rates (ndarray) – Per-class arrival rates of the open classes (Ro,)

  • N (ndarray) – Per-class population of the closed classes (Rc,)

  • Z (ndarray) – Per-class think times of the closed classes (Rc,)

  • D (ndarray) – Service time matrix (M, Ro+Rc)

  • strategies (List) – Per-server scheduling strategies (length M)

  • S (ndarray | None) – Optional per-server multiplicity (M,)

  • dispatching (RoutingStrategy) – Dispatching policy (defaults to RAND)

static tandem_ps_inf(lambda_rates, D, Z=None)

Create a tandem network with PS queues and INF (delay) stations.

Creates an open queueing network with: - Source node generating arrivals - Optional Delay stations (INF scheduling) from Z - Queue stations (PS scheduling) from D - Sink node

Parameters:
  • lambda_rates (ndarray) – Array of arrival rates for each class (R,)

  • D (ndarray) – Service time matrix for queues (M x R), where M is number of PS queues and R is number of classes

  • Z (ndarray | None) – Optional delay service times (Mz x R), where Mz is number of delay stations

Returns:

Configured tandem queueing network

Return type:

Network

Example

>>> lambda_rates = np.array([0.02, 0.04])
>>> D = np.array([[10, 5], [5, 9]])
>>> Z = np.array([[91, 92]])
>>> model = Network.tandemPsInf(lambda_rates, D, Z)

References

MATLAB: matlab/src/lang/@MNetwork/tandemPsInf.m

static tandem_ps(lambda_rates, D)

Create an open tandem network of PS queues (no delay stations).

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (tandemPs)

static tandem_fcfs(lambda_rates, D)

Create an open tandem network of FCFS queues (no delay stations).

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (tandemFcfs)

static tandem_fcfs_inf(lambda_rates, D, Z=None)

Create an open tandem network with FCFS queues and INF (delay) stations.

Parameters:
  • lambda_rates (ndarray) – Array of arrival rates for each class (R,)

  • D (ndarray) – Service time matrix for FCFS queues (M x R)

  • Z (ndarray | None) – Optional delay service times (Mz x R)

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (tandemFcfsInf)

static cyclic_ps_inf(N, D, Z, S=None)

Create a cyclic network with Delay (INF) stations followed by PS queues.

This creates a closed queueing network where: - The first MZ stations are Delays (infinite server, think time stations) - The remaining M stations are PS (processor sharing) queues

Parameters:
  • N – Population vector [1 x R] or list - number of jobs per class

  • D – Service demand matrix [M x R] - demands at queue stations per class

  • Z – Think time matrix [MZ x R] - think times at delay stations per class If all zeros, no delay stations are created.

  • S – Number of servers per queue station [M x 1] or list (default: 1 each)

Returns:

Configured closed queueing network with delays and PS queues

Return type:

Network

Example

>>> N = [10]  # 10 jobs
>>> D = [[1.0], [0.5]]  # Demands at 2 queues
>>> Z = [[5.0]]  # 5 seconds think time at 1 delay
>>> model = Network.cyclicPsInf(N, D, Z)

References

MATLAB: matlab/src/lang/JNetwork.m Java: jar/src/main/java/jline/lang/Network.java

static cyclic_fcfs(N, D, S=None)

Create a cyclic queueing network with FCFS scheduling at all stations.

Parameters:
  • N – Population vector [1 x R] or list - number of jobs per class

  • D – Service demand matrix [M x R] - service demands at each station per class

  • S – Number of servers per station [M x 1] or list (default: 1 each)

Returns:

Configured closed queueing network with FCFS scheduling

Return type:

Network

Example

>>> N = [10]  # 10 jobs
>>> D = [[0.5], [1.0]]  # Service demands at 2 stations
>>> model = Network.cyclicFcfs(N, D)

References

MATLAB: matlab/src/lang/JNetwork.m Java: jar/src/main/java/jline/lang/Network.java

static cyclic_fcfs_inf(N, D, Z=None, S=None)

Create a cyclic closed network with Delay (INF) stations followed by FCFS queues.

Parameters:
  • N – Population vector [1 x R] or list - number of jobs per class

  • D – Service demand matrix [M x R] - demands at FCFS queues per class

  • Z – Think time matrix [MZ x R] at delay stations per class

  • S – Number of servers per FCFS queue [M x 1] or list (default: 1 each)

References

MATLAB: matlab/src/lang/@MNetwork/MNetwork.m (cyclicFcfsInf)

static cyclic_ps(N, D, S=None)

Create a cyclic queueing network with PS scheduling at all stations.

Parameters:
  • N – Population vector [1 x R] or list - number of jobs per class

  • D – Service demand matrix [M x R] - service demands at each station per class

  • S – Number of servers per station [M x 1] or list (default: 1 each)

Returns:

Configured closed queueing network with PS scheduling

Return type:

Network

Example

>>> N = [10]  # 10 jobs
>>> D = [[0.5], [1.0]]  # Service demands at 2 stations
>>> model = Network.cyclicPs(N, D)

References

MATLAB: matlab/src/lang/JNetwork.m Java: jar/src/main/java/jline/lang/Network.java

static cluster(lambda_rates, D, strategies, S=None, dispatching=None)[source]

Create an open server-farm network: Source -> Dispatcher -> Servers -> Sink.

The dispatcher is a Router that distributes incoming jobs to the M parallel server queues according to the supplied dispatching strategy (RAND, RROBIN, JSQ, …).

Parameters:
  • lambda_rates (ndarray) – Per-class arrival rates (R,)

  • D (ndarray) – Service time matrix (M x R); D[i, r] is the mean service time of class r at server i

  • strategies (List) – Per-server scheduling strategies (length M)

  • S (ndarray | None) – Optional per-server multiplicity (M,) or (M, 1); defaults to all-1

  • dispatching (RoutingStrategy) – Dispatching policy applied at the router (defaults to RAND)

Returns:

Configured open server-farm model

Return type:

Network

static clusterPs(lambda_rates, D, S=None, dispatching=None)

Open PS cluster (single-server queues unless S overrides multiplicity).

static clusterFcfs(lambda_rates, D, S=None, dispatching=None)

Open FCFS cluster.

static clusterClosed(N, Z, D, strategies, S=None, dispatching=None)

Create a closed server-farm network: Think -> Dispatcher -> Servers -> Think.

Parameters:
  • N (ndarray) – Per-class population (1, R) or (R,)

  • Z (ndarray) – Per-class think times (1, R) or (R,)

  • D (ndarray) – Service time matrix (M, R)

  • strategies (List) – Per-server scheduling strategies (length M)

  • S (ndarray | None) – Optional per-server multiplicity (M,)

  • dispatching (RoutingStrategy) – Dispatching policy (defaults to RAND)

static clusterMixed(lambda_rates, N, Z, D, strategies, S=None, dispatching=None)

Create a mixed cluster network sharing dispatcher and servers between open and closed classes: open classes flow Source -> Dispatcher -> Servers -> Sink, closed classes cycle Think -> Dispatcher -> Servers -> Think.

Classes are ordered open first, so columns 0..Ro-1 of D refer to the open classes and columns Ro..Ro+Rc-1 to the closed ones.

Parameters:
  • lambda_rates (ndarray) – Per-class arrival rates of the open classes (Ro,)

  • N (ndarray) – Per-class population of the closed classes (Rc,)

  • Z (ndarray) – Per-class think times of the closed classes (Rc,)

  • D (ndarray) – Service time matrix (M, Ro+Rc)

  • strategies (List) – Per-server scheduling strategies (length M)

  • S (ndarray | None) – Optional per-server multiplicity (M,)

  • dispatching (RoutingStrategy) – Dispatching policy (defaults to RAND)

class Ensemble[source]

Bases: object

Static helpers for combining independent network models.

static merge(models)[source]

Combine a list of independent Network models into a single Network.

Node and class names are prefixed with their model name to prevent conflicts; all Source and Sink nodes are merged into single MergedSource and MergedSink nodes.

Parameters:

models – list of Network models to merge.

Returns:

A new Network containing the disconnected subnetworks.

References

MATLAB: matlab/src/lang/Ensemble.m (merge) Java: java/src/main/java/jline/lang/Ensemble.java (merge)

Node Classes

class Node(node_type, name='')[source]

Bases: NetworkElement

Abstract base class for network nodes.

Initialize a node.

Parameters:
  • node_type (NodeType) – Type of node (from NodeType enum)

  • name (str) – Node name

__init__(node_type, name='')[source]

Initialize a node.

Parameters:
  • node_type (NodeType) – Type of node (from NodeType enum)

  • name (str) – Node name

__index__()[source]

Return node/station index for Python array indexing.

Returns station index if available (for Station subclasses), otherwise returns node index.

property node_type: NodeType

Get the node type.

set_model(model)[source]

Link this node to a network model.

Parameters:

model – Network instance

remove_job_class(jobclass)[source]

Remove all per-class configuration referencing a job class from this node.

Every per-class setting on a node (service and arrival processes, scheduling parameters, class capacities, routing strategies, balking, retrial, patience, …) is stored in a dictionary keyed by the JobClass object, or by a tuple containing it. Dropping those entries therefore removes the class from this node exhaustively, and stays correct when a new per-class dictionary is added to a node type. Subclasses override this to handle per-class state that is not dictionary-based, such as the class-switching matrix of a ClassSwitch.

Called by Network.remove_class; mirrors Node.removeJobClass in the JAR.

Parameters:

jobclass – the job class being removed from the model

removeJobClass(jobclass)

Remove all per-class configuration referencing a job class from this node.

Every per-class setting on a node (service and arrival processes, scheduling parameters, class capacities, routing strategies, balking, retrial, patience, …) is stored in a dictionary keyed by the JobClass object, or by a tuple containing it. Dropping those entries therefore removes the class from this node exhaustively, and stays correct when a new per-class dictionary is added to a node type. Subclasses override this to handle per-class state that is not dictionary-based, such as the class-switching matrix of a ClassSwitch.

Called by Network.remove_class; mirrors Node.removeJobClass in the JAR.

Parameters:

jobclass – the job class being removed from the model

remap_job_classes(new_classes)[source]

Re-key every per-class setting of this node onto a new list of classes.

A deep copy of a node carries orphan JobClass copies as dictionary keys, which no lookup with the copied model’s own class objects can ever hit, so the setting silently reverts to its default. This remaps keys (and values that are themselves classes, such as the hit and miss classes of a cache) by class index, covering every per-class dictionary of every node type. Called by Network.copy.

Parameters:

new_classes – the class list the node must be re-keyed onto

remapJobClasses(new_classes)

Re-key every per-class setting of this node onto a new list of classes.

A deep copy of a node carries orphan JobClass copies as dictionary keys, which no lookup with the copied model’s own class objects can ever hit, so the setting silently reverts to its default. This remaps keys (and values that are themselves classes, such as the hit and miss classes of a cache) by class index, covering every per-class dictionary of every node type. Called by Network.copy.

Parameters:

new_classes – the class list the node must be re-keyed onto

is_stateful()[source]

Check if node is stateful (can have jobs).

is_station()[source]

Check if node is a station (can serve jobs).

has_class_switching()[source]

Check if this node switches the class of the jobs traversing it.

Twin of the MATLAB Node.hasClassSwitching, which tests whether the service section is a ClassSwitcher; ClassSwitch and Cache nodes are the two that carry one.

summary()[source]

Print the one-line node summary, twin of MATLAB Node.summary.

Create a link from this node to another node.

Parameters:

node_to – Destination node

set_routing(jobclass, strategy, *params)[source]

Set routing strategy for a job class.

Parameters:
  • jobclass (JobClass) – Job class

  • strategy (RoutingStrategy) – Routing strategy (RAND, PROB, etc.)

  • params – Additional parameters (depends on strategy) For WRROBIN: (destination_node, weight) - can be called multiple times For SQ: (d,) - number of sampled destinations

set_state_dep_routing(jobclass, departure, branches, level, C, d)[source]

Declare this node the entry center of a Krzesinski SDR subnetwork.

Krzesinski, A. E., “Multiclass Queueing Networks with State-Dependent Routing”, Performance Evaluation 7(2):125-143, 1987.

departure is the departure center d of Q(V,V), which may be this node itself in a central server model. branches follows the paper’s own indexing: branches[0] must be empty because branch index 1 denotes the complement M-V, and branches[b] lists the nodes of branch b with its entry center first and its departure center last. level[b] is the index t of the subnetwork with B_b in V_t - V_{t+1}. C is the length-T list of coefficients C_t and d the T x B matrix of coefficients d_tb, read for 1 <= t <= level[b] and b >= 1 in 0-based terms.

Negative C_t and positive d_tb make the routing prefer the least congested branches and impose the population bounds m_b <= d_tb/(-C_t) and v_t <= D_tt/(-C_t).

get_routing(jobclass)[source]

Get routing strategy for a job class.

Parameters:

jobclass (JobClass) – Job class

Returns:

Tuple of (strategy, parameters)

Return type:

Tuple[RoutingStrategy, tuple]

get_routing_weight(jobclass, destination)[source]

Get the WRROBIN routing weight for a class and destination (default 1.0).

getRoutingWeight(jobclass, destination)

Get the WRROBIN routing weight for a class and destination (default 1.0).

get_prob_routing(jobclass)[source]

Get probabilistic routing for a job class as {destination: prob}.

getProbRouting(jobclass)

Get probabilistic routing for a job class as {destination: prob}.

set_prob_routing(jobclass, destination, prob)[source]

Set probabilistic routing to a destination node.

Parameters:
  • jobclass (JobClass) – Job class

  • destination – Destination node

  • prob (float) – Routing probability (0 to 1)

setModel(model)

Link this node to a network model.

Parameters:

model – Network instance

isStateful()

Check if node is stateful (can have jobs).

isStation()

Check if node is a station (can serve jobs).

hasClassSwitching()

Check if this node switches the class of the jobs traversing it.

Twin of the MATLAB Node.hasClassSwitching, which tests whether the service section is a ClassSwitcher; ClassSwitch and Cache nodes are the two that carry one.

getRouting(jobclass)

Get routing strategy for a job class.

Parameters:

jobclass (JobClass) – Job class

Returns:

Tuple of (strategy, parameters)

Return type:

Tuple[RoutingStrategy, tuple]

class Source(model, name)[source]

Bases: Station

Source node for external arrivals.

A Source represents the entry point for open-class jobs. Each network can have at most one Source node.

Initialize a Source node.

Parameters:
  • model – Network instance

  • name (str) – Source node name

__init__(model, name)[source]

Initialize a Source node.

Parameters:
  • model – Network instance

  • name (str) – Source node name

set_arrival(jobclass, distribution)[source]

Set arrival distribution for a job class.

Parameters:
  • jobclass (JobClass) – Job class

  • distribution – Arrival distribution (Exp, Erlang, etc.)

set_arrival_batch(jobclass, batch_size)[source]

Set a batch-size law for a class, turning each arrival epoch into the simultaneous release of a batch of jobs.

The interarrival distribution set by set_arrival() keeps spacing the epochs; this decides how many jobs each epoch releases. Geometric interarrivals with a Geometric batch size is the Geo^X arrival stream, whose analytical counterpart is line_solver.api.dqsys.dqsys_geoxgeo1().

The batch size must be supported on {1,2,…}: an epoch that releases no job is not an arrival epoch, so a law that can return zero is rejected rather than clamped.

Parameters:
  • jobclass (JobClass) – Job class

  • batch_size – Batch-size distribution, or None to restore single arrivals

get_arrival_batch(jobclass)[source]

Return the batch-size law bound to a class, or None for single arrivals.

set_marked_arrival(mmap, classes)[source]

Bind a marked arrival process with K marks to K open classes: mark k emits jobs of classes[k-1], with all marks driven by one shared modulating chain. Mirrors MATLAB Source.setMarkedArrival.

mmap is any marked family: MarkedMAP and MPH (stationary), MMAPt and MPHt (schedule-bearing), or BMMAPt (schedule-bearing and BATCH). The schedule families do not subclass MarkedMAP – they stay outside the Markovian hierarchy for the same reason MAPt does – so the test is by capability, not by one class name. A BMMAPt epoch releases a whole batch under the class its mark selects: the batch is homogeneous in its mark.

Parameters:
  • mmap – MarkedMAP/MMAP, MPH, MMAPt, MPHt or BMMAPt arrival process

  • classes – list of K distinct OpenClass instances, ordered by mark

get_marked_process()[source]

The shared marked process bound via set_marked_arrival, or None.

It is a MarkedMAP (or MPH), an MMAPt, an MPHt or a BMMAPt.

get_marked_classes()[source]

Get the classes bound to marks 1..K of the marked process, or None.

clear_marked_arrival()[source]

Drop the marked-arrival binding, leaving the per-class arrival processes currently set.

Used where a transformation replaces the shared marked stream by per-class renewal streams (map2renv freezes each modulating phase into its own Exp), after which a struct still advertising markidx would claim a marked arrival the model no longer has.

setMarkedArrival(mmap, classes)

Bind a marked arrival process with K marks to K open classes: mark k emits jobs of classes[k-1], with all marks driven by one shared modulating chain. Mirrors MATLAB Source.setMarkedArrival.

mmap is any marked family: MarkedMAP and MPH (stationary), MMAPt and MPHt (schedule-bearing), or BMMAPt (schedule-bearing and BATCH). The schedule families do not subclass MarkedMAP – they stay outside the Markovian hierarchy for the same reason MAPt does – so the test is by capability, not by one class name. A BMMAPt epoch releases a whole batch under the class its mark selects: the batch is homogeneous in its mark.

Parameters:
  • mmap – MarkedMAP/MMAP, MPH, MMAPt, MPHt or BMMAPt arrival process

  • classes – list of K distinct OpenClass instances, ordered by mark

clearMarkedArrival()

Drop the marked-arrival binding, leaving the per-class arrival processes currently set.

Used where a transformation replaces the shared marked stream by per-class renewal streams (map2renv freezes each modulating phase into its own Exp), after which a struct still advertising markidx would claim a marked arrival the model no longer has.

get_arrival(jobclass)[source]

Get arrival distribution for a job class.

Parameters:

jobclass (JobClass) – Job class

Returns:

Arrival distribution or None if not set

arrival_process(jobclass)

Get the arrival process (distribution) for a job class.

get_arrival_rates()[source]

Get arrival rates (1/mean) for all classes.

Returns:

Dict mapping classes to arrival rates

Return type:

Dict[JobClass, float]

getArrival(jobclass)

Get arrival distribution for a job class.

Parameters:

jobclass (JobClass) – Job class

Returns:

Arrival distribution or None if not set

getArrivalRates()

Get arrival rates (1/mean) for all classes.

Returns:

Dict mapping classes to arrival rates

Return type:

Dict[JobClass, float]

setArrivalBatch(jobclass, batch_size)

Set a batch-size law for a class, turning each arrival epoch into the simultaneous release of a batch of jobs.

The interarrival distribution set by set_arrival() keeps spacing the epochs; this decides how many jobs each epoch releases. Geometric interarrivals with a Geometric batch size is the Geo^X arrival stream, whose analytical counterpart is line_solver.api.dqsys.dqsys_geoxgeo1().

The batch size must be supported on {1,2,…}: an epoch that releases no job is not an arrival epoch, so a law that can return zero is rejected rather than clamped.

Parameters:
  • jobclass (JobClass) – Job class

  • batch_size – Batch-size distribution, or None to restore single arrivals

getArrivalBatch(jobclass)

Return the batch-size law bound to a class, or None for single arrivals.

class Queue(model, name, sched_strategy=SchedStrategy.FCFS)[source]

Bases: Station

Queue station for customer service.

A Queue represents a queueing station with one or more servers and configurable scheduling strategy.

Initialize a Queue node.

Parameters:
  • model – Network instance

  • name (str) – Queue name

  • sched_strategy (SchedStrategy) – Scheduling strategy (default: FCFS)

Raises:

ValueError – If scheduling strategy is invalid

__init__(model, name, sched_strategy=SchedStrategy.FCFS)[source]

Initialize a Queue node.

Parameters:
  • model – Network instance

  • name (str) – Queue name

  • sched_strategy (SchedStrategy) – Scheduling strategy (default: FCFS)

Raises:

ValueError – If scheduling strategy is invalid

set_sched_strategy(strategy)[source]

Set scheduling strategy.

Parameters:

strategy (SchedStrategy) – Scheduling strategy (FCFS, LCFS, PS, etc.)

get_sched_policy()[source]

Get scheduling policy (preemptive or non-preemptive).

set_service(jobclass, distribution=None, weight=None)[source]

Set service time distribution for a job class.

On a pass-and-swap (PAS) queue, call as set_service(mu) where mu is a callable taking the ordered state vector c (a list/array of class indices, c[0] the oldest job) and returning the scalar total service rate mu(c). Per-class service distributions are not used by an order-independent/PAS queue.

Parameters:
  • jobclass (JobClass) – Job class, or the mu(c) callable on a PAS queue

  • distribution – Service distribution (Exp, Erlang, HyperExp, etc.) or Workflow for activity-based distributions

  • weight (float) – Optional weight/scale factor for the distribution

service_process(jobclass)

Get the service process (distribution) for a job class (wrapper-compatible name).

get_service(jobclass)[source]

Get service distribution for a job class.

Parameters:

jobclass (JobClass) – Job class

Returns:

Service distribution or None if not set

set_item_service_rate(cache, jobin_class, item, service_rate)[source]

Override, at this queue, the retrieval service rate for a single item of the read class jobin_class in cache’s retrieval system. The default (when not overridden) is the read class’s own service distribution at this queue. item is 0-based.

setItemServiceRate(cache, jobin_class, item, service_rate)

Override, at this queue, the retrieval service rate for a single item of the read class jobin_class in cache’s retrieval system. The default (when not overridden) is the read class’s own service distribution at this queue. item is 0-based.

set_strategy_param(jobclass, param)[source]

Set scheduling parameter for a job class.

For PS/DPS/GPS: weight or priority parameter For other strategies: may be unused

Parameters:
  • jobclass (JobClass) – Job class

  • param – Scheduling parameter (weight, priority, etc.)

get_strategy_param(jobclass)[source]

Get scheduling parameter for a job class.

set_swap_graph(graph)[source]

Set the class compatibility/swap graph of a pass-and-swap (PAS) queue.

Parameters:

graph – (nclasses x nclasses) adjacency matrix; entry (r,s) nonzero iff, upon completion of a class-r job, a waiting class-s job may take its place (order-independent swap). Undirected; self-loops allowed.

get_swap_graph()[source]

Return the (nclasses x nclasses) swap graph of a PAS queue, or None.

set_service_rate_function(mu_fun)[source]

Set the total service rate function mu(c) of a pass-and-swap (PAS) queue.

Parameters:

mu_fun – callable taking the ordered state vector c (list/array of class indices, c[0] oldest) and returning the scalar total service rate mu(c). The rate of position i is the increment Delta_mu(c[:i]) = mu(c[:i]) - mu(c[:i-1]).

get_service_rate_function()[source]

Return the mu(c) service rate function of a PAS queue, or None.

check_perm_invariance(Nvec, cap)[source]

Check the order-independence (OI) condition on the service rate mu(c): the rate of the job in position j must depend only on the jobs at or ahead of it (positions 1..j) and not on those behind. Since the position-j rate is the prefix increment mu(c[:j]) - mu(c[:j-1]), tail-independence is structural; the substantive requirement is that this increment be independent of the order of the jobs ahead, which (by induction on prefix length) is equivalent to mu(c) being permutation- invariant. Enumerates the reachable multisets (per-class counts <= Nvec, total <= cap) when small, otherwise samples and returns partial=True. Returns (ok, badc, partial).

check_rate_monotonicity(Nvec, cap)[source]

Check OI condition (1) on the service rate mu(c): the per-job rates must be non-negative, mu(c_1,…,c_j) >= mu(c_1,…,c_{j-1}) for every microstate and position.

A rate can be permutation-invariant and still fail to parameterize an OI queue. Single-server processor sharing with class-dependent rates, mu(c) = (sum_j mu_{c_j}) / n, is the standard trap: it is flatly invariant under permutations, yet as soon as two classes have different rates its prefix increments go negative – with mu_hit = 3.0 and mu_miss = 0.7, mu(Hit) = 3.0 and mu(Hit,Miss) = 1.85, so the second job would be served at rate -1.15.

Run this AFTER check_perm_invariance: permutation invariance is what makes mu a function of the count vector, and the increments to test are then just mu(n + e_r) - mu(n) over count vectors n and classes r, with no permutation enumeration. Prefixes are non-empty, so mu is never evaluated on an empty microstate, and an increment of exactly zero is accepted – that is how a class which does not visit this station is expressed. Enumerates the reachable count vectors (per-class counts <= Nvec, total <= cap) when small, otherwise samples and returns partial=True. Returns (ok, badc, badr, partial): badc the microstate whose rate is lowered, badr the class whose arrival lowers it.

is_service_defined(jobclass)[source]

Check if service distribution is defined for a class.

is_service_disabled(jobclass)[source]

Check if service is explicitly disabled for a class.

get_service_rates()[source]

Get mean service rates for all classes.

Returns:

Dict mapping classes to mean service rates

Return type:

Dict[JobClass, float]

set_number_of_servers(value)[source]

Set the number of servers. Matches MATLAB Queue.setNumServers: DPS/GPS scheduling does not admit multi-server stations.

set_limit(limit)[source]

Set the maximum number of jobs in service for LPS (Limited Processor Sharing) scheduling. Mirrors MATLAB Queue.setLimit: the limit is stored as the queue-level scheduling parameter (schedparam column 1).

get_limit()[source]

Return the LPS concurrency limit set via set_limit, or numberOfServers when no explicit limit was set (matching MATLAB Queue.getLimit).

getSchedPolicy()

Get scheduling policy (preemptive or non-preemptive).

getService(jobclass)

Get service distribution for a job class.

Parameters:

jobclass (JobClass) – Job class

Returns:

Service distribution or None if not set

setStrategyParam(jobclass, param)

Set scheduling parameter for a job class.

For PS/DPS/GPS: weight or priority parameter For other strategies: may be unused

Parameters:
  • jobclass (JobClass) – Job class

  • param – Scheduling parameter (weight, priority, etc.)

getStrategyParam(jobclass)

Get scheduling parameter for a job class.

getServiceRates()

Get mean service rates for all classes.

Returns:

Dict mapping classes to mean service rates

Return type:

Dict[JobClass, float]

setSwapGraph(graph)

Set the class compatibility/swap graph of a pass-and-swap (PAS) queue.

Parameters:

graph – (nclasses x nclasses) adjacency matrix; entry (r,s) nonzero iff, upon completion of a class-r job, a waiting class-s job may take its place (order-independent swap). Undirected; self-loops allowed.

getSwapGraph()

Return the (nclasses x nclasses) swap graph of a PAS queue, or None.

setServiceRateFunction(mu_fun)

Set the total service rate function mu(c) of a pass-and-swap (PAS) queue.

Parameters:

mu_fun – callable taking the ordered state vector c (list/array of class indices, c[0] oldest) and returning the scalar total service rate mu(c). The rate of position i is the increment Delta_mu(c[:i]) = mu(c[:i]) - mu(c[:i-1]).

getServiceRateFunction()

Return the mu(c) service rate function of a PAS queue, or None.

set_num_servers(value)

Set the number of servers. Matches MATLAB Queue.setNumServers: DPS/GPS scheduling does not admit multi-server stations.

setLimit(limit)

Set the maximum number of jobs in service for LPS (Limited Processor Sharing) scheduling. Mirrors MATLAB Queue.setLimit: the limit is stored as the queue-level scheduling parameter (schedparam column 1).

getLimit()

Return the LPS concurrency limit set via set_limit, or numberOfServers when no explicit limit was set (matching MATLAB Queue.getLimit).

is_heterogeneous()[source]

Check if this queue has heterogeneous servers.

Returns:

True if server types have been defined

Return type:

bool

add_server_type(server_type)[source]

Add a server type to this queue.

Parameters:

server_type – ServerType object to add

Raises:

ValueError – If server type is already in queue

get_server_types()[source]

Get the list of server types.

set_hetero_sched_policy(policy)[source]

Set the heterogeneous scheduling policy.

Parameters:

policy – HeteroSchedPolicy value

get_hetero_sched_policy()[source]

Get the heterogeneous scheduling policy.

set_hetero_service(jobclass, server_type, distribution)[source]

Set service distribution for a specific job class and server type.

Parameters:
  • jobclass – Job class

  • server_type – ServerType object

  • distribution – Service distribution

get_hetero_service(jobclass, server_type)[source]

Get service distribution for a specific job class and server type.

Parameters:
  • jobclass – Job class

  • server_type – ServerType object

Returns:

Service distribution or None

set_server_parallelism(jobclass, n)[source]

Set the number of servers a job of this class seizes for the whole of its service, JMT’s job parallelism (Server.serverNumRequired).

A job waits until n servers are simultaneously free and holds all of them until it completes, so the station serves at most floor(c/n) such jobs at a time. The default is 1.

Parameters:
  • jobclass – Job class

  • n (int) – Number of servers required, an integer in [1, c]

Raises:

ValueError – If n is below 1 or above the server count

get_server_parallelism(jobclass)[source]

Get the number of servers seized by a job of this class, 1 if unset.

has_server_parallelism()[source]

True when some class seizes more than one server.

get_total_num_of_servers()[source]

Get total number of servers across all server types.

Returns:

Total server count

Return type:

int

isHeterogeneous()

Check if this queue has heterogeneous servers.

Returns:

True if server types have been defined

Return type:

bool

addServerType(server_type)

Add a server type to this queue.

Parameters:

server_type – ServerType object to add

Raises:

ValueError – If server type is already in queue

getServerTypes()

Get the list of server types.

setHeteroSchedPolicy(policy)

Set the heterogeneous scheduling policy.

Parameters:

policy – HeteroSchedPolicy value

getHeteroSchedPolicy()

Get the heterogeneous scheduling policy.

setHeteroService(jobclass, server_type, distribution)

Set service distribution for a specific job class and server type.

Parameters:
  • jobclass – Job class

  • server_type – ServerType object

  • distribution – Service distribution

getHeteroService(jobclass, server_type)

Get service distribution for a specific job class and server type.

Parameters:
  • jobclass – Job class

  • server_type – ServerType object

Returns:

Service distribution or None

getTotalNumOfServers()

Get total number of servers across all server types.

Returns:

Total server count

Return type:

int

setServerParallelism(jobclass, n)

Set the number of servers a job of this class seizes for the whole of its service, JMT’s job parallelism (Server.serverNumRequired).

A job waits until n servers are simultaneously free and holds all of them until it completes, so the station serves at most floor(c/n) such jobs at a time. The default is 1.

Parameters:
  • jobclass – Job class

  • n (int) – Number of servers required, an integer in [1, c]

Raises:

ValueError – If n is below 1 or above the server count

getServerParallelism(jobclass)

Get the number of servers seized by a job of this class, 1 if unset.

hasServerParallelism()

True when some class seizes more than one server.

set_polling_type(polling_type, k=None)[source]

Set the polling type for this queue.

Parameters:
  • polling_type – PollingType enum value (EXHAUSTIVE, GATED, KLIMITED)

  • k – For KLIMITED polling, the maximum number of jobs to serve (default: 1)

get_polling_type()[source]

Get the polling type for this queue.

set_polling_k(k)[source]

Set the k parameter for k-limited polling (wrapper-compatible name).

set_switchover(from_class, to_class_or_dist, distribution=None)[source]

Set switchover time between job classes.

Can be called as: - set_switchover(from_class, to_class, distribution) - for class-to-class switchover - set_switchover(jobclass, distribution) - for single class switchover time

Parameters:
  • from_class – Source job class

  • to_class_or_dist – Target job class, OR distribution if only 2 args

  • distribution – Switchover time distribution (optional if 2 args)

set_delay_off(jobclass, setup_time, delay_off_time)[source]

Set setup and delay-off time distributions for a job class.

Models a vacation queue: when a server finishes serving class r and no more class r jobs are waiting, the server enters a delay-off period. When a new class r job arrives during delay-off, the server goes through setup before serving.

Parameters:
  • jobclass – Job class

  • setup_time – Distribution for setup time

  • delay_off_time – Distribution for delay-off time

setDelayOff(jobclass, setup_time, delay_off_time)

Set setup and delay-off time distributions for a job class.

Models a vacation queue: when a server finishes serving class r and no more class r jobs are waiting, the server enters a delay-off period. When a new class r job arrives during delay-off, the server goes through setup before serving.

Parameters:
  • jobclass – Job class

  • setup_time – Distribution for setup time

  • delay_off_time – Distribution for delay-off time

get_setup_time(jobclass)[source]

Get setup time distribution for a job class.

getSetupTime(jobclass)

Get setup time distribution for a job class.

get_delay_off_time(jobclass)[source]

Get delay-off time distribution for a job class.

getDelayOffTime(jobclass)

Get delay-off time distribution for a job class.

is_delay_off_enabled()[source]

Return True if setup and delay-off times are both configured.

isDelayOffEnabled()

Return True if setup and delay-off times are both configured.

set_prob_routing(jobclass, destination, prob)[source]

Set probabilistic routing to a destination node.

Parameters:
  • jobclass – Job class

  • destination – Destination node

  • prob (float) – Routing probability (0 to 1)

get_prob_routing(jobclass)[source]

Get probabilistic routing for a job class.

set_state(state)[source]

Set initial state for this node.

Parameters:

state – Array of initial job counts per class

set_routing_weight(jobclass, destination, weight)[source]

Set routing weight for weighted round-robin routing.

Parameters:
  • jobclass (JobClass) – Job class

  • destination – Destination node

  • weight (float) – Routing weight for this destination

get_routing_weight(jobclass, destination)[source]

Get routing weight for a job class and destination.

set_balking(jobclass, strategy, thresholds)[source]

Configure balking for a job class.

Parameters:
  • jobclass – JobClass object

  • strategy – BalkingStrategy enum value

  • thresholds – List of (minJobs, maxJobs, probability) tuples

get_balking(jobclass)[source]

Get balking config for a job class. Returns (strategy, thresholds) or (None, []).

has_balking(jobclass)[source]

Check if a job class has balking configured.

set_retrial(jobclass, delay_distribution, max_attempts=-1)[source]

Configure retrial for a job class.

Parameters:
  • jobclass – JobClass object

  • delay_distribution – Distribution for retrial delay

  • max_attempts – Maximum retrial attempts (-1 = unlimited)

get_retrial(jobclass)[source]

Get retrial config for a job class. Returns (delay_dist, max_attempts) or (None, -1).

has_retrial(jobclass)[source]

Check if a job class has retrial configured.

set_orbit(jobclass, retrial_distribution, policy=None, max_orbit=-1)[source]

Declare this station to be a retrial queue for jobclass: a job that finds every server busy joins an orbit and re-attempts entry after a random delay, instead of waiting in a line.

This is the first-class form of the retrial idiom. It removes the waiting room itself, so the caller no longer has to know that set_capacity(nservers) is the way to express “no waiting room, blocked jobs orbit”.

Parameters:
  • jobclass – JobClass object

  • retrial_distribution – retrial delay of an orbiting job

  • policy – RetrialPolicy.LINEAR (default), where each orbiting job retries at its own rate so the aggregate rate is (orbit size)*nu, or RetrialPolicy.CONSTANT, where the orbit retries as a whole at rate nu whenever non-empty.

  • max_orbit – orbit capacity; -1 (default) leaves the orbit unbounded. A job that finds the orbit full is lost.

The mean orbit length is reported by get_avg_orbit / get_avg_orbit_table.

get_orbit(jobclass)[source]

Return (retrial_distribution, policy, max_orbit) for jobclass at this station.

set_breakdown(failure_distribution, repair_distribution, down_service_distribution=None)[source]

Make the server of this station subject to breakdowns. The server alternates between an UP and a DOWN status: while up it fails after failure_distribution, while down it is restored after repair_distribution.

The failure clock runs whenever the server is up, whether or not a job is in service, so a station can fail while idle. Arrivals are unaffected by the server status and keep queueing (subject to the station capacity) while the server is down. A job that is in service when the server fails is not lost: it stays at the station and, service being memoryless in the supported case, resumes when the server is repaired.

Parameters:
  • failure_distribution – time to failure of an up server (Exp)

  • repair_distribution – repair time of a down server (Exp)

  • down_service_distribution – optional service distribution used while the server is down, either a single Distribution applied to every class or a list indexed by class. Omitted or empty means the server does not serve at all while down, which is the usual breakdown model.

Only exponential failure and repair distributions are currently expanded into the joint (queue, server status) chain; anything else is rejected here rather than silently approximated.

get_breakdown()[source]

Return (failure_distribution, repair_distribution, down_service_distribution) for this station, or (None, None, []) when the station is not subject to breakdowns.

has_breakdown()[source]

True iff a failure and a repair distribution are configured here.

set_orbit_impatience(jobclass, distribution)[source]

Set the impatience (abandonment) rate for customers in the orbit.

This is separate from queue patience (reneging from the waiting queue). Used in BMAP/PH/N/N retrial queues where customers in the orbit may abandon before successfully retrying.

Parameters:
  • jobclass – JobClass object

  • distribution – Distribution for orbit abandonment time (e.g. Exp(gamma)). Modulated processes (BMAP, MAP, DMAP, MMPP2) and schedule-bearing ones (MAPt, PHt, MMAPt, MPHt, BMMAPt) are not supported.

setOrbitImpatience(jobclass, distribution)

Set the impatience (abandonment) rate for customers in the orbit.

This is separate from queue patience (reneging from the waiting queue). Used in BMAP/PH/N/N retrial queues where customers in the orbit may abandon before successfully retrying.

Parameters:
  • jobclass – JobClass object

  • distribution – Distribution for orbit abandonment time (e.g. Exp(gamma)). Modulated processes (BMAP, MAP, DMAP, MMPP2) and schedule-bearing ones (MAPt, PHt, MMAPt, MPHt, BMMAPt) are not supported.

get_orbit_impatience(jobclass)[source]

Get the orbit-impatience distribution for a job class (or None).

getOrbitImpatience(jobclass)

Get the orbit-impatience distribution for a job class (or None).

has_orbit_impatience(jobclass)[source]

Check if a job class has orbit impatience configured at this queue.

hasOrbitImpatience(jobclass)

Check if a job class has orbit impatience configured at this queue.

set_batch_reject_probability(jobclass, p)[source]

Set the batch rejection probability for a job class.

Used in BMAP/PH/N/N retrial queues. When a batch of size k arrives and only m < k servers are free:

  • with probability p the entire batch is rejected to the orbit;

  • with probability (1-p) m customers are admitted and k-m go to orbit.

Parameters:
  • jobclass – JobClass object

  • p – Probability in [0,1] that the batch is rejected rather than partially admitted. Default is 0 (partial admission allowed).

setBatchRejectProbability(jobclass, p)

Set the batch rejection probability for a job class.

Used in BMAP/PH/N/N retrial queues. When a batch of size k arrives and only m < k servers are free:

  • with probability p the entire batch is rejected to the orbit;

  • with probability (1-p) m customers are admitted and k-m go to orbit.

Parameters:
  • jobclass – JobClass object

  • p – Probability in [0,1] that the batch is rejected rather than partially admitted. Default is 0 (partial admission allowed).

get_batch_reject_probability(jobclass)[source]

Get the batch reject probability for a job class (0 if not set).

getBatchRejectProbability(jobclass)

Get the batch reject probability for a job class (0 if not set).

set_patience(jobclass, distribution, impatience_type=None)[source]

Set patience distribution for a job class.

Parameters:
  • jobclass – JobClass object

  • distribution – Patience distribution

  • impatience_type – ImpatienceType enum value (default: RENEGING)

get_patience(jobclass)[source]

Get patience distribution for a job class, or None.

get_impatience_type(jobclass)[source]

Get impatience type for a job class, or None.

has_patience(jobclass)[source]

Check if a job class has patience configured.

set_immediate_feedback(value)[source]

Configure immediate feedback for this queue.

When enabled, a job that routes back to this queue (self-loop) stays in service instead of re-entering the queue.

Parameters:

value – bool (enable/disable for all classes), JobClass (enable for specific class), or list of JobClass (enable for specific classes)

has_immediate_feedback(jobclass=None)[source]

Check if immediate feedback is enabled.

Parameters:

jobclass – If provided, check for specific class. If None, check if any class has it.

get_immediate_feedback_classes()[source]

Get set of class indices with immediate feedback enabled, or ‘all’.

getPollingType()

Get the polling type for this queue.

getProbRouting(jobclass)

Get probabilistic routing for a job class.

setRoutingWeight(jobclass, destination, weight)

Set routing weight for weighted round-robin routing.

Parameters:
  • jobclass (JobClass) – Job class

  • destination – Destination node

  • weight (float) – Routing weight for this destination

getRoutingWeight(jobclass, destination)

Get routing weight for a job class and destination.

setBalking(jobclass, strategy, thresholds)

Configure balking for a job class.

Parameters:
  • jobclass – JobClass object

  • strategy – BalkingStrategy enum value

  • thresholds – List of (minJobs, maxJobs, probability) tuples

getBalking(jobclass)

Get balking config for a job class. Returns (strategy, thresholds) or (None, []).

hasBalking(jobclass)

Check if a job class has balking configured.

setRetrial(jobclass, delay_distribution, max_attempts=-1)

Configure retrial for a job class.

Parameters:
  • jobclass – JobClass object

  • delay_distribution – Distribution for retrial delay

  • max_attempts – Maximum retrial attempts (-1 = unlimited)

getRetrial(jobclass)

Get retrial config for a job class. Returns (delay_dist, max_attempts) or (None, -1).

hasRetrial(jobclass)

Check if a job class has retrial configured.

setOrbit(jobclass, retrial_distribution, policy=None, max_orbit=-1)

Declare this station to be a retrial queue for jobclass: a job that finds every server busy joins an orbit and re-attempts entry after a random delay, instead of waiting in a line.

This is the first-class form of the retrial idiom. It removes the waiting room itself, so the caller no longer has to know that set_capacity(nservers) is the way to express “no waiting room, blocked jobs orbit”.

Parameters:
  • jobclass – JobClass object

  • retrial_distribution – retrial delay of an orbiting job

  • policy – RetrialPolicy.LINEAR (default), where each orbiting job retries at its own rate so the aggregate rate is (orbit size)*nu, or RetrialPolicy.CONSTANT, where the orbit retries as a whole at rate nu whenever non-empty.

  • max_orbit – orbit capacity; -1 (default) leaves the orbit unbounded. A job that finds the orbit full is lost.

The mean orbit length is reported by get_avg_orbit / get_avg_orbit_table.

getOrbit(jobclass)

Return (retrial_distribution, policy, max_orbit) for jobclass at this station.

setBreakdown(failure_distribution, repair_distribution, down_service_distribution=None)

Make the server of this station subject to breakdowns. The server alternates between an UP and a DOWN status: while up it fails after failure_distribution, while down it is restored after repair_distribution.

The failure clock runs whenever the server is up, whether or not a job is in service, so a station can fail while idle. Arrivals are unaffected by the server status and keep queueing (subject to the station capacity) while the server is down. A job that is in service when the server fails is not lost: it stays at the station and, service being memoryless in the supported case, resumes when the server is repaired.

Parameters:
  • failure_distribution – time to failure of an up server (Exp)

  • repair_distribution – repair time of a down server (Exp)

  • down_service_distribution – optional service distribution used while the server is down, either a single Distribution applied to every class or a list indexed by class. Omitted or empty means the server does not serve at all while down, which is the usual breakdown model.

Only exponential failure and repair distributions are currently expanded into the joint (queue, server status) chain; anything else is rejected here rather than silently approximated.

getBreakdown()

Return (failure_distribution, repair_distribution, down_service_distribution) for this station, or (None, None, []) when the station is not subject to breakdowns.

hasBreakdown()

True iff a failure and a repair distribution are configured here.

setPatience(jobclass, distribution, impatience_type=None)

Set patience distribution for a job class.

Parameters:
  • jobclass – JobClass object

  • distribution – Patience distribution

  • impatience_type – ImpatienceType enum value (default: RENEGING)

getPatience(jobclass)

Get patience distribution for a job class, or None.

hasPatience(jobclass)

Check if a job class has patience configured.

getImpatienceType(jobclass)

Get impatience type for a job class, or None.

setImmediateFeedback(value)

Configure immediate feedback for this queue.

When enabled, a job that routes back to this queue (self-loop) stays in service instead of re-entering the queue.

Parameters:

value – bool (enable/disable for all classes), JobClass (enable for specific class), or list of JobClass (enable for specific classes)

hasImmediateFeedback(jobclass=None)

Check if immediate feedback is enabled.

Parameters:

jobclass – If provided, check for specific class. If None, check if any class has it.

getImmediateFeedbackClasses()

Get set of class indices with immediate feedback enabled, or ‘all’.

class Delay(model, name)[source]

Bases: Queue

Delay node (infinite server).

A Delay represents an infinite-capacity queue (think time station in closed models). All jobs immediately begin service without queueing.

Initialize a Delay node.

Parameters:
  • model – Network instance

  • name (str) – Delay node name

__init__(model, name)[source]

Initialize a Delay node.

Parameters:
  • model – Network instance

  • name (str) – Delay node name

set_number_of_servers(value)[source]

Set number of servers (always infinity for Delay).

Parameters:

value (int) – Ignored (always set to infinity)

Raises:

ValueError – If not infinity

class Sink(model, name)[source]

Bases: Node

Sink node for job departure.

A Sink is the exit point for open-class jobs. Each network can have at most one Sink node.

Initialize a Sink node.

Parameters:
  • model – Network instance

  • name (str) – Sink node name

__init__(model, name)[source]

Initialize a Sink node.

Parameters:
  • model – Network instance

  • name (str) – Sink node name

class Router(model, name)[source]

Bases: Node

Router node for routing decisions.

A Router is a node that routes jobs without service delay. Can be used for probabilistic routing and routing-dependent decisions.

Initialize a Router node.

Parameters:
  • model – Network instance

  • name (str) – Router node name

__init__(model, name)[source]

Initialize a Router node.

Parameters:
  • model – Network instance

  • name (str) – Router node name

class Fork(model, name)[source]

Bases: Node

Fork node for parallel processing.

A Fork splits an incoming job into multiple parallel tasks that must be synchronized at a corresponding Join node.

Initialize a Fork node.

Parameters:
  • model – Network instance

  • name (str) – Fork node name

__init__(model, name)[source]

Initialize a Fork node.

Parameters:
  • model – Network instance

  • name (str) – Fork node name

set_state(state)[source]

Set Fork state (FJ tag-augmented copies only).

get_state_prior()[source]

Get Fork state prior (FJ tag-augmented copies only).

set_state_prior(prior)[source]

Set Fork state prior (FJ tag-augmented copies only).

setStatePrior(prior)

Set Fork state prior (FJ tag-augmented copies only).

get_state_space()[source]

Get Fork state space (FJ tag-augmented copies only).

set_state_space(space)[source]

Set Fork state space (FJ tag-augmented copies only).

Set number of tasks spawned per outgoing link.

The total number of siblings a firing creates is (number of outgoing links) * ntasks. SolverJMT and SolverLDES simulate it directly; the MMT fork-join transform behind SolverMVA/SolverNC carries the load of all the siblings on the auxiliary open class and synchronises on the order statistic of that many branch times, each branch replicated ntasks times. The H-T transform refuses ntasks > 1.

Parameters:
  • ntasks – Task count emitted on each outgoing link

  • jobclass – Optional job class; when given, only that class is affected and every other class keeps the node-wide value

  • dest_node – Optional destination node; when given, only the link towards it is affected and the other links are left alone

Get tasks per link.

Make the number of tasks emitted on each outgoing link RANDOM.

The degree is redrawn independently for every link and every forked job, which is the variable forking level of JMT’s JobsPerLinkDis. Exact under SolverJMT and SolverLDES, which draw it at the fork epoch; the analytical solvers see E[dist].

Parameters:
  • jobclass – Job class the distribution applies to

  • dist – DiscreteSampler over the tasks-per-link support

  • dest_node – Optional destination node restricting it to one link

set_branch_probability(jobclass, dest_node, prob)[source]

Activate an outgoing branch only with probability prob.

Branches are activated independently, so the number of siblings a job produces is random even when the tasks per link are deterministic. The matched Join must be told what to wait for: under a standard join a job that skipped a branch would block forever, so any probability below one requires JoinStrategy.PARTIAL or a quorum.

Get the registered per-link jobs-per-link distributions.

get_branch_probability()[source]

Get the registered branch activation probabilities.

getStatePrior()

Get Fork state prior (FJ tag-augmented copies only).

setStateSpace(space)

Set Fork state space (FJ tag-augmented copies only).

getStateSpace()

Get Fork state space (FJ tag-augmented copies only).

Get tasks per link.

setTasksPerLinkDistribution(jobclass, dist, dest_node=None)

Make the number of tasks emitted on each outgoing link RANDOM.

The degree is redrawn independently for every link and every forked job, which is the variable forking level of JMT’s JobsPerLinkDis. Exact under SolverJMT and SolverLDES, which draw it at the fork epoch; the analytical solvers see E[dist].

Parameters:
  • jobclass – Job class the distribution applies to

  • dist – DiscreteSampler over the tasks-per-link support

  • dest_node – Optional destination node restricting it to one link

getTasksPerLinkDistribution()

Get the registered per-link jobs-per-link distributions.

setBranchProbability(jobclass, dest_node, prob)

Activate an outgoing branch only with probability prob.

Branches are activated independently, so the number of siblings a job produces is random even when the tasks per link are deterministic. The matched Join must be told what to wait for: under a standard join a job that skipped a branch would block forever, so any probability below one requires JoinStrategy.PARTIAL or a quorum.

getBranchProbability()

Get the registered branch activation probabilities.

property capacity: float

Get capacity.

class Join(model, name, fork=None)[source]

Bases: Station

Join node for parallel processing synchronization.

A Join waits for all parallel tasks from a corresponding Fork before releasing the job downstream.

Initialize a Join node.

Parameters:
  • model – Network instance

  • name (str) – Join node name

  • fork (Fork | None) – Associated Fork node (optional)

__init__(model, name, fork=None)[source]

Initialize a Join node.

Parameters:
  • model – Network instance

  • name (str) – Join node name

  • fork (Fork | None) – Associated Fork node (optional)

set_fork(fork)[source]

Set associated Fork node.

Parameters:

fork (Fork) – Fork node to pair with

get_fork()[source]

Get associated Fork node.

set_strategy(jobclass, strategy)[source]

Set join strategy for a job class.

Parameters:
get_strategy(jobclass)[source]

Get join strategy for a job class.

set_required(jobclass, nrequired)[source]

Set required number of tasks to wait for (quorum join).

Parameters:
  • jobclass (JobClass) – Job class

  • nrequired (int) – Number of required tasks (-1 = all)

get_required(jobclass)[source]

Get required task count.

class Cache(model, name, num_items, item_level_cap, replacement_strategy=ReplacementStrategy.LRU)[source]

Bases: Station

Multi-level cache node with configurable replacement strategy.

A Cache node models content caching behavior where arriving jobs request items from a catalog. Items may be cached (hit) or fetched (miss), with jobs potentially switching classes based on hit/miss outcomes.

The cache supports multiple levels (hierarchy) with different capacities and uses configurable replacement policies (LRU, FIFO, RR, SFIFO).

Cache analysis can be performed using algorithms from api.cache: - cache_mva: Mean Value Analysis for hit/miss probabilities - cache_erec: Exact recursive computation - cache_spm: Singular Perturbation Method (approximate)

Parameters:
  • model – Network instance

  • name (str) – Cache node name

  • num_items (int) – Number of items in the catalog (n)

  • item_level_cap (int | ndarray | list) – Capacity of each cache level (int or array)

  • replacement_strategy (ReplacementStrategy) – Cache replacement policy (LRU, FIFO, RR, SFIFO)

Example

>>> model = Network('CacheModel')
>>> cache = Cache(model, 'MyCache', num_items=100,
...               item_level_cap=10, replacement_strategy=ReplacementStrategy.LRU)
>>> job_class = ClosedClass(model, 'Request', 5, delay)
>>> hit_class = ClosedClass(model, 'Hit', 0, delay)
>>> miss_class = ClosedClass(model, 'Miss', 0, delay)
>>> cache.set_read(job_class, Zipf(1.2, 100))  # Zipf popularity
>>> cache.set_hit_class(job_class, hit_class)
>>> cache.set_miss_class(job_class, miss_class)
Reference:
  • Cache algorithms: Che, H. et al. “Hierarchical Web Caching Systems”

  • TTL approximation: Fofack et al. “Analysis of TTL-based Cache Networks”

Initialize a Cache node.

Parameters:
  • model – Network instance

  • name (str) – Cache node name

  • num_items (int) – Total number of items in catalog (n >= 1)

  • item_level_cap (int | ndarray | list) – Capacity per cache level. Can be: - int: Single-level cache with given capacity - array/list: Multi-level cache with capacity per level

  • replacement_strategy (ReplacementStrategy) – Replacement policy (default: LRU)

Raises:

ValueError – If num_items < 1 or item_level_cap invalid

__init__(model, name, num_items, item_level_cap, replacement_strategy=ReplacementStrategy.LRU)[source]

Initialize a Cache node.

Parameters:
  • model – Network instance

  • name (str) – Cache node name

  • num_items (int) – Total number of items in catalog (n >= 1)

  • item_level_cap (int | ndarray | list) – Capacity per cache level. Can be: - int: Single-level cache with given capacity - array/list: Multi-level cache with capacity per level

  • replacement_strategy (ReplacementStrategy) – Replacement policy (default: LRU)

Raises:

ValueError – If num_items < 1 or item_level_cap invalid

is_station()[source]

Check if node is a station.

Cache nodes are NOT stations in LINE’s semantics, matching MATLAB behavior. They are stateful nodes that immediately process arriving jobs (class switching based on cache hit/miss) but don’t queue or serve jobs like stations.

Returns:

Cache is a stateful non-station node

Return type:

False

has_class_switching()[source]

A Cache switches the arriving class into its hit or miss class (MATLAB: its server is a CacheClassSwitcher, itself a ClassSwitcher).

property num_items: int

Get the number of items in the catalog.

property num_levels: int

Get the number of cache levels.

property item_level_cap: ndarray

Get the capacity array for each cache level.

property total_capacity: int

Get the total cache capacity across all levels.

property replacement_strategy: ReplacementStrategy

Get the cache replacement strategy.

remove_job_class(jobclass)[source]

Reject class removal on a cache node.

The cache item state is indexed by class and is carried in the model state, not only in the node configuration, so a class cannot be dropped without invalidating it. Matches MATLAB @MNetwork/removeClass.m and Cache.removeJobClass in the JAR, which both refuse.

Parameters:

jobclass – the job class being removed from the model

Raises:

RuntimeError – always

removeJobClass(jobclass)

Reject class removal on a cache node.

The cache item state is indexed by class and is carried in the model state, not only in the node configuration, so a class cannot be dropped without invalidating it. Matches MATLAB @MNetwork/removeClass.m and Cache.removeJobClass in the JAR, which both refuse.

Parameters:

jobclass – the job class being removed from the model

Raises:

RuntimeError – always

set_hit_class(input_class, output_class)[source]

Set the output class for cache hits.

When a job of input_class experiences a cache hit, it transitions to output_class for further processing.

Parameters:
  • input_class (JobClass) – Incoming job class making the request

  • output_class (JobClass) – Resulting job class after cache hit

get_hit_class(input_class)[source]

Get the output class for cache hits.

set_miss_class(input_class, output_class)[source]

Set the output class for cache misses.

When a job of input_class experiences a cache miss, it transitions to output_class for further processing.

Parameters:
  • input_class (JobClass) – Incoming job class making the request

  • output_class (JobClass) – Resulting job class after cache miss

get_miss_class(input_class)[source]

Get the output class for cache misses.

set_read(jobclass, distribution)[source]

Set the read (popularity) distribution for a job class.

The distribution determines which items are requested by jobs of the given class. Typically a discrete distribution like Zipf.

Parameters:
  • jobclass (JobClass) – Job class making requests

  • distribution – Popularity distribution (e.g., Zipf, Uniform)

set_item_read_classes(read_classes, hit_classes)[source]

Declare that read_classes[i] is the request stream for item i at this cache.

Use at the cache the exogenous requests enter, where the per-item classes are the user’s own; item popularity is then carried by the per-class request rates rather than by a popularity distribution the cache draws from. This is what keeps a cache network free of arc-level class switching, so no class acquires a default route into the cache that the model never intended.

set_item_classes(jobin_class, hit_classes)[source]

Mint one class per item at a cache FED BY ANOTHER CACHE, so item identity survives the miss hop. Idempotent.

set_miss_cache(jobin_class, next_cache, hit_class_at_next)[source]

Send this cache’s misses to next_cache preserving item identity: the miss class of this cache for item i IS the read class of next_cache for item i. Returns the classes minted on next_cache so the caller can route them onward.

set_item_miss_class(jobin_class, miss_classes)[source]

Terminate a cache network: every per-item class of this cache reports a miss as the matching entry of miss_classes, which the user routes onward.

set_item_of_class(jobclass, item)[source]

Record that a class reads a given item (1-based), as read back from JSON.

get_item_of_class(jobclass)[source]

Item a per-item class reads (1-based), 0 when the class is not one.

get_read(jobclass)[source]

Get the read distribution for a job class.

set_access_prob(access_prob)[source]

Set the access probability matrix directly.

Alternative to set_read() for specifying item access probabilities as a matrix.

Parameters:

access_prob (ndarray) – (num_items, num_classes) probability matrix. Each column should sum to 1.

set_admission_prob(q)[source]

Set the q-LRU admission probability.

Probability q in [0, 1] of admitting a missed item into the cache. Only used when the replacement strategy is QLRU.

set_item_sizes(sizes)[source]

Set the storage cost (size) of each item.

A positive integer vector with one entry per item, or a single value applied to every item. Used together with set_cost_caps to bound the storage held by each cache list.

get_item_sizes()[source]

Get the per-item storage costs (sizes), None when unset.

set_cost_caps(caps)[source]

Set the per-list cap on the total storage cost of the resident items.

A single value declares one cap for the whole cache, modelled as the same cap on every list.

get_cost_caps()[source]

Get the per-list storage cost caps, None when unset.

is_cost_cap_global()[source]

Report whether the cost caps came from a single cache-wide cap.

set_result_list_cost(list_cost)[source]

Store the mean storage cost held by each list, as computed by a solver.

get_list_cost()[source]

Get the mean storage cost held by each list, None when not computed.

get_access_prob()[source]

Get the access probability matrix.

set_access_graph(graph)[source]

Set the per-item access-cost (list-move) graph.

Mirrors the MATLAB Cache constructor’s graph argument: one (h+1) x (h+1) matrix per item, shared by all job classes, where h is the number of cache lists. Row 1 governs miss insertion (column 1 = do not cache, column 1+l = insert into list l); row 1+i governs hits in list i (column 1+j = move the item to list j >= i).

Parameters:

graph – sequence of num_items matrices, each (h+1) x (h+1).

get_access_graph()[source]

Return the per-item access-cost graph, or None if not set.

set_result_hit_prob(hit_prob)[source]

Set the computed hit probability (called by solver).

ABSENT CLEARS: a None here means the solve did not produce the quantity, and the previous solver’s value must not survive into the next answer.

set_result_miss_prob(miss_prob)[source]

Set the computed miss probability (called by solver). None clears.

set_result_delayed_hit_prob(delayed_hit_prob)[source]

Set the computed delayed-hit fraction (retrieval system). None clears.

set_result_hit_prob_list(hit_prob_list)[source]

Set the per-list (per-level) hit fractions [classes x lists]. None clears.

get_delayed_hit_ratio()[source]

Get the delayed-hit fraction per class (None when no retrieval system).

get_hit_ratio_by_list()[source]

Get the per-class, per-list hit fraction matrix [classes x lists].

set_result_item_prob(item_prob)[source]

Set the per-item occupancy [items x (lists+1)]; col 0 = miss, cols 1.. = per-list.

get_item_prob()[source]

Get the per-item occupancy matrix [items x (lists+1)] (col 0 = miss).

get_gamma_matrix(nclasses=1)[source]

Build the gamma (access factor) matrix for cache analysis.

The gamma matrix has shape (num_items, num_levels) and contains the cache access intensity factors used by cache_mva/cache_erec.

Parameters:

nclasses (int) – Number of job classes

Returns:

Gamma matrix for cache analysis algorithms

Return type:

ndarray

get_capacity_vector()[source]

Get the cache capacity vector.

Returns:

Array of cache level capacities

Return type:

ndarray

property total_cache_capacity: int

Total cache capacity (sum of per-level capacities).

property retrieval_system_capacity: int

Number of items that can be in the retrieval system simultaneously (0 when no retrieval system is configured).

set_result_delayed_hit_qlen(d1, dfull)[source]

Set the per-item delayed-hit queue length (called by solver).

d1 is the mean number of secondary requests waiting on the in-flight fetch of each item; dfull additionally counts the request that triggered the fetch.

get_delayed_hit_qlen()[source]

Per-item delayed-hit queue length (d1, dfull), or (None, None).

set_result_residt(expected_latency)[source]

Set the computed expected latency (called by solver).

get_residt()[source]

Get the expected latency per class, or None if not computed yet.

reset()[source]

Clear solver result fields.

set_retrieval_class(input_class, output_class, item)[source]

Set the retrieval class for (item, input_class). item is 0-based.

get_retrieval_class(input_class, item)[source]

Get the retrieval class for (item, input_class).

set_retrieval_system(jobin_class, miss_class, queues)[source]

Initialise the retrieval system through which a request that misses the cache is fetched. While the retrieval is pending, repeat requests for the same item become delayed hits; after retrieval completes the job switches into miss_class.

Routing and service are NOT passed here; they are taken from the read class:
  • service: the read class’s service distribution at each queue. Call queue.set_service(jobin_class, ...) beforehand; override per item with queue.set_item_service_rate(cache, jobin_class, item, rate).

  • routing: the read class’s routing among the retrieval queues drawn in the top-level routing matrix P; override per item with set_item_routing_prob (pass the cache as source for a cache->queue entry, or as dest for a queue->cache exit).

Parameters:
  • jobin_class (JobClass) – arrival JobClass routed through the retrieval system

  • miss_class (JobClass) – JobClass a completed retrieval transitions into

  • queues – a Queue or list of Queue nodes comprising the system

set_item_routing_probability(jobin_class, item, source, dest, probability)[source]

Probability of routing the retrieval class for item between two nodes of the retrieval system. source/dest are either a retrieval queue or the cache itself: pass the cache as source for a cache->queue entry, or as dest for a queue->cache exit. item is 0-based. Requires a prior set_retrieval_system() call.

setRetrievalSystem(jobin_class, miss_class, queues)

Initialise the retrieval system through which a request that misses the cache is fetched. While the retrieval is pending, repeat requests for the same item become delayed hits; after retrieval completes the job switches into miss_class.

Routing and service are NOT passed here; they are taken from the read class:
  • service: the read class’s service distribution at each queue. Call queue.set_service(jobin_class, ...) beforehand; override per item with queue.set_item_service_rate(cache, jobin_class, item, rate).

  • routing: the read class’s routing among the retrieval queues drawn in the top-level routing matrix P; override per item with set_item_routing_prob (pass the cache as source for a cache->queue entry, or as dest for a queue->cache exit).

Parameters:
  • jobin_class (JobClass) – arrival JobClass routed through the retrieval system

  • miss_class (JobClass) – JobClass a completed retrieval transitions into

  • queues – a Queue or list of Queue nodes comprising the system

getResidT()

Get the expected latency per class, or None if not computed yet.

setResultResidT(expected_latency)

Set the computed expected latency (called by solver).

setItemRoutingProbability(jobin_class, item, source, dest, probability)

Probability of routing the retrieval class for item between two nodes of the retrieval system. source/dest are either a retrieval queue or the cache itself: pass the cache as source for a cache->queue entry, or as dest for a queue->cache exit. item is 0-based. Requires a prior set_retrieval_system() call.

set_item_routing_prob(jobin_class, item, source, dest, probability)

Probability of routing the retrieval class for item between two nodes of the retrieval system. source/dest are either a retrieval queue or the cache itself: pass the cache as source for a cache->queue entry, or as dest for a queue->cache exit. item is 0-based. Requires a prior set_retrieval_system() call.

setItemRoutingProb(jobin_class, item, source, dest, probability)

Probability of routing the retrieval class for item between two nodes of the retrieval system. source/dest are either a retrieval queue or the cache itself: pass the cache as source for a cache->queue entry, or as dest for a queue->cache exit. item is 0-based. Requires a prior set_retrieval_system() call.

getHitClass(input_class)

Get the output class for cache hits.

getMissClass(input_class)

Get the output class for cache misses.

getRead(jobclass)

Get the read distribution for a job class.

setAccessProb(access_prob)

Set the access probability matrix directly.

Alternative to set_read() for specifying item access probabilities as a matrix.

Parameters:

access_prob (ndarray) – (num_items, num_classes) probability matrix. Each column should sum to 1.

getAccessProb()

Get the access probability matrix.

setResultHitProb(hit_prob)

Set the computed hit probability (called by solver).

ABSENT CLEARS: a None here means the solve did not produce the quantity, and the previous solver’s value must not survive into the next answer.

setResultMissProb(miss_prob)

Set the computed miss probability (called by solver). None clears.

setResultDelayedHitProb(delayed_hit_prob)

Set the computed delayed-hit fraction (retrieval system). None clears.

setResultHitProbList(hit_prob_list)

Set the per-list (per-level) hit fractions [classes x lists]. None clears.

getDelayedHitRatio()

Get the delayed-hit fraction per class (None when no retrieval system).

getHitRatioByList()

Get the per-class, per-list hit fraction matrix [classes x lists].

hit_ratio()

Get the hit ratio (probability) for each class.

Returns:

Hit probability array, or None if not computed yet.

Return type:

ndarray | None

miss_ratio()

Get the miss ratio (probability) for each class.

Returns:

Miss probability array, or None if not computed yet.

Return type:

ndarray | None

getNumItems()[source]

Get number of items (MATLAB compatibility).

getNumLevels()[source]

Get number of cache levels (MATLAB compatibility).

getCapacity()[source]

Get total cache capacity (MATLAB compatibility).

getReplacementStrategy()[source]

Get replacement strategy (MATLAB compatibility).

getItemLevelCap()[source]

Get per-level capacity array (MATLAB compatibility).

class Logger(model, name, log_file_name)[source]

Bases: Node

Logger node for recording job passage.

A Logger node records arrival and departure timestamps for jobs passing through it. Used internally by getCdfRespT to collect response time samples via transient simulation.

Ported from MATLAB’s Logger class in matlab/src/lang/nodes/Logger.m

Initialize a Logger node.

Parameters:
  • model – Network instance

  • name (str) – Logger name

  • log_file_name (str) – Full path to the log file

__init__(model, name, log_file_name)[source]

Initialize a Logger node.

Parameters:
  • model – Network instance

  • name (str) – Logger name

  • log_file_name (str) – Full path to the log file

property file_name: str

Get the log file name.

property file_path: str

Get the log file path.

get_start_time()[source]
get_logger_name()[source]
get_timestamp()[source]
get_job_id()[source]
get_job_class()[source]
get_time_same_class()[source]
get_time_any_class()[source]
set_start_time(value)[source]
set_logger_name(value)[source]
set_timestamp(value)[source]
set_job_id(value)[source]
set_job_class(value)[source]
set_time_same_class(value)[source]
set_time_any_class(value)[source]
set_prob_routing(jobclass, destination, probability)[source]

Set probabilistic routing to a destination.

Parameters:
  • jobclass (JobClass) – Job class

  • destination – Destination node

  • probability (float) – Routing probability

getStartTime()
getLoggerName()
getTimestamp()
getJobID()
getJobClass()
getTimeSameClass()
getTimeAnyClass()
setLoggerName(value)
property fileName
property filePath
class ClassSwitch(model, name, cs_matrix=None)[source]

Bases: Node

ClassSwitch node for job class switching.

A ClassSwitch allows jobs to change class without service delay. Useful for modeling class-dependent routing and scheduling.

Initialize a ClassSwitch node.

Parameters:
  • model – Network instance

  • name (str) – ClassSwitch node name

  • cs_matrix – Optional K×K class switching probability matrix where element (i,j) is the probability that a job in class i switches to class j. If not provided, must be set later using set_class_switching_matrix().

__init__(model, name, cs_matrix=None)[source]

Initialize a ClassSwitch node.

Parameters:
  • model – Network instance

  • name (str) – ClassSwitch node name

  • cs_matrix – Optional K×K class switching probability matrix where element (i,j) is the probability that a job in class i switches to class j. If not provided, must be set later using set_class_switching_matrix().

has_class_switching()[source]

A ClassSwitch node always switches classes (MATLAB: its server is a StatelessClassSwitcher).

init_class_switch_matrix()[source]

Initialize and return a class switching matrix.

Creates a K×K matrix of zeros where K is the number of classes. Use this to create a template that can be filled with switching probabilities before calling set_class_switching_matrix().

Returns:

K×K matrix initialized to zeros

Return type:

np.ndarray

Example

>>> csmatrix = cs_node.init_class_switch_matrix()
>>> csmatrix[0, 1] = 0.3  # 30% switch from class 0 to class 1
>>> csmatrix[0, 0] = 0.7  # 70% stay in class 0
>>> csmatrix[1, 0] = 1.0  # 100% switch from class 1 to class 0
>>> cs_node.set_class_switching_matrix(csmatrix)
init_class_switching_matrix()

Initialize and return a class switching matrix.

Creates a K×K matrix of zeros where K is the number of classes. Use this to create a template that can be filled with switching probabilities before calling set_class_switching_matrix().

Returns:

K×K matrix initialized to zeros

Return type:

np.ndarray

Example

>>> csmatrix = cs_node.init_class_switch_matrix()
>>> csmatrix[0, 1] = 0.3  # 30% switch from class 0 to class 1
>>> csmatrix[0, 0] = 0.7  # 70% stay in class 0
>>> csmatrix[1, 0] = 1.0  # 100% switch from class 1 to class 0
>>> cs_node.set_class_switching_matrix(csmatrix)
set_class_switching_matrix(cs_matrix)[source]

Set the class switching probability matrix.

Parameters:

cs_matrix (ndarray) – K×K matrix where element (i,j) is the probability that a job in class i switches to class j. Each row should sum to 1.0.

Example

>>> csmatrix = cs_node.init_class_switch_matrix()
>>> csmatrix[0, 0] = 0.7
>>> csmatrix[0, 1] = 0.3
>>> cs_node.set_class_switching_matrix(csmatrix)
get_class_switching_matrix()[source]

Get the current class switching matrix.

Returns:

The K×K switching matrix, or None if not set

Return type:

np.ndarray or None

getClassSwitchingMatrix()

Get the current class switching matrix.

Returns:

The K×K switching matrix, or None if not set

Return type:

np.ndarray or None

set_switch_probability(from_class, to_class, probability)[source]

Set the probability of switching from one class to another.

This is a convenience method that handles indexing automatically.

Parameters:
  • from_class – Source job class (object or 0-based index)

  • to_class – Target job class (object or 0-based index)

  • probability (float) – Switching probability (0.0 to 1.0)

Example

>>> cs_node.set_switch_probability(class1, class2, 0.3)
>>> cs_node.set_switch_probability(class1, class1, 0.7)
setSwitchProbability(from_class, to_class, probability)

Set the probability of switching from one class to another.

This is a convenience method that handles indexing automatically.

Parameters:
  • from_class – Source job class (object or 0-based index)

  • to_class – Target job class (object or 0-based index)

  • probability (float) – Switching probability (0.0 to 1.0)

Example

>>> cs_node.set_switch_probability(class1, class2, 0.3)
>>> cs_node.set_switch_probability(class1, class1, 0.7)
remove_job_class(jobclass)[source]

Drop a job class from this node and from its class-switching matrix.

The matrix is indexed by class position, not by class object, so the removed row and column are deleted while the model still holds the class (Network.remove_class updates the nodes before dropping the class). Mirrors ClassSwitch.removeJobClass in the JAR.

Parameters:

jobclass – the job class being removed from the model

removeJobClass(jobclass)

Drop a job class from this node and from its class-switching matrix.

The matrix is indexed by class position, not by class object, so the removed row and column are deleted while the model still holds the class (Network.remove_class updates the nodes before dropping the class). Mirrors ClassSwitch.removeJobClass in the JAR.

Parameters:

jobclass – the job class being removed from the model

class Place(model, name, sched_strategy=None)[source]

Bases: Station

Place node for Stochastic Petri Nets.

A Place represents a location where tokens (jobs) can accumulate. Places have infinite capacity (like Delay nodes) and infinite servers.

In queueing network terms, a Place is similar to a delay station but tokens are processed when they move to Transitions.

Initialize a Place node.

Parameters:
  • model – Network instance

  • name (str) – Place name

  • sched_strategy – optional scheduling strategy of the embedded queue. When provided (and a service process is later assigned via set_service), the place becomes a queueing place (QPN semantics) rather than an ordinary/INF pass-through place.

__init__(model, name, sched_strategy=None)[source]

Initialize a Place node.

Parameters:
  • model – Network instance

  • name (str) – Place name

  • sched_strategy – optional scheduling strategy of the embedded queue. When provided (and a service process is later assigned via set_service), the place becomes a queueing place (QPN semantics) rather than an ordinary/INF pass-through place.

set_service(jobclass, distribution)[source]

Assign a service process to a token color, turning this ordinary place into a queueing place (QPN semantics). The embedded queue serves tokens under the place’s scheduling strategy; on completion tokens move to the depository from which output transitions consume them.

Parameters:
  • jobclass (JobClass) – token color (job class)

  • distribution – service-time distribution of the embedded queue

get_service(jobclass)[source]

Get the embedded-queue service distribution for a job class (or None).

getService(jobclass)

Get the embedded-queue service distribution for a job class (or None).

is_queueing()[source]

True if this place is a queueing place (has an embedded queue).

isQueueing()

True if this place is a queueing place (has an embedded queue).

set_departure_discipline(jobclass, discipline)[source]

Set the depository departure discipline for a token color.

setDepartureDiscipline(jobclass, discipline)

Set the depository departure discipline for a token color.

set_number_of_servers(k)[source]

Set the number of servers of the embedded queue (multiserver queueing place).

set_class_capacity(jobclass, capacity)[source]

Set per-class capacity limit.

Parameters:
  • jobclass (JobClass) – Job class

  • capacity (float) – Capacity for this class

set_drop_rule(jobclass, drop_rule)[source]

Set the per-class drop rule for a bounded place.

Determines what happens to a token that would exceed the place capacity (DropStrategy.DROP for a loss place, WAITQ/BAS/… for blocking). Mirrors MATLAB Place.setDropRule.

Parameters:
  • jobclass (JobClass) – token color (job class)

  • drop_rule – a DropStrategy value

set_state(state)[source]

Set initial token state for this place.

Parameters:

state – Array of initial token counts per class

set_marking(state)

Set initial token state for this place.

Parameters:

state – Array of initial token counts per class

setMarking(state)

Set initial token state for this place.

Parameters:

state – Array of initial token counts per class

class Transition(model, name)[source]

Bases: StatefulNode

Transition node for Stochastic Petri Nets.

A Transition consumes tokens from input Places and produces tokens to output Places according to defined firing modes.

Each Transition can have multiple firing modes, each with: - Enabling conditions: required tokens from input places - Inhibiting conditions: blocking conditions from places - Firing outcomes: tokens produced to output places - Timing distribution: delay before firing - Priority and weight for conflict resolution

Initialize a Transition node.

Parameters:
  • model – Network instance

  • name (str) – Transition name

__init__(model, name)[source]

Initialize a Transition node.

Parameters:
  • model – Network instance

  • name (str) – Transition name

add_mode(mode_name)[source]

Add a new firing mode to this transition.

Parameters:

mode_name (str) – Name for the new mode

Returns:

Mode object representing the new mode

Return type:

Mode

set_enabling_conditions(mode, jobclass, input_node, enabling_condition)[source]

Set enabling conditions for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • jobclass (JobClass) – Job class

  • input_node – Input Place node

  • enabling_condition (int) – Number of tokens required

set_inhibiting_conditions(mode, jobclass, input_node, inhibiting_condition)[source]

Set inhibiting conditions for a mode.

The transition cannot fire if the place has >= inhibiting_condition tokens.

Parameters:
  • mode – Mode object or index (1-based)

  • jobclass (JobClass) – Job class

  • input_node – Input Place node

  • inhibiting_condition (int) – Number of tokens that inhibit firing

set_firing_outcome(mode, jobclass, output_node, firing_outcome)[source]

Set firing outcome for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • jobclass (JobClass) – Job class

  • output_node – Output Place or Sink node

  • firing_outcome (int) – Number of tokens produced

set_distribution(mode, distribution)[source]

Set firing time distribution for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • distribution – Timing distribution (Exp, Erlang, etc.)

get_distribution(mode)[source]

Get firing time distribution for a mode.

getDistribution(mode)

Get firing time distribution for a mode.

set_firing_rate_dependence(mode, g)[source]

Marking-dependent firing-rate multiplier for a timed mode.

g(m) takes the node-indexed input-place marking matrix and returns a positive scalar; the effective firing rate of an enabled binding becomes rate_base(mode)*g(m). Exact only for memoryless firing, so the mode must be TIMED and exponentially distributed (mirrors the PS/FCFS-only station load/class dependence). Pass None to restore the unit multiplier.

setFiringRateDependence(mode, g)

Marking-dependent firing-rate multiplier for a timed mode.

g(m) takes the node-indexed input-place marking matrix and returns a positive scalar; the effective firing rate of an enabled binding becomes rate_base(mode)*g(m). Exact only for memoryless firing, so the mode must be TIMED and exponentially distributed (mirrors the PS/FCFS-only station load/class dependence). Pass None to restore the unit multiplier.

get_firing_rate_dependence(mode)[source]

Get the marking-dependent firing-rate multiplier for a mode (or None).

getFiringRateDependence(mode)

Get the marking-dependent firing-rate multiplier for a mode (or None).

set_number_of_servers(mode, num_servers)[source]

Set number of servers for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • num_servers (int) – Number of servers (or GlobalConstants.MaxInt for infinite)

set_timing_strategy(mode, timing_strategy)[source]

Set timing strategy for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • timing_strategy (TimingStrategy) – TIMED or IMMEDIATE

set_firing_priorities(mode, priority)[source]

Set firing priority for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • priority (float) – Priority value (higher = more priority)

set_firing_weights(mode, weight)[source]

Set firing weight for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • weight (float) – Weight for probabilistic selection among enabled modes

set_mode_names(mode, mode_name)[source]

Set name for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • mode_name (str) – New name for the mode

setModeNames(mode, mode_name)

Set name for a mode.

Parameters:
  • mode – Mode object or index (1-based)

  • mode_name (str) – New name for the mode

get_service_rates()[source]

Get service rates for all modes.

Returns:

Tuple of (map, mu, phi) lists for PH-type service rates

getServiceRates()

Get service rates for all modes.

Returns:

Tuple of (map, mu, phi) lists for PH-type service rates

Job Class Classes

class JobClass(model_or_type, name_or_jobclass_type=None, jobclass_type_or_prio=None, prio_or_deadline=None)[source]

Bases: NetworkElement

Abstract base class for job classes.

Initialize a job class.

Supports two signatures for compatibility: 1. (model, name, JobClassType, priority) - from classes.py 2. (jobclass_type, name, priority, deadline) - native implementation

Parameters:
  • model_or_type – Model instance or JobClassType

  • name_or_jobclass_type (str) – Class name or JobClassType

  • jobclass_type_or_prio – JobClassType or priority

  • prio_or_deadline – Priority or deadline

__init__(model_or_type, name_or_jobclass_type=None, jobclass_type_or_prio=None, prio_or_deadline=None)[source]

Initialize a job class.

Supports two signatures for compatibility: 1. (model, name, JobClassType, priority) - from classes.py 2. (jobclass_type, name, priority, deadline) - native implementation

Parameters:
  • model_or_type – Model instance or JobClassType

  • name_or_jobclass_type (str) – Class name or JobClassType

  • jobclass_type_or_prio – JobClassType or priority

  • prio_or_deadline – Priority or deadline

property jobclass_type: JobClassType

Get the job class type.

summary()[source]

Print the one-line class summary, twin of MATLAB JobClass.summary.

__index__()[source]

Return zero-based index for Python array indexing.

property priority: int

Get scheduling priority.

property deadline: float

Get relative deadline.

property completes: bool

Get whether this class completes a visit.

When completes=True (default), visiting this class contributes to the system response time. When completes=False, the class represents an intermediate step that doesn’t count towards completion.

property is_reference_class: bool

Get whether this class is the reference class for its chain.

When is_reference_class=True, this class’s visits at the reference station are used as the denominator for WN (residence time) computation. Typically set to True for the TASK class in LN layer models.

setReferenceClass(value)[source]

Set whether this class is the reference class for its chain (MATLAB-compatible name).

set_reference_class(value)[source]

Set whether this class is the reference class (snake_case alias).

isReferenceClass()[source]

Get whether this class is the reference class (MATLAB-compatible name).

property immediateFeedback: bool

Get whether immediate feedback is enabled globally for this class.

setImmediateFeedback(value)[source]

Set immediate feedback for this class (MATLAB-compatible name).

hasImmediateFeedback()[source]

Check if immediate feedback is enabled for this class.

set_immediate_feedback(value)

Set immediate feedback for this class (MATLAB-compatible name).

has_immediate_feedback()

Check if immediate feedback is enabled for this class.

set_reference_station(station)[source]

Set reference station for this class.

Parameters:

station – Reference Station node

get_reference_station()[source]

Get reference station.

is_reference_station(node)[source]

Check if a node is the reference station.

set_patience(*args)[source]

Set the class-level patience (impatience) distribution.

Mirrors MATLAB JobClass.setPatience and Java JobClass.setPatience:

  • set_patience(distribution): defaults the impatience type to RENEGING.

  • set_patience(impatience_type, distribution): sets both.

A node-scoped patience set with Queue.set_patience(jobclass, ...) takes precedence over the class-level one.

Parameters:
  • impatience_type – ImpatienceType (RENEGING or BALKING). The legacy strings ‘reneging’/’balking’ are also accepted.

  • distribution – Patience distribution.

get_patience()[source]

Get patience distribution (if any).

get_impatience_type()[source]

Get the class-level ImpatienceType (or None if no patience is set).

has_patience()[source]

Check if class has patience.

set_reply_signal_class(reply_class)[source]

Set reply signal class for synchronous calls.

get_reply_signal_class()[source]

Get reply signal class.

set_spawn_class(spawn_class)[source]

Set the class injected at the same station on each service completion of this class (LQN phase-2 continuation).

get_spawn_class()[source]

Get the spawn-on-completion class.

setPriority(value)[source]

Set scheduling priority .

getDeadline()[source]

Get relative deadline .

setDeadline(value)[source]

Set relative deadline .

getCompletes()[source]

Get whether this class completes a visit .

setCompletes(value)[source]

Set whether this class completes a visit .

setNumberOfJobs(njobs)[source]

Set the class population; only meaningful for closed classes.

set_number_of_jobs(njobs)

Set the class population; only meaningful for closed classes.

setReferenceStation(station)[source]

Alias for set_reference_station (CamelCase).

getReferenceStation()[source]

Alias for get_reference_station (CamelCase).

isReferenceStation(station)[source]

Alias for is_reference_station (CamelCase).

setPatience(*args)[source]

Alias for set_patience (CamelCase).

Accepts setPatience(distribution) or setPatience(impatience_type, distribution). It previously forwarded a single argument into the two-argument-only set_patience, so every call raised TypeError.

getPatience()[source]

Alias for get_patience (CamelCase).

getImpatienceType()[source]

Alias for get_impatience_type (CamelCase).

hasPatience()[source]

Alias for has_patience (CamelCase).

setReplySignalClass(class_obj)[source]

Alias for set_reply_signal_class (CamelCase).

getReplySignalClass()[source]

Alias for get_reply_signal_class (CamelCase).

set_priority(value)

Set scheduling priority .

get_deadline()

Get relative deadline .

set_deadline(value)

Set relative deadline .

get_completes()

Get whether this class completes a visit .

set_completes(value)

Set whether this class completes a visit .

class OpenClass(model, name, prio=0, deadline=float('inf'))[source]

Bases: JobClass

Open job class for external arrivals.

Jobs in an open class arrive from outside the system according to an arrival process and leave the system after completing service.

Parameters:
  • model (Network) – Parent network model.

  • name (str) – Name of the job class.

  • prio (int) – Priority level (default: 0).

  • deadline (float) – Soft deadline for response time (default: inf).

Initialize a job class.

Supports two signatures for compatibility: 1. (model, name, JobClassType, priority) - from classes.py 2. (jobclass_type, name, priority, deadline) - native implementation

Parameters:
  • model_or_type – Model instance or JobClassType

  • name_or_jobclass_type – Class name or JobClassType

  • jobclass_type_or_prio – JobClassType or priority

  • prio_or_deadline – Priority or deadline

property completes: bool

Whether jobs complete counting towards system throughput.

setCompletes(value)[source]

Set whether jobs complete counting towards system throughput.

getCompletes()[source]

Get whether jobs complete counting towards system throughput.

getDeadline()[source]

Get the soft deadline for this class.

setDeadline(deadline)[source]

Set the soft deadline for this class.

set_completes(value)

Set whether jobs complete counting towards system throughput.

get_completes()

Get whether jobs complete counting towards system throughput.

get_deadline()

Get the soft deadline for this class.

set_deadline(deadline)

Set the soft deadline for this class.

class ClosedClass(model, name, njobs, refstat, prio=0, deadline=float('inf'))[source]

Bases: JobClass

Closed job class with fixed population.

Jobs in a closed class circulate within the system with a fixed population. Jobs never leave the system but cycle through stations.

Parameters:
  • model (Network) – Parent network model.

  • name (str) – Name of the job class.

  • njobs (int) – Number of jobs (population) in this class.

  • refstat (Station) – Reference station for this class.

  • prio (int) – Priority level (default: 0).

  • deadline (float) – Soft deadline for response time (default: inf).

Initialize a job class.

Supports two signatures for compatibility: 1. (model, name, JobClassType, priority) - from classes.py 2. (jobclass_type, name, priority, deadline) - native implementation

Parameters:
  • model_or_type – Model instance or JobClassType

  • name_or_jobclass_type – Class name or JobClassType

  • jobclass_type_or_prio – JobClassType or priority

  • prio_or_deadline – Priority or deadline

setNumberOfJobs(njobs)[source]

Set the population of this closed class.

property completes: bool

Whether jobs complete counting towards system throughput.

setCompletes(value)[source]

Set whether jobs complete counting towards system throughput.

getCompletes()[source]

Get whether jobs complete counting towards system throughput.

getDeadline()[source]

Get the soft deadline for this class.

setDeadline(deadline)[source]

Set the soft deadline for this class.

set_number_of_jobs(njobs)

Set the population of this closed class.

property population: int

Get the population (alias for getNumberOfJobs).

property number_of_jobs: int

Get the population of this closed class.

set_completes(value)

Set whether jobs complete counting towards system throughput.

get_completes()

Get whether jobs complete counting towards system throughput.

get_deadline()

Get the soft deadline for this class.

set_deadline(deadline)

Set the soft deadline for this class.

class SelfLoopingClass(model, name, njobs, refstat, prio=0)[source]

Bases: ClosedClass

Self-looping closed class.

Jobs in this class perpetually loop at their reference station, useful for modeling background workloads or special scheduling.

Parameters:
  • model (Network) – Parent network model.

  • name (str) – Name of the job class.

  • njobs (int) – Number of jobs in this class.

  • refstat (Station) – Reference station where jobs loop.

  • prio (int) – Priority level (default: 0).

Initialize a job class.

Supports two signatures for compatibility: 1. (model, name, JobClassType, priority) - from classes.py 2. (jobclass_type, name, priority, deadline) - native implementation

Parameters:
  • model_or_type – Model instance or JobClassType

  • name_or_jobclass_type – Class name or JobClassType

  • jobclass_type_or_prio – JobClassType or priority

  • prio_or_deadline – Priority or deadline

Routing and Structure

class RoutingMatrix(network)[source]

Bases: object

Matrix representing routing probabilities between network nodes.

The routing matrix defines how jobs move between nodes in the network, specifying the probability that a job leaving one node will arrive at another node.

This is a pure Python implementation that stores routing data in numpy arrays and can be converted to Java when needed.

Parameters:

network (Network) – The parent network this routing matrix belongs to.

Initialize a routing matrix for a network.

Parameters:

network (Network) – The parent network.

__init__(network)[source]

Initialize a routing matrix for a network.

Parameters:

network (Network) – The parent network.

set(*args)[source]

Set routing probabilities in the matrix.

Supports multiple argument patterns: - 5 args: set(class_source, class_dest, node_source, node_dest, probability) - 3 args: set(class_source, class_dest, routing_matrix) - 2 args: set(jobclass, routing_matrix)

Returns:

Self for method chaining.

Return type:

RoutingMatrix

__getitem__(index)[source]

Enable [i][j] access to routing probabilities by node index.

Parameters:

index (int) – Row index (source node index).

Returns:

RoutingMatrixRowView for second-level indexing.

Return type:

RoutingMatrixRowView

__setitem__(key, value)[source]

Set routing using indexing notation.

Parameters:
  • key – Single jobclass (or int index) or tuple of (source_class, dest_class).

  • value – 2D numpy array of routing probabilities.

addClassSwitch(source_class, dest_class, source_node, dest_node, probability)[source]

Add a class switching route between nodes.

Parameters:
  • source_class (JobClass) – The job class before the switch.

  • dest_class (JobClass) – The job class after the switch.

  • source_node (Node) – The node where the job departs from.

  • dest_node (Node) – The node where the job arrives.

  • probability (float) – The probability of taking this route.

get_cell()[source]

Return the routing probabilities as a class-by-class table of node-by-node matrices, twin of MATLAB RoutingMatrix.getCell.

The matrices are freshly built, matching the MATLAB cell array being returned by value: mutating them does not alter this routing matrix.

Returns:

the node-by-node probabilities from class r to class s, with r and s 0-based class indices.

Return type:

cell[r][s]

getCell()

Return the routing probabilities as a class-by-class table of node-by-node matrices, twin of MATLAB RoutingMatrix.getCell.

The matrices are freshly built, matching the MATLAB cell array being returned by value: mutating them does not alter this routing matrix.

Returns:

the node-by-node probabilities from class r to class s, with r and s 0-based class indices.

Return type:

cell[r][s]

static rtnodes2rtorig(sn)[source]

Recover the routing matrix as declared by the user, before the class switch nodes were materialised. Twin of the static MATLAB RoutingMatrix.rtnodes2rtorig, the same computation as sn_rtnodes_to_rtorig: this is the class-level name for it and delegates rather than duplicating it.

Note this is NOT sn.rtorig: the stochastic complement also fills the rows of a station a class never visits, which is why MATLAB refreshRoutingMatrix.m does not use it to populate that field.

Parameters:

sn – Network structure

Returns:

Tuple of (rtorigcell, rtorig), rtorigcell keyed by (r, s).

Return type:

Tuple[Dict[Tuple[int, int], ndarray], ndarray]

toMatrix()[source]

Convert the routing to a dense matrix.

Non-station nodes (like Router, ClassSwitch) are absorbed by computing transitive routing through them.

Returns:

(M*K) x (M*K) routing matrix indexed by station then class.

Return type:

ndarray

remove_job_class(jobclass)[source]

Drop every route that references a job class.

Routes are keyed by (source class, destination class), so a class removed from the model must be purged from both positions, including the pre-ClassSwitch snapshot in _original_routes. Leaving a stale entry makes toMatrix() index the rebuilt (smaller) rt matrix with the old class index. Called by Network.remove_class.

Parameters:

jobclass (JobClass) – the job class being removed from the model

removeJobClass(jobclass)

Drop every route that references a job class.

Routes are keyed by (source class, destination class), so a class removed from the model must be purged from both positions, including the pre-ClassSwitch snapshot in _original_routes. Leaving a stale entry makes toMatrix() index the rebuilt (smaller) rt matrix with the old class index. Called by Network.remove_class.

Parameters:

jobclass (JobClass) – the job class being removed from the model

set_routing_matrix(jobclass, nodes, pmatrix)

Set routing probabilities using a matrix for specific job class(es).

Parameters:
  • jobclass (JobClass | List[JobClass]) – Job class or list of job classes.

  • nodes (List[Node]) – List of nodes in the routing matrix.

  • pmatrix (ndarray) – 2D or 3D matrix of routing probabilities.

add_route(jobclass, *args)

Add a routing path through multiple nodes for a job class.

Parameters:
  • jobclass (JobClass) – Job class to configure routing for.

  • *args – Nodes followed by optional probability. If last arg is a number, it’s used as probability.

add_class_switch(source_class, dest_class, source_node, dest_node, probability)

Add a class switching route between nodes.

Parameters:
  • source_class (JobClass) – The job class before the switch.

  • dest_class (JobClass) – The job class after the switch.

  • source_node (Node) – The node where the job departs from.

  • dest_node (Node) – The node where the job arrives.

  • probability (float) – The probability of taking this route.

The NetworkStruct data structure produced by Network.getStruct() is documented with the rest of the stochastic network utilities in Stochastic Network Utilities.

State Representation

class State(network=None)[source]

Bases: object

State representation for stochastic network models.

Represents the system state including job populations at each node, phase information for multi-phase processes, and other state variables.

Initialize a state for the network.

Parameters:

network (ForwardRef('Network') | None) – The network this state belongs to.

__init__(network=None)[source]

Initialize a state for the network.

Parameters:

network (ForwardRef('Network') | None) – The network this state belongs to.

get(stateful_idx)[source]

Get state for a stateful node.

set(stateful_idx, state)[source]

Set state for a stateful node.

toArray()[source]

Convert to a flat array representation.

static fromMarg(model, node_idx, ntot)[source]

Generate the state space with a given TOTAL queue length at a node.

Class-summed counterpart of fromMarginal: it fixes only how many jobs the node holds ALTOGETHER, and returns the union of fromMarginal over every class split of ntot the node can hold. Classes disabled at the station are excluded from the split enumeration through classcap.

Parameters:
  • model (Network) – Network model

  • node_idx (int) – Node index (0-based)

  • ntot (int) – Total number of jobs at the node, all classes summed

Returns:

State space matrix with the requested total.

Return type:

ndarray

References

MATLAB: matlab/src/lang/+State/fromMarg.m

static fromMargAndStarted(model, node_idx, ntot, stot)[source]

Generate the states with a given TOTAL queue length and a given TOTAL number of started jobs.

Returns the union of fromMarginalAndStarted over every (n,s) pair with sum(n)=ntot, sum(s)=stot and s <= n elementwise.

Parameters:
  • model (Network) – Network model

  • node_idx (int) – Node index (0-based)

  • ntot (int) – Total number of jobs at the node, all classes summed

  • stot (int) – Total number of jobs that have started service

Returns:

State space matrix with the requested totals.

Return type:

ndarray

References

MATLAB: matlab/src/lang/+State/fromMargAndStarted.m

static fromMarginalBounds(model, node_idx, lb, ub, cap=None)[source]

Generate all valid states whose per-class marginal lies between lb and ub (inclusive), subject to the node capacity cap.

Parameters:
  • model (Network) – Network model (or NetworkStruct).

  • node_idx (int) – Node index (0-based).

  • lb – Lower bound on the number of resident jobs (scalar total or per-class vector). None is treated as all-zero.

  • ub – Upper bound on the number of resident jobs (scalar total or per-class vector).

  • cap – Total capacity at the node (defaults to the station’s total capacity, or infinity if unbounded).

Returns:

State space matrix where each row is a valid state.

Return type:

ndarray

References

MATLAB: matlab/src/lang/+State/fromMarginalBounds.m

static from_marginal(model, node_idx, n)

Generate state space with specific marginal job counts at a node.

Creates all possible network states where the specified node has exactly n[r] jobs of class r.

Parameters:
  • model (Network) – Network model

  • node_idx (int) – Node index (0-based)

  • n (List[int] | ndarray) – Vector of job counts per class

Returns:

State space matrix where each row is a valid state.

Return type:

ndarray

static from_marginal_and_running(model, node_idx, n, s)

Generate state space with specific marginal and running job counts.

Creates states where node has n[r] jobs of class r total, with s[r] jobs of class r currently in service (running).

Parameters:
  • model (Network) – Network model

  • node_idx (int) – Node index (0-based)

  • n (List[int] | ndarray) – Vector of total job counts per class

  • s (List[int] | ndarray) – Vector of running job counts per class

Returns:

State space matrix where each row is a valid state.

Return type:

ndarray

static from_marginal_and_started(model, node_idx, n, s)

Generate state space with specific marginal and started job counts.

Creates states where node has n[r] jobs of class r total, with s[r] jobs of class r that have started service.

Parameters:
  • model (Network) – Network model

  • node_idx (int) – Node index (0-based)

  • n (List[int] | ndarray) – Vector of total job counts per class

  • s (List[int] | ndarray) – Vector of started job counts per class

Returns:

State space matrix where each row is a valid state.

Return type:

ndarray

static from_marg(model, node_idx, ntot)

Generate the state space with a given TOTAL queue length at a node.

Class-summed counterpart of fromMarginal: it fixes only how many jobs the node holds ALTOGETHER, and returns the union of fromMarginal over every class split of ntot the node can hold. Classes disabled at the station are excluded from the split enumeration through classcap.

Parameters:
  • model (Network) – Network model

  • node_idx (int) – Node index (0-based)

  • ntot (int) – Total number of jobs at the node, all classes summed

Returns:

State space matrix with the requested total.

Return type:

ndarray

References

MATLAB: matlab/src/lang/+State/fromMarg.m

static from_marg_and_started(model, node_idx, ntot, stot)

Generate the states with a given TOTAL queue length and a given TOTAL number of started jobs.

Returns the union of fromMarginalAndStarted over every (n,s) pair with sum(n)=ntot, sum(s)=stot and s <= n elementwise.

Parameters:
  • model (Network) – Network model

  • node_idx (int) – Node index (0-based)

  • ntot (int) – Total number of jobs at the node, all classes summed

  • stot (int) – Total number of jobs that have started service

Returns:

State space matrix with the requested totals.

Return type:

ndarray

References

MATLAB: matlab/src/lang/+State/fromMargAndStarted.m

static to_marginal(model, node_idx, state_i)

Extract marginal job statistics from a state for a specific node.

Parameters:
  • model (Network) – Network model (or NetworkStruct).

  • node_idx (int) – Node index (0-based).

  • state_i (ndarray) – State vector/matrix for the node.

Returns:

  • ni: total jobs in the node,

  • nir: total jobs per class,

  • sir: jobs in service per class,

  • kir: jobs in service per class and phase.

Return type:

Tuple (ni, nir, sir, kir)

References

MATLAB: matlab/src/lang/+State/toMarginal.m

static from_marginal_bounds(model, node_idx, lb, ub, cap=None)

Generate all valid states whose per-class marginal lies between lb and ub (inclusive), subject to the node capacity cap.

Parameters:
  • model (Network) – Network model (or NetworkStruct).

  • node_idx (int) – Node index (0-based).

  • lb – Lower bound on the number of resident jobs (scalar total or per-class vector). None is treated as all-zero.

  • ub – Upper bound on the number of resident jobs (scalar total or per-class vector).

  • cap – Total capacity at the node (defaults to the station’s total capacity, or infinity if unbounded).

Returns:

State space matrix where each row is a valid state.

Return type:

ndarray

References

MATLAB: matlab/src/lang/+State/fromMarginalBounds.m

static is_valid(model, n, s=None, options=None)

Validate a network state against capacity and scheduling constraints.

Parameters:
  • model (Network) – Network model (or NetworkStruct).

  • n (ndarray) – (nstations x nclasses) matrix of resident jobs per class.

  • s (ndarray) – (nstations x nclasses) matrix of running jobs per class (optional).

  • options – Unused; accepted for API compatibility.

Returns:

True if the state satisfies all capacity, server, disabled-process and chain-population constraints; False otherwise.

Return type:

bool

References

MATLAB: matlab/src/lang/+State/isValid.m

class Mode(transition, name, index)[source]

Bases: object

A firing mode for a Petri net transition.

Modes define how a transition can fire, including: - Enabling conditions (required tokens from input places) - Inhibiting conditions (blocking tokens in places) - Firing outcomes (tokens produced to output places) - Timing distribution for the firing delay

Initialize a Mode.

Parameters:
  • transition (Transition) – Parent Transition object

  • name (str) – Mode name

  • index (int) – 1-based index of this mode

__init__(transition, name, index)[source]

Initialize a Mode.

Parameters:
  • transition (Transition) – Parent Transition object

  • name (str) – Mode name

  • index (int) – 1-based index of this mode

property name: str

Get mode name.

property index: int

Get 1-based index (MATLAB compatibility).

__index__()[source]

Allow Mode to be used as an index (0-based for Python arrays).

__int__()[source]

Convert to int (1-based index).

Solvers (line_solver.solvers)

The solvers module provides various analytical and simulation-based solvers.

Base Solver Classes

class Solver(options=None, *args, **kwargs)

Bases: object

Base class for all queueing network solvers.

static defaultOptions()

Get default solver options.

static default_options()

Get default solver options.

property name

Get solver name (property access).

supports(model)

Check if solver supports the model.

class NetworkSolver[source]

Bases: Solver

Base class for single-network LINE solvers.

Used by: SolverMVA, SolverNC, SolverCTMC, SolverSSA, SolverMAM, SolverJMT,

SolverLDES, SolverQNS, SolverAUTO, SolverFLD

lastPermEngine = ''
getProbSysMarg(nvec, engine='exact')[source]

Joint probability of the per-station TOTAL queue lengths.

Declared here so that a solver without the metric refuses by name rather than by AttributeError. Only SolverNC implements it, through the permanent identity of the closed product-form joint law.

property result

The analyzer result.

MATLAB’s solver.result is always the analyzer’s own return, and a caller reads result.method off it to check WHICH method answered – the one assertion that catches a silent fallback. Only FLD and MAM published it here, so every other solver answered None and that check could not be written; the private _result every solver does set is the fallback, which makes the two agree.

libraries()[source]

Third-party libraries this solver will use, without printing.

Attribution in LINE is pull-based, as in Sage: nothing is written to the console during a solve. Mirrors MATLAB @NetworkSolver/libraries.m.

Returns:

list of library names, possibly empty

citations(display=False)[source]

Bibliographic references for the algorithms this solver used.

The references follow what the run actually did: the method the analyzer resolved to (not merely the one requested), the fork-join transformation if the model has forks, and the percentile method of the last getPerctRespT call. Mirrors MATLAB @NetworkSolver/citations.m.

Parameters:

display – print the list instead of only returning it

Returns:

list of dicts with keys ‘key’ (internal bibliography key, never displayed), ‘ref’ and ‘covers’

getAvg()[source]

Average station metrics (Q, U, R, T, A, W) as station x class matrices.

Single analyzer funnel of the native solvers, mirroring MATLAB @NetworkSolver/getAvg.m and JAR NetworkSolver.getAvg(): it runs the analyzer if there is no cached result, then reads the averages from whichever result store the solver uses. Every solver used to carry its own copy of this body, differing only in that store (‘_result’ vs ‘result’) and in the field naming (‘QN’ vs ‘Q’), which _AVG_FIELDS already reconciles; the duplication also meant there was no single place to intercept a solve, as the other two codebases have.

Returns:

queue lengths, utilizations, response times, throughputs, arrival rates and residence times.

Return type:

(Q, U, R, T, A, W)

get_avg()

Average station metrics (Q, U, R, T, A, W) as station x class matrices.

Single analyzer funnel of the native solvers, mirroring MATLAB @NetworkSolver/getAvg.m and JAR NetworkSolver.getAvg(): it runs the analyzer if there is no cached result, then reads the averages from whichever result store the solver uses. Every solver used to carry its own copy of this body, differing only in that store (‘_result’ vs ‘result’) and in the field naming (‘QN’ vs ‘Q’), which _AVG_FIELDS already reconciles; the duplication also meant there was no single place to intercept a solve, as the other two codebases have.

Returns:

queue lengths, utilizations, response times, throughputs, arrival rates and residence times.

Return type:

(Q, U, R, T, A, W)

supportsTransientAnalysis()[source]

Does this solver produce transient averages, i.e. does getTranAvg return trajectories on a finite options.timespan?

Declared False here and overridden by the solvers that populate transient results (FLD, CTMC, LDES, JMT). It is a capability claim, not a state test: it must answer before any run has taken place, because mapEnvApprox uses it to decide whether the environment stages can be coupled by the mean-field analyzer (which needs getTranAvg) or only by the two steady-state limits.

supportsTransientVariance()[source]

Does this solver produce a transient COVARIANCE alongside the transient means, i.e. does getTranAvgVar return one?

Declared False here and overridden by SolverFLD, the only family that integrates a second moment along the trajectory, and there only for the methods that do (‘kp’, ‘dae’). Like supportsTransientAnalysis it is a capability claim that must answer before any run: SolverENV’s ‘meancov’ coupling reads it to decide whether a stage contributes a within-stage covariance or only the timing variance of its sojourn.

needsMapEnv(options)[source]

Should this model be solved through the random-environment image of its MAP/MMPP processes instead of natively?

True when the ONLY features the resolved method cannot consume are non-renewal processes, i.e. the model becomes supported once each modulated process is frozen into an exponential stage. A model that also uses some other unsupported feature keeps its original rejection, since the environment image would not make it solvable.

Mirrors matlab @NetworkSolver/NetworkSolver.m needsMapEnv.

mapEnvApprox(options)[source]

Solver-agnostic random-environment approximation of a network with MAP/MMPP/MMAP arrival or service processes, for solvers that cannot consume a non-renewal process natively.

map2renv turns each modulated process into a set of environment stages in which that process is exponential with the phase-conditional intensity, and SolverENV recombines the stages with the calling solver as the stage solver. Which recombination is reachable is decided by the solver’s transient capability: ‘meanfield’ carries the queue state across a phase switch but needs getTranAvg on the stage solver, while ‘dec’ (quasi-stationary limit) and ‘avg’ (rate-averaged limit) use steady state alone. options.config[‘map_env_method’] selects one; ‘auto’ takes ‘meanfield’ whenever the solver supports transient analysis and otherwise compares the mean stage holding time with the model relaxation time.

Mirrors matlab @NetworkSolver/mapEnvApprox.m.

supports_transient_analysis()

Does this solver produce transient averages, i.e. does getTranAvg return trajectories on a finite options.timespan?

Declared False here and overridden by the solvers that populate transient results (FLD, CTMC, LDES, JMT). It is a capability claim, not a state test: it must answer before any run has taken place, because mapEnvApprox uses it to decide whether the environment stages can be coupled by the mean-field analyzer (which needs getTranAvg) or only by the two steady-state limits.

needs_map_env(options)

Should this model be solved through the random-environment image of its MAP/MMPP processes instead of natively?

True when the ONLY features the resolved method cannot consume are non-renewal processes, i.e. the model becomes supported once each modulated process is frozen into an exponential stage. A model that also uses some other unsupported feature keeps its original rejection, since the environment image would not make it solvable.

Mirrors matlab @NetworkSolver/NetworkSolver.m needsMapEnv.

map_env_approx(options)

Solver-agnostic random-environment approximation of a network with MAP/MMPP/MMAP arrival or service processes, for solvers that cannot consume a non-renewal process natively.

map2renv turns each modulated process into a set of environment stages in which that process is exponential with the phase-conditional intensity, and SolverENV recombines the stages with the calling solver as the stage solver. Which recombination is reachable is decided by the solver’s transient capability: ‘meanfield’ carries the queue state across a phase switch but needs getTranAvg on the stage solver, while ‘dec’ (quasi-stationary limit) and ‘avg’ (rate-averaged limit) use steady state alone. options.config[‘map_env_method’] selects one; ‘auto’ takes ‘meanfield’ whenever the solver supports transient analysis and otherwise compares the mean stage holding time with the model relaxation time.

Mirrors matlab @NetworkSolver/mapEnvApprox.m.

avg(*args)[source]

Alias for getAvg (returns QN, UN, RN, TN, AN, WN).

avg_table()[source]

Get average performance metrics as an IndexedTable with proper formatting.

This method wraps the raw DataFrame from getAvgTable() in an IndexedTable to provide MATLAB-style number formatting (e.g., 0 instead of 0.00000).

Returns:

Wrapped DataFrame with MATLAB-style formatting.

Return type:

IndexedTable

get_avg_table()

Get average performance metrics as an IndexedTable with proper formatting.

This method wraps the raw DataFrame from getAvgTable() in an IndexedTable to provide MATLAB-style number formatting (e.g., 0 instead of 0.00000).

Returns:

Wrapped DataFrame with MATLAB-style formatting.

Return type:

IndexedTable

getAvgOrbit()[source]

Mean number of jobs waiting in the ORBIT of each retrial station, as an (nstations, nclasses) array. Stations that are not retrial queues report 0.

A retrial station has no waiting room: a job that finds every server busy joins the orbit instead of queueing, so its station population splits into the jobs currently in service and the jobs orbiting. getAvgQLen reports the whole station population, which is why the orbit had to be recovered by hand as QLen - Util. This method reports it directly.

The in-service population is obtained from the station throughput by Little’s law applied to the servers alone, E[in service] = X * E[S], which holds for any service distribution and any number of servers, so the orbit length is exact whenever QLen and Tput are.

get_avg_orbit()

Mean number of jobs waiting in the ORBIT of each retrial station, as an (nstations, nclasses) array. Stations that are not retrial queues report 0.

A retrial station has no waiting room: a job that finds every server busy joins the orbit instead of queueing, so its station population splits into the jobs currently in service and the jobs orbiting. getAvgQLen reports the whole station population, which is why the orbit had to be recovered by hand as QLen - Util. This method reports it directly.

The in-service population is obtained from the station throughput by Little’s law applied to the servers alone, E[in service] = X * E[S], which holds for any service distribution and any number of servers, so the orbit length is exact whenever QLen and Tput are.

getAvgOrbitTable()[source]

Table of the mean orbit length of every retrial station-class pair, with the station population and the in-service population it decomposes into.

Reported as a separate table rather than as an extra column of getAvgTable so that the average table keeps its shape for models without retrials.

get_avg_orbit_table()

Table of the mean orbit length of every retrial station-class pair, with the station population and the in-service population it decomposes into.

Reported as a separate table rather than as an extra column of getAvgTable so that the average table keeps its shape for models without retrials.

getAvgLossTable()[source]

Table of loss (drop) metrics for every station-class pair that receives offered traffic: offered arrival rate (ArvR), carried throughput (Tput), loss rate (ArvR - Tput, the rate of jobs dropped by finite capacity, blocking, or reneging) and loss ratio (LossRate / ArvR).

Only pairs with ArvR > 0 are listed, which excludes the Source (whose offered arrival rate is zero); a lossless station has ArvR = Tput and so LossRate = LossRatio = 0.

get_avg_loss_table()

Table of loss (drop) metrics for every station-class pair that receives offered traffic: offered arrival rate (ArvR), carried throughput (Tput), loss rate (ArvR - Tput, the rate of jobs dropped by finite capacity, blocking, or reneging) and loss ratio (LossRate / ArvR).

Only pairs with ArvR > 0 are listed, which excludes the Source (whose offered arrival rate is zero); a lossless station has ArvR = Tput and so LossRate = LossRatio = 0.

supportsExactSensitivity()[source]

True when the solver evaluates a product-form recursion that getSensitivityTable can differentiate analytically. False here, so that a solver reaching this base implementation obtains its sensitivities by finite differences on its own predictions. Overridden by SolverMVA and SolverNC.

supports_exact_sensitivity()

True when the solver evaluates a product-form recursion that getSensitivityTable can differentiate analytically. False here, so that a solver reaching this base implementation obtains its sensitivities by finite differences on its own predictions. Overridden by SolverMVA and SolverNC.

getSensitivityTable(method='auto', step=None, scheme='forward')[source]

Performance sensitivities with respect to service rates.

Returns a DataFrame with one row per (Station, JobClass) giving the derivative of that row’s mean performance measures with respect to that station-class service RATE: dTput_dRate, dRespT_dRate, dQLen_dRate, dUtil_dRate.

Two branches produce the derivatives, selected automatically:

‘exact’ Analytic differentiation of a product-form recursion, exact to

machine precision and cheaper than a single extra solve. Closed networks use pfqn_sens (differentiated MVA), open networks the closed-form BCMP sensitivities (their stations decouple). Rate derivatives follow the chain rule d(.)/d(rate) = -(L/rate) d(.)/dL, since L(i,r) = visits(i,r)/rate(i,r). Available only on the solvers that evaluate that recursion, SolverMVA and SolverNC, and only for single-server queues plus an optional delay, with mixed (open+closed) models excluded.

‘fd’ Forward or central finite differences on the CALLING solver’s

own predictions: the service process at (station,class) is rate-scaled by (1+h), the same solver with the same options is re-run, and the difference quotient is formed. This costs 1+M*R solves (forward) or 2*M*R (central), and it is the only branch that applies to non-product-form models, so it is what every solver other than SolverMVA and SolverNC uses.

Parameters:
  • method – ‘auto’ (default: exact where available and in scope, finite differences otherwise), ‘exact’ or ‘fd’.

  • step – relative step of the rate perturbation, default 1e-4 for deterministic solvers and 1e-2 for the simulators, whose Monte Carlo error would otherwise dominate the difference quotient.

  • scheme – ‘forward’ (default) or ‘central’.

The branch actually taken is reported in .attrs['method'].

Simulation solvers must be run with common random numbers for the difference quotient to be meaningful: the same options, and hence the same seed, are reused for the base and the perturbed runs. An unset seed is pinned before the sweep so that the runs remain paired.

get_sensitivity_table(method='auto', step=None, scheme='forward')

Performance sensitivities with respect to service rates.

Returns a DataFrame with one row per (Station, JobClass) giving the derivative of that row’s mean performance measures with respect to that station-class service RATE: dTput_dRate, dRespT_dRate, dQLen_dRate, dUtil_dRate.

Two branches produce the derivatives, selected automatically:

‘exact’ Analytic differentiation of a product-form recursion, exact to

machine precision and cheaper than a single extra solve. Closed networks use pfqn_sens (differentiated MVA), open networks the closed-form BCMP sensitivities (their stations decouple). Rate derivatives follow the chain rule d(.)/d(rate) = -(L/rate) d(.)/dL, since L(i,r) = visits(i,r)/rate(i,r). Available only on the solvers that evaluate that recursion, SolverMVA and SolverNC, and only for single-server queues plus an optional delay, with mixed (open+closed) models excluded.

‘fd’ Forward or central finite differences on the CALLING solver’s

own predictions: the service process at (station,class) is rate-scaled by (1+h), the same solver with the same options is re-run, and the difference quotient is formed. This costs 1+M*R solves (forward) or 2*M*R (central), and it is the only branch that applies to non-product-form models, so it is what every solver other than SolverMVA and SolverNC uses.

Parameters:
  • method – ‘auto’ (default: exact where available and in scope, finite differences otherwise), ‘exact’ or ‘fd’.

  • step – relative step of the rate perturbation, default 1e-4 for deterministic solvers and 1e-2 for the simulators, whose Monte Carlo error would otherwise dominate the difference quotient.

  • scheme – ‘forward’ (default) or ‘central’.

The branch actually taken is reported in .attrs['method'].

Simulation solvers must be run with common random numbers for the difference quotient to be meaningful: the same options, and hence the same seed, are reused for the base and the perturbed runs. An unset seed is pinned before the sweep so that the runs remain paired.

getMomentTable(order=None, method=None)[source]

Exact higher moments of the per-class performance measures.

Returns (MomentTable, mom) where MomentTable is a DataFrame with one row per (Station, JobClass) giving, in addition to the means that getAvgTable reports, the second moments of that row’s queue length and response time: QLen, QLenVar, QLenSCV, RespT, RespTVar, RespTSCV.

order selects which moment orders to report. It is a SET: a scalar k is read as 1:k, “everything up to order k”; an explicit list/vector selects exactly those orders:

1        the means only:            QLen, RespT
2        (default) means and second moments, i.e. the columns above
3        also adds RespTSkew
[1, 2]   the same as 2
[2, 3]   second moments and skewness, without the means

Order 1 contributes QLen and RespT, order 2 contributes the Var and SCV columns, order 3 contributes RespTSkew.

order = 3 also adds QLenSkew, the skewness of the per-class queue length. That quantity is reachable because the generating parameter need not scale a whole demand column: scaling L(i,r) alone is Theorem 1 of Akyildiz and Strelen with the class subset T = {r}, and it generates the moments of n(i,r) itself. QLenSkew is available only for closed single-server models, which is the scope of pfqn_sens_mom; it is NaN otherwise.

All of it is exact, not simulated and not approximated, EXCEPT on the momlin branch. The queue-length moments come from the product-form identity Cov[n(i,r),n(j,s)] = L(j,s) dQ(i,r)/dL(j,s), evaluated by the pfqn_sens_* family; see _kb/03-api-layer.md.

method selects how the queue-length moments are obtained on a closed single-server model:

''        (default) exact, unless the population lattice prod(N+1)
          exceeds 1e6 points, in which case momlin is used and a
          warning is raised
'exact'   always the pfqn_sens_* recursion, however large the lattice
'momlin'  always pfqn_momlin: the same covariance identity, with the
          derivatives taken by linearizing the Schweitzer-Bard fixed
          point. Cost is polynomial rather than exponential in the
          number of classes, and BOTH moments then carry the AMVA
          error. ``mom['qlen'].method`` is 'momlin' on this branch,
          which also fills ``QCovFull``, the cross-station covariance
          tensor the exact branch does not return.

method is ignored on multiserver, mixed and purely open models, which have no momlin path.

RESPONSE-TIME MOMENTS ARE FCFS OR PROCESSOR-SHARING. RespTVar and RespTSCV are NaN at any station that is neither, and at an LCFS center in particular, because the sojourn-time distribution there is not known in general (Strelen 1990, Section 4) and a wrong value is worse than a blank. The mean RespT is always reported, since it needs no distributional result.

The FCFS moments come from pfqn_sens_respt and are closed-model only. The processor-sharing moments come from Mitra and Morrison (1983) and cover two configurations, both requiring exponential single-server service: purely open, where qsys_mm1_ps is exact at any PS station whose arrivals are Poisson, that is, that lies on no routing cycle; and purely closed, where pfqn_respt_ps_moments covers the terminal-driven system the paper analyses, one PS station visited once per think cycle with delay stations holding the think time. A PS station outside those configurations keeps RespTVar = NaN.

Scope by model type:

closed, single-server            -> pfqn_sens_mva
closed, multiserver              -> pfqn_sens_mvaldmx
mixed open and closed            -> pfqn_sens_mvaldmx
purely open, single-server       -> exact BCMP closed form (below)
purely open, multiserver         -> not supported, see the error

mom is a dict carrying the raw results: mom['qlen'] is the underlying pfqn_sens_mva / pfqn_sens_mvaldmx object (with the full covariance matrices, not just the diagonal this table shows), mom['respt'] is the pfqn_sens_respt object or None, and mom['psrespt'] is the pfqn_respt_ps_moments object or None. Use it when the per-pair covariances, or the route taken at a PS station, are needed.

Per-station TOTAL moments, including the third moment and the skewness, are in getMomentStationTable: they are only defined for a station total, because the parameter that generates them scales a whole demand column.

See also: getAvgTable, getSensitivityTable, getMomentStationTable.

get_moment_table(order=None, method=None)

Exact higher moments of the per-class performance measures.

Returns (MomentTable, mom) where MomentTable is a DataFrame with one row per (Station, JobClass) giving, in addition to the means that getAvgTable reports, the second moments of that row’s queue length and response time: QLen, QLenVar, QLenSCV, RespT, RespTVar, RespTSCV.

order selects which moment orders to report. It is a SET: a scalar k is read as 1:k, “everything up to order k”; an explicit list/vector selects exactly those orders:

1        the means only:            QLen, RespT
2        (default) means and second moments, i.e. the columns above
3        also adds RespTSkew
[1, 2]   the same as 2
[2, 3]   second moments and skewness, without the means

Order 1 contributes QLen and RespT, order 2 contributes the Var and SCV columns, order 3 contributes RespTSkew.

order = 3 also adds QLenSkew, the skewness of the per-class queue length. That quantity is reachable because the generating parameter need not scale a whole demand column: scaling L(i,r) alone is Theorem 1 of Akyildiz and Strelen with the class subset T = {r}, and it generates the moments of n(i,r) itself. QLenSkew is available only for closed single-server models, which is the scope of pfqn_sens_mom; it is NaN otherwise.

All of it is exact, not simulated and not approximated, EXCEPT on the momlin branch. The queue-length moments come from the product-form identity Cov[n(i,r),n(j,s)] = L(j,s) dQ(i,r)/dL(j,s), evaluated by the pfqn_sens_* family; see _kb/03-api-layer.md.

method selects how the queue-length moments are obtained on a closed single-server model:

''        (default) exact, unless the population lattice prod(N+1)
          exceeds 1e6 points, in which case momlin is used and a
          warning is raised
'exact'   always the pfqn_sens_* recursion, however large the lattice
'momlin'  always pfqn_momlin: the same covariance identity, with the
          derivatives taken by linearizing the Schweitzer-Bard fixed
          point. Cost is polynomial rather than exponential in the
          number of classes, and BOTH moments then carry the AMVA
          error. ``mom['qlen'].method`` is 'momlin' on this branch,
          which also fills ``QCovFull``, the cross-station covariance
          tensor the exact branch does not return.

method is ignored on multiserver, mixed and purely open models, which have no momlin path.

RESPONSE-TIME MOMENTS ARE FCFS OR PROCESSOR-SHARING. RespTVar and RespTSCV are NaN at any station that is neither, and at an LCFS center in particular, because the sojourn-time distribution there is not known in general (Strelen 1990, Section 4) and a wrong value is worse than a blank. The mean RespT is always reported, since it needs no distributional result.

The FCFS moments come from pfqn_sens_respt and are closed-model only. The processor-sharing moments come from Mitra and Morrison (1983) and cover two configurations, both requiring exponential single-server service: purely open, where qsys_mm1_ps is exact at any PS station whose arrivals are Poisson, that is, that lies on no routing cycle; and purely closed, where pfqn_respt_ps_moments covers the terminal-driven system the paper analyses, one PS station visited once per think cycle with delay stations holding the think time. A PS station outside those configurations keeps RespTVar = NaN.

Scope by model type:

closed, single-server            -> pfqn_sens_mva
closed, multiserver              -> pfqn_sens_mvaldmx
mixed open and closed            -> pfqn_sens_mvaldmx
purely open, single-server       -> exact BCMP closed form (below)
purely open, multiserver         -> not supported, see the error

mom is a dict carrying the raw results: mom['qlen'] is the underlying pfqn_sens_mva / pfqn_sens_mvaldmx object (with the full covariance matrices, not just the diagonal this table shows), mom['respt'] is the pfqn_sens_respt object or None, and mom['psrespt'] is the pfqn_respt_ps_moments object or None. Use it when the per-pair covariances, or the route taken at a PS station, are needed.

Per-station TOTAL moments, including the third moment and the skewness, are in getMomentStationTable: they are only defined for a station total, because the parameter that generates them scales a whole demand column.

See also: getAvgTable, getSensitivityTable, getMomentStationTable.

getMomentStationTable(order=None)[source]

Exact higher moments of the total queue length.

Returns (MomentStationTable, mom) where MomentStationTable is a DataFrame with one row per Station giving the moments of the TOTAL queue length at that station, Q_i = sum_r n(i,r): QLen, QLenVar, QLenSCV.

order selects which moment orders to report. It is a SET: a scalar k is read as 1:k, “everything up to order k”; an explicit list/vector selects exactly those orders:

1        the mean only:             QLen
2        (default) mean and second moment: QLen, QLenVar, QLenSCV
3        also adds QLenM3 and QLenSkew
[1, 3]   the mean and the third moment, without the variance

Order 1 contributes QLen, order 2 contributes QLenVar and QLenSCV, order 3 contributes QLenM3 and QLenSkew.

Why this is a separate table from getMomentTable. The moments beyond the second are generated by differentiating with respect to x_i, the reciprocal of the capacity of station i, which scales the service times of ALL classes at that station at once. That parameter therefore produces moments of the station total, not of any one class: there is no per-class third moment to report, and inventing one by splitting the total would be fiction. The per-class second moments, which do exist, are in getMomentTable. The two are consistent: Var[Q_i] here equals the sum of getMomentTable’s per-class covariances at station i over all class pairs.

The ALGORITHM is chosen by the solver’s method, set at construction, not by an argument here: a method is a property of the solver object, so passing one per call would let a single solver answer with two different algorithms:

SolverMVA(model)                 -> pfqn_sens_mom, exact, but it
                                    walks the whole population
                                    lattice at a cost of prod(N+1),
                                    so it is unusable once the
                                    populations are large.
SolverMVA(model, method='lin')   -> pfqn_sens_linearizer,
                                    approximate and polynomial-time.
                                    The reference reports relative
                                    errors below 2.1% on E[Q], 4.1%
                                    on E[Q^2] and 6.2% on E[Q^3].

Any Linearizer-family method (‘lin’, ‘amva.lin’, ‘egflin’, ‘gflin’) takes the approximate path; every other method takes the exact one.

Restricted to closed models. Mixed and open second moments are available per class from getMomentTable; the higher moments of the reference are stated for closed networks only.

mom is the underlying pfqn_sens_mom / pfqn_sens_linearizer object, which also carries the cross-station covariance matrix .Cov that this table does not show.

Reference: J. C. Strelen, “Moment Analysis for Closed Queuing Networks and its Linearizer”, Performance Evaluation 11:127-142, 1990, Theorem 3.1 and equation (3.2).

See also: getMomentTable, getAvgTable, getSensitivityTable.

get_moment_station_table(order=None)

Exact higher moments of the total queue length.

Returns (MomentStationTable, mom) where MomentStationTable is a DataFrame with one row per Station giving the moments of the TOTAL queue length at that station, Q_i = sum_r n(i,r): QLen, QLenVar, QLenSCV.

order selects which moment orders to report. It is a SET: a scalar k is read as 1:k, “everything up to order k”; an explicit list/vector selects exactly those orders:

1        the mean only:             QLen
2        (default) mean and second moment: QLen, QLenVar, QLenSCV
3        also adds QLenM3 and QLenSkew
[1, 3]   the mean and the third moment, without the variance

Order 1 contributes QLen, order 2 contributes QLenVar and QLenSCV, order 3 contributes QLenM3 and QLenSkew.

Why this is a separate table from getMomentTable. The moments beyond the second are generated by differentiating with respect to x_i, the reciprocal of the capacity of station i, which scales the service times of ALL classes at that station at once. That parameter therefore produces moments of the station total, not of any one class: there is no per-class third moment to report, and inventing one by splitting the total would be fiction. The per-class second moments, which do exist, are in getMomentTable. The two are consistent: Var[Q_i] here equals the sum of getMomentTable’s per-class covariances at station i over all class pairs.

The ALGORITHM is chosen by the solver’s method, set at construction, not by an argument here: a method is a property of the solver object, so passing one per call would let a single solver answer with two different algorithms:

SolverMVA(model)                 -> pfqn_sens_mom, exact, but it
                                    walks the whole population
                                    lattice at a cost of prod(N+1),
                                    so it is unusable once the
                                    populations are large.
SolverMVA(model, method='lin')   -> pfqn_sens_linearizer,
                                    approximate and polynomial-time.
                                    The reference reports relative
                                    errors below 2.1% on E[Q], 4.1%
                                    on E[Q^2] and 6.2% on E[Q^3].

Any Linearizer-family method (‘lin’, ‘amva.lin’, ‘egflin’, ‘gflin’) takes the approximate path; every other method takes the exact one.

Restricted to closed models. Mixed and open second moments are available per class from getMomentTable; the higher moments of the reference are stated for closed networks only.

mom is the underlying pfqn_sens_mom / pfqn_sens_linearizer object, which also carries the cross-station covariance matrix .Cov that this table does not show.

Reference: J. C. Strelen, “Moment Analysis for Closed Queuing Networks and its Linearizer”, Performance Evaluation 11:127-142, 1990, Theorem 3.1 and equation (3.2).

See also: getMomentTable, getAvgTable, getSensitivityTable.

solveMeansForStruct(sn)[source]

Mean queue lengths of a perturbed structure, under this solver’s own method. Mirrors MATLAB’s @NetworkSolver/solveMeansForStruct.

This is the mean-value oracle behind the numerical-derivative path of getMomentChainTable and getMomentStationTable. The moment identity Cov[n,n] = L dQ/dL does not care HOW the mean queue lengths were obtained, only that they are the means of a product-form model as a function of its demands. So rather than hand-differentiating each algorithm, the solver is re-run on a perturbed structure and differentiated numerically.

Running THIS solver, rather than one chosen algorithm, is what makes the path general: it covers every method of every solver, including the normalizing-constant methods of SolverNC (comom, ca, le, mom, …) and the summation methods of SolverMVA (sum, esum), none of which any hand-differentiated implementation reaches. Restricting the oracle to one analyzer would restrict the moments to that analyzer’s methods for no mathematical reason.

The perturbed sn is injected by copying the model and overwriting its cached structure, then constructing a fresh solver of this class with this solver’s options. Every step is load-bearing:

  • model.copy() because Python objects are by-reference and numpy arrays alias, so writing _sn on the caller’s model would corrupt every later solve off it;

  • _has_struct = True (MATLAB’s hasStruct) so get_struct() does not regenerate the struct and discard the perturbation;

  • _rates_dirty = False because a solver’s _extract_network_params calls model.refresh_rates() when the flag is set, which would recompute rates from the model’s distributions and discard the perturbation just as surely. Network.copy() already leaves it False; it is pinned here because the failure mode is SILENT – the means come back unperturbed and every finite difference is exactly zero;

  • a FRESH solver because a solver caches its results (and its demands) at construction and would otherwise return the unperturbed answer.

Returns the (nstations x nclasses) mean queue lengths.

solve_means_for_struct(sn)

Mean queue lengths of a perturbed structure, under this solver’s own method. Mirrors MATLAB’s @NetworkSolver/solveMeansForStruct.

This is the mean-value oracle behind the numerical-derivative path of getMomentChainTable and getMomentStationTable. The moment identity Cov[n,n] = L dQ/dL does not care HOW the mean queue lengths were obtained, only that they are the means of a product-form model as a function of its demands. So rather than hand-differentiating each algorithm, the solver is re-run on a perturbed structure and differentiated numerically.

Running THIS solver, rather than one chosen algorithm, is what makes the path general: it covers every method of every solver, including the normalizing-constant methods of SolverNC (comom, ca, le, mom, …) and the summation methods of SolverMVA (sum, esum), none of which any hand-differentiated implementation reaches. Restricting the oracle to one analyzer would restrict the moments to that analyzer’s methods for no mathematical reason.

The perturbed sn is injected by copying the model and overwriting its cached structure, then constructing a fresh solver of this class with this solver’s options. Every step is load-bearing:

  • model.copy() because Python objects are by-reference and numpy arrays alias, so writing _sn on the caller’s model would corrupt every later solve off it;

  • _has_struct = True (MATLAB’s hasStruct) so get_struct() does not regenerate the struct and discard the perturbation;

  • _rates_dirty = False because a solver’s _extract_network_params calls model.refresh_rates() when the flag is set, which would recompute rates from the model’s distributions and discard the perturbation just as surely. Network.copy() already leaves it False; it is pinned here because the failure mode is SILENT – the means come back unperturbed and every finite difference is exactly zero;

  • a FRESH solver because a solver caches its results (and its demands) at construction and would otherwise return the unperturbed answer.

Returns the (nstations x nclasses) mean queue lengths.

getMomentChainTable(order=None)[source]

Exact higher moments of the per-chain queue length.

Returns (MomentChainTable, mom) where MomentChainTable is a DataFrame with one row per (Station, Chain) giving the moments of the queue length of that chain at that station, Q_(i,c) = sum_(r in chain c) n(i,r): QLen, QLenVar, QLenSCV.

This is the chain-level analogue of getAvgChainTable, and it sits between the two other moment tables: getMomentTable is per class, getMomentStationTable is per station total, and this one is per chain, i.e. per group of classes that circulate together.

order selects which moment orders to report. It is a SET: a scalar k is read as 1:k, “everything up to order k”; an explicit list/vector selects exactly those orders:

1        the mean only:             QLen
2        (default) mean and second moment: QLen, QLenVar, QLenSCV
3        also adds QLenM3 and QLenSkew

Unlike the per-class table, order 3 IS available here. All three tables are the same recursion under different groupings of the classes: the generating parameter scales the service times of a class subset T at a station, and the moments it produces are those of sum_(r in T) n(i,r). T = {r} gives getMomentTable, T = chain gives this table, T = all classes gives getMomentStationTable. That is Theorem 1 of Akyildiz and Strelen; Strelen’s own x_i is the last case.

The ALGORITHM is chosen by the solver’s method, set at construction, not by an argument here; see getMomentStationTable. A Linearizer-family method approximates the per-station totals only, so it cannot express a per-chain grouping and is rejected here unless every class already sits in one chain, in which case the chain IS the station total.

Restricted to closed, single-server models, which is the scope of pfqn_sens_mom.

mom is the underlying pfqn_sens_mom struct. Its .Cov is (M x C x M x C) and carries the cross-chain and cross-station covariances this table does not show.

Reference: I. F. Akyildiz and J. C. Strelen, “Moment Analysis for Load-Dependent Mixed Product Form Queueing Networks”, IEEE Trans. Communications 39(6):828-832, 1991, Theorem 1; J. C. Strelen, “Moment Analysis for Closed Queuing Networks and its Linearizer”, Performance Evaluation 11:127-142, 1990, equation (3.2).

See also: getMomentTable, getMomentStationTable, getAvgChainTable.

get_moment_chain_table(order=None)

Exact higher moments of the per-chain queue length.

Returns (MomentChainTable, mom) where MomentChainTable is a DataFrame with one row per (Station, Chain) giving the moments of the queue length of that chain at that station, Q_(i,c) = sum_(r in chain c) n(i,r): QLen, QLenVar, QLenSCV.

This is the chain-level analogue of getAvgChainTable, and it sits between the two other moment tables: getMomentTable is per class, getMomentStationTable is per station total, and this one is per chain, i.e. per group of classes that circulate together.

order selects which moment orders to report. It is a SET: a scalar k is read as 1:k, “everything up to order k”; an explicit list/vector selects exactly those orders:

1        the mean only:             QLen
2        (default) mean and second moment: QLen, QLenVar, QLenSCV
3        also adds QLenM3 and QLenSkew

Unlike the per-class table, order 3 IS available here. All three tables are the same recursion under different groupings of the classes: the generating parameter scales the service times of a class subset T at a station, and the moments it produces are those of sum_(r in T) n(i,r). T = {r} gives getMomentTable, T = chain gives this table, T = all classes gives getMomentStationTable. That is Theorem 1 of Akyildiz and Strelen; Strelen’s own x_i is the last case.

The ALGORITHM is chosen by the solver’s method, set at construction, not by an argument here; see getMomentStationTable. A Linearizer-family method approximates the per-station totals only, so it cannot express a per-chain grouping and is rejected here unless every class already sits in one chain, in which case the chain IS the station total.

Restricted to closed, single-server models, which is the scope of pfqn_sens_mom.

mom is the underlying pfqn_sens_mom struct. Its .Cov is (M x C x M x C) and carries the cross-chain and cross-station covariances this table does not show.

Reference: I. F. Akyildiz and J. C. Strelen, “Moment Analysis for Load-Dependent Mixed Product Form Queueing Networks”, IEEE Trans. Communications 39(6):828-832, 1991, Theorem 1; J. C. Strelen, “Moment Analysis for Closed Queuing Networks and its Linearizer”, Performance Evaluation 11:127-142, 1990, equation (3.2).

See also: getMomentTable, getMomentStationTable, getAvgChainTable.

getAvgSysTable()[source]

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

get_avg_sys_table()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avgT()[source]

Short alias for getAvgTable.

avgSysT()[source]

Short alias for getAvgSysTable.

avgNodeT()[source]

Short alias for getAvgNodeTable.

avgChainT()[source]

Short alias for getAvgChainTable.

avgNodeChainT()[source]

Short alias for getAvgNodeChainTable.

momentT(*args, **kwargs)[source]

Short alias for getMomentTable.

momentChainT(*args, **kwargs)[source]

Short alias for getMomentChainTable.

momentStationT(*args, **kwargs)[source]

Short alias for getMomentStationTable.

sensitivityT(*args, **kwargs)[source]

Short alias for getSensitivityTable.

cacheAvgT(*args, **kwargs)[source]

Short alias for getAvgCacheTable.

itemAvgT(*args, **kwargs)[source]

Short alias for getAvgItemTable.

orbitAvgT(*args, **kwargs)[source]

Short alias for getAvgOrbitTable.

lossAvgT(*args, **kwargs)[source]

Short alias for getAvgLossTable.

getAvgRegionLossTable()[source]

Table of loss (drop) metrics per finite-capacity region and class, for regions that drop jobs (DROP rule). Each row reports the offered arrival rate (carried Tput plus drop rate), the carried throughput, the loss rate (region drop rate) and the loss ratio (LossRate / ArvR).

Only regions with offered traffic are listed; empty for solvers that do not track region drops (only the LDES simulation populates the FCR drop rate DropRateNfcr).

get_avg_region_loss_table()

Table of loss (drop) metrics per finite-capacity region and class, for regions that drop jobs (DROP rule). Each row reports the offered arrival rate (carried Tput plus drop rate), the carried throughput, the loss rate (region drop rate) and the loss ratio (LossRate / ArvR).

Only regions with offered traffic are listed; empty for solvers that do not track region drops (only the LDES simulation populates the FCR drop rate DropRateNfcr).

regionLossAvgT(*args, **kwargs)[source]

Short alias for getAvgRegionLossTable.

mT(*args, **kwargs)

Short alias for getMomentTable.

getMomentT(*args, **kwargs)

Short alias for getMomentTable.

mCT(*args, **kwargs)

Short alias for getMomentChainTable.

getMomentChainT(*args, **kwargs)

Short alias for getMomentChainTable.

mST(*args, **kwargs)

Short alias for getMomentStationTable.

getMomentStationT(*args, **kwargs)

Short alias for getMomentStationTable.

sT(*args, **kwargs)

Short alias for getSensitivityTable.

getSensitivityT(*args, **kwargs)

Short alias for getSensitivityTable.

aCaT(*args, **kwargs)

Short alias for getAvgCacheTable.

getAvgCacheT(*args, **kwargs)

Short alias for getAvgCacheTable.

aIT(*args, **kwargs)

Short alias for getAvgItemTable.

getAvgItemT(*args, **kwargs)

Short alias for getAvgItemTable.

aOT(*args, **kwargs)

Short alias for getAvgOrbitTable.

getAvgOrbitT(*args, **kwargs)

Short alias for getAvgOrbitTable.

aLT(*args, **kwargs)

Short alias for getAvgLossTable.

getAvgLossT(*args, **kwargs)

Short alias for getAvgLossTable.

aRLT(*args, **kwargs)

Short alias for getAvgRegionLossTable.

getAvgRegionLossT(*args, **kwargs)

Short alias for getAvgRegionLossTable.

aRT(*args, **kwargs)

Short alias for getAvgRegionTable.

getAvgRegionT(*args, **kwargs)

Short alias for getAvgRegionTable.

hasResults()[source]

True if the solver has computed results.

isSolved()[source]

True if the solver has computed results (alias of hasResults).

getSolverType()[source]

Get the solver type name (e.g. ‘MVA’ for SolverMVA).

reset()[source]

Reset solver state and clear results.

classmethod supportsModel(model)[source]

Check if this solver supports the given model type.

static checkBindingCapacity(model, solver_name)[source]

(bool, reason) structural gate for finite station capacity (setCapacity) and finite per-class buffers (classCap), used by the product-form solvers (MVA, NC). A product-form solver has no representation of a finite buffer, so without this gate it silently returns the UNCONSTRAINED answer (e.g. QLen=4 instead of the M/M/1/2 value 0.8525). There is no registry feature name for plain capacity, hence the structural test. Port of MATLAB NetworkSolver.checkBindingCapacity.

Reads the node-level capacity/class_capacity set by the user, NOT sn.cap/sn.classcap: _refresh_capacity derives a FINITE sn.classcap (= the chain population) for every closed model, so an sn-level test would reject every closed model.

Only a capacity that can actually BIND is rejected. A closed model whose station capacity is at least the total population can never block a job, so the declaration is a no-op and the product-form answer stays exact (a common idiom: setCapacity(N) on a station of an N-job closed model). njobs is Inf for an open class, so any finite capacity reachable by an open class binds.

Cache models are exempt: the Cache node sets class_capacity=1 on the retrieval queues it builds, and MVA/NC solve those through their dedicated cache/retrieval analyzers rather than as a buffer constraint.

THE TEST ITSELF IS Network.find_binding_capacity, one predicate with two callers: this gate, which words the refusal, and get_used_lang_features, which marks the registry name ‘FiniteCapacity’ on the same answer, so a solver method that does not declare the name is refused by the feature set on exactly the models this gate refuses.

static check_binding_capacity(model, solver_name)

(bool, reason) structural gate for finite station capacity (setCapacity) and finite per-class buffers (classCap), used by the product-form solvers (MVA, NC). A product-form solver has no representation of a finite buffer, so without this gate it silently returns the UNCONSTRAINED answer (e.g. QLen=4 instead of the M/M/1/2 value 0.8525). There is no registry feature name for plain capacity, hence the structural test. Port of MATLAB NetworkSolver.checkBindingCapacity.

Reads the node-level capacity/class_capacity set by the user, NOT sn.cap/sn.classcap: _refresh_capacity derives a FINITE sn.classcap (= the chain population) for every closed model, so an sn-level test would reject every closed model.

Only a capacity that can actually BIND is rejected. A closed model whose station capacity is at least the total population can never block a job, so the declaration is a no-op and the product-form answer stays exact (a common idiom: setCapacity(N) on a station of an N-job closed model). njobs is Inf for an open class, so any finite capacity reachable by an open class binds.

Cache models are exempt: the Cache node sets class_capacity=1 on the retrieval queues it builds, and MVA/NC solve those through their dedicated cache/retrieval analyzers rather than as a buffer constraint.

THE TEST ITSELF IS Network.find_binding_capacity, one predicate with two callers: this gate, which words the refusal, and get_used_lang_features, which marks the registry name ‘FiniteCapacity’ on the same answer, so a solver method that does not declare the name is refused by the feature set on exactly the models this gate refuses.

resolveMethod(options)[source]

Resolve the concrete method that will run. Base behavior is a no-op (returns options.method). Solvers that perform feature-driven selection for options.method=’default’ override this (typically via selectMethod).

getMethodFeatureSet(method)[source]

Per-method feature set as a set of feature-name strings, or None to signal ‘this solver does not diverge per method’ (the gate then uses the solver’s own supports(model)). Divergent solvers override this.

supportsModelMethod(method)[source]

Fine, method-aware gate. Returns (bool, reason). Base behavior derives the answer from getMethodFeatureSet(method); when that is None, the solver’s coarse supports(model) is used (reason left empty). Solvers with non-feature-set structural per-method rules override this.

selectMethod(preference_list)[source]

Feature-driven selection: first method in preference_list whose per-method feature set covers the model; falls back to the last entry.

unsupportedMethodReason(method)[source]

A BY-NAME explanation for a method this solver does not implement, or ‘’ when it has none.

It answers about the NAME and not about the model, which is what makes it safe to call from runAnalyzerChecks(): that gate runs before the struct is necessarily usable, so an override must not reach for the struct or for anything else that depends on the model. A reason that depends on the model belongs in supportsModelMethod(), which runs later and is allowed to.

The case this exists for is a method that MOVED. Dropping the name from listValidMethods is what makes the solver refuse it, and it is also what loses the forwarding address, so the two have to be declared together. C++ has always ordered the two this way – check_method calls rcat_moved_to_ag BEFORE its unlisted-method throw – and this is the python counterpart of that helper.

runAnalyzerChecks(options)[source]

Single, method-aware feature gate shared by every solver. Resolves the concrete method (default may map to a specific method), validates the method name, then gates the model against that method’s feature set.

resolve_method(options)

Resolve the concrete method that will run. Base behavior is a no-op (returns options.method). Solvers that perform feature-driven selection for options.method=’default’ override this (typically via selectMethod).

get_method_feature_set(method)

Per-method feature set as a set of feature-name strings, or None to signal ‘this solver does not diverge per method’ (the gate then uses the solver’s own supports(model)). Divergent solvers override this.

supports_model_method(method)

Fine, method-aware gate. Returns (bool, reason). Base behavior derives the answer from getMethodFeatureSet(method); when that is None, the solver’s coarse supports(model) is used (reason left empty). Solvers with non-feature-set structural per-method rules override this.

select_method(preference_list)

Feature-driven selection: first method in preference_list whose per-method feature set covers the model; falls back to the last entry.

run_analyzer_checks(options)

Single, method-aware feature gate shared by every solver. Resolves the concrete method (default may map to a specific method), validates the method name, then gates the model against that method’s feature set.

has_results()

True if the solver has computed results.

is_solved()

True if the solver has computed results (alias of hasResults).

solver_type()

Get the solver type name (e.g. ‘MVA’ for SolverMVA).

initFromSolver(init_solver)[source]

Warm-start the solver from the steady-state solution of an auxiliary solver.

The auxiliary solver’s steady-state distribution decides an integer job placement (see warmstart.warm_start_placement): with SolverCTMC the mode of the exact aggregate stationary distribution, with any other solver the rounded mean queue lengths conserving each closed-class population. The placement is applied as the model initial state via initFromMarginal, which the state-driven solvers honor: SolverFLD starts the ODE integration from it, SolverSSA starts the simulated trajectory from it, and SolverJMT preloads the stations with it. Note that this modifies the initial state of the model object shared with any other solver instance.

Parameters:

init_solver – auxiliary solver used to compute the steady-state distribution (e.g. SolverCTMC or SolverMVA on the same model)

Returns:

self, for chaining

init_from_solver(init_solver)

Warm-start the solver from the steady-state solution of an auxiliary solver.

The auxiliary solver’s steady-state distribution decides an integer job placement (see warmstart.warm_start_placement): with SolverCTMC the mode of the exact aggregate stationary distribution, with any other solver the rounded mean queue lengths conserving each closed-class population. The placement is applied as the model initial state via initFromMarginal, which the state-driven solvers honor: SolverFLD starts the ODE integration from it, SolverSSA starts the simulated trajectory from it, and SolverJMT preloads the stations with it. Note that this modifies the initial state of the model object shared with any other solver instance.

Parameters:

init_solver – auxiliary solver used to compute the steady-state distribution (e.g. SolverCTMC or SolverMVA on the same model)

Returns:

self, for chaining

getQLen()[source]

Get average queue lengths (alias for getAvgQLen).

getUtil()[source]

Get utilizations (alias for getAvgUtil).

getRespT()[source]

Get average response times (alias for getAvgRespT).

getResidT()[source]

Get average residence times (alias for getAvgResidT).

getTput()[source]

Get average throughputs (alias for getAvgTput), so (M, K).

It is an ALIAS and nothing else. SolverFLD and AvgResultsMixin each used to override it with a reduction of their own – mean(TN, axis=0) and TN.flatten() – so one name meant three shapes and two of them were not throughputs: the mean over stations counts the Source row and every station a class never visits, and agreed with the real value only on a closed cycle, where every station carries the same X. A per-class system throughput is getAvgSysTput; the per station-class one is this.

getWaitT()[source]

Get average waiting times (alias for getAvgWaitT).

getCdfRespT(R=None)[source]

Response time CDF at steady state, the base exponential fallback.

Mirrors MATLAB @NetworkSolver/getCdfRespT and JAR NetworkSolver: an exponential law with the right mean per (station, class), tabulated on 100 quantile points. It is a trivial approximation that says nothing about the tail; solvers with a distributional result override it, and the simulators refuse instead of inheriting it.

Returns:

List of dicts with ‘station’, ‘class’ (1-based), ‘t’, ‘p’, one per (station, class) pair with a finite positive mean response time – the flat native contract the analytical solvers share.

getTranCdfRespT(*args, **kwargs)[source]

Not supported by this solver, as in the reference base class.

getCdfPassT(*args, **kwargs)[source]

Not supported by this solver, as in the reference base class.

getTranCdfPassT(*args, **kwargs)[source]

Not supported by this solver, as in the reference base class.

getSjrnT(*args, **kwargs)[source]

System sojourn-time distribution: alias of getCdfRespT (matching the JAR/wrapper API).

sjrn_t(*args, **kwargs)

System sojourn-time distribution: alias of getCdfRespT (matching the JAR/wrapper API).

getDistribRespT(*args, **kwargs)[source]

Response time distributions (same data as getCdfRespT).

getDistribRespTChain(*args, **kwargs)

Response time distributions (same data as getCdfRespT).

getDistribRespTNode(*args, **kwargs)

Response time distributions (same data as getCdfRespT).

getDistribRespTNodeChain(*args, **kwargs)

Response time distributions (same data as getCdfRespT).

distrib_respt(*args, **kwargs)

Response time distributions (same data as getCdfRespT).

distrib_respt_chain(*args, **kwargs)

Response time distributions (same data as getCdfRespT).

distrib_respt_node(*args, **kwargs)

Response time distributions (same data as getCdfRespT).

distrib_respt_node_chain(*args, **kwargs)

Response time distributions (same data as getCdfRespT).

getCacheAvgT()[source]

Cache metrics table: alias of getAvgCacheTable (JAR-compatible name).

getItemAvgT()[source]

Cache item metrics table: alias of getAvgItemTable (JAR-compatible name).

cache_avg_t()

Cache metrics table: alias of getAvgCacheTable (JAR-compatible name).

item_avg_t()

Cache item metrics table: alias of getAvgItemTable (JAR-compatible name).

getAvgRegionTable()[source]

Per-region finite capacity region (FCR) metrics table.

One row per region per class with QLen, RespT, ResidT, ArvR, Tput and the FCR-specific Weight and MemOcc columns. Populated by solvers whose results carry FCR matrices (LDES); empty otherwise, matching the wrapper behavior.

avg_region_table()

Per-region finite capacity region (FCR) metrics table.

One row per region per class with QLen, RespT, ResidT, ArvR, Tput and the FCR-specific Weight and MemOcc columns. Populated by solvers whose results carry FCR matrices (LDES); empty otherwise, matching the wrapper behavior.

getRegionAvgT()

Per-region finite capacity region (FCR) metrics table.

One row per region per class with QLen, RespT, ResidT, ArvR, Tput and the FCR-specific Weight and MemOcc columns. Populated by solvers whose results carry FCR matrices (LDES); empty otherwise, matching the wrapper behavior.

regionAvgT()[source]

Per-region finite capacity region (FCR) metrics table.

One row per region per class with QLen, RespT, ResidT, ArvR, Tput and the FCR-specific Weight and MemOcc columns. Populated by solvers whose results carry FCR matrices (LDES); empty otherwise, matching the wrapper behavior.

getDeadlineTable()[source]

Deadline metrics table (RespT, tardiness, system tardiness).

Requires a solver whose results carry the TardN/SysTardN matrices; returns None otherwise, matching the JAR getDeadlineTable behavior.

deadline_table()

Deadline metrics table (RespT, tardiness, system tardiness).

Requires a solver whose results carry the TardN/SysTardN matrices; returns None otherwise, matching the JAR getDeadlineTable behavior.

getTranProb(node)[source]

Transient state probabilities for a node. Only available for solvers that compute transient state trajectories (LDES); raises otherwise, matching the JAR behavior for unsupported solvers.

getTranProbAggr(node)[source]

Aggregated transient state probabilities for a node (see getTranProb).

getTranProbSys()[source]

Transient system state probabilities (see getTranProb).

getTranProbSysAggr()[source]

Aggregated transient system state probabilities (see getTranProb).

tran_prob(node)

Transient state probabilities for a node. Only available for solvers that compute transient state trajectories (LDES); raises otherwise, matching the JAR behavior for unsupported solvers.

tran_prob_aggr(node)

Aggregated transient state probabilities for a node (see getTranProb).

tran_prob_sys()

Transient system state probabilities (see getTranProb).

tran_prob_sys_aggr()

Aggregated transient system state probabilities (see getTranProb).

aT()

Short alias for getAvgTable.

aST()

Short alias for getAvgSysTable.

aNT()

Short alias for getAvgNodeTable.

aCT()

Short alias for getAvgChainTable.

aNCT()

Short alias for getAvgNodeChainTable.

chainAvgT()

Short alias for getAvgChainTable.

nodeAvgT()

Short alias for getAvgNodeTable.

nodeChainAvgT()

Short alias for getAvgNodeChainTable.

sysAvgT()

Short alias for getAvgSysTable.

avg_t()

Short alias for getAvgTable.

avg_sys_t()

Short alias for getAvgSysTable.

avg_node_t()

Short alias for getAvgNodeTable.

avg_chain_t()

Short alias for getAvgChainTable.

avg_node_chain_t()

Short alias for getAvgNodeChainTable.

a_t()

Short alias for getAvgTable.

a_st()

Short alias for getAvgSysTable.

a_nt()

Short alias for getAvgNodeTable.

a_ct()

Short alias for getAvgChainTable.

a_nct()

Short alias for getAvgNodeChainTable.

class EnsembleSolver[source]

Bases: Solver

Base class for ensemble/multi-model LINE solvers.

Used by: SolverLN, SolverUQ, SolverENV

isStochastic()[source]

An ensemble solver is stochastic if any of its submodel solvers is stochastic. Each submodel solver classifies itself, including from the method it resolved at runtime.

is_stochastic()

An ensemble solver is stochastic if any of its submodel solvers is stochastic. Each submodel solver classifies itself, including from the method it resolved at runtime.

avgT()[source]

Short alias for getAvgTable (resolved on the concrete ensemble solver: SolverLN/SolverENV/SolverUQ).

aT()

Short alias for getAvgTable (resolved on the concrete ensemble solver: SolverLN/SolverENV/SolverUQ).

getAvgT()

Short alias for getAvgTable (resolved on the concrete ensemble solver: SolverLN/SolverENV/SolverUQ).

avg_t()

Short alias for getAvgTable (resolved on the concrete ensemble solver: SolverLN/SolverENV/SolverUQ).

a_t()

Short alias for getAvgTable (resolved on the concrete ensemble solver: SolverLN/SolverENV/SolverUQ).

Analytical Solvers

class SolverMVA(model, method_or_options=None, **kwargs)[source]

Bases: TransformSolveMixin, ForkJoinDriverMixin, NetworkSolver

Native Python Mean Value Analysis (MVA) solver.

This solver implements MVA algorithms using pure Python/NumPy, providing the same functionality as the Java wrapper without requiring the JVM.

Supported methods:
  • ‘exact’: Exact MVA (pfqn_mva)

  • ‘mva’: Same as exact

  • ‘amva’: Approximate MVA using Schweitzer approximation

  • ‘qna’: Queueing Network Analyzer (for open networks)

Bound methods (aba, bjb, pb, gb, sb, mwba, …) are served by SolverBA, not here; runAnalyzer rejects the whole family.

Parameters:
  • model – Network model (Python wrapper or native structure)

  • method – Solution method (default: ‘default’, which auto-selects exact/amva based on model)

  • **kwargs – Additional solver options

supportsExactSensitivity()[source]

MVA differentiates its own recursion: getSensitivityTable uses the analytic branch (pfqn_sens) wherever the model is in scope.

supports_exact_sensitivity()

MVA differentiates its own recursion: getSensitivityTable uses the analytic branch (pfqn_sens) wherever the model is in scope.

reset()[source]

Reset the solver to force recomputation on next getAvg call.

runAnalyzer()[source]

Run the MVA analysis.

getAvgTable()[source]

Get comprehensive average performance metrics table.

Returns node-based results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork) are included with zero metrics.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getProbAggr(ist)[source]

Get probability of current per-class job distribution at a station.

Returns P(n_1, …, n_K at station i) using binomial approximation.

Parameters:

ist (int) – Station index (1-based, MATLAB style)

Returns:

Tuple of log probability and probability value

Return type:

(log_prob, prob)

Raises:

ValueError – If station index invalid or analysis not run

Example

>>> solver = SolverMVA(model)
>>> solver.runAnalyzer()
>>> log_p, p = solver.getProbAggr(1)  # Station 1
getProbMarg(ist, jobclass, state_m=None)[source]

Get marginal queue-length distribution for a class at a station.

Returns P(n | station i, class r) for n = 0, 1, …, N[r].

Parameters:
  • ist (int) – Station index (1-based)

  • jobclass (int) – Job class index (1-based)

  • state_m (ndarray | None) – Optional state vector (for future use)

Returns:

Array of state indices and probabilities
  • states: [0, 1, …, N[jobclass]]

  • probs: Probability distribution summing to 1.0

Return type:

(states, probs)

Example

>>> states, probs = solver.getProbMarg(1, 1)
>>> print(f"P(n=2 at station 1, class 1) = {probs[2]}")
getProbSysAggr()[source]

Get joint probability of current system state.

Returns P(full system state) using product of station marginals.

Returns:

Log probability and probability value

Return type:

(log_prob, prob)

Notes

  • Assumes station independence (valid for product-form networks)

  • Requires network state to be set

Example

>>> log_p, p = solver.getProbSysAggr()
>>> print(f"System state probability: {p:.6e}")
getProbNormConstAggr()[source]

Get log normalizing constant for closed queueing network.

Returns log(G) where G = ∑_state P(state).

Returns:

Natural logarithm of normalizing constant
  • For open networks: returns inf

  • For closed networks: exact value from MVA or approximation

Return type:

log_G

Notes

  • Only applies to closed queueing networks

  • Returns inf for open networks (normalizing constant is infinite)

Example

>>> log_G = solver.getProbNormConstAggr()
>>> G = np.exp(log_G)  # Reconstruct if needed
getAvgQLen()[source]

Get average queue lengths.

Returns:

Queue lengths matrix (M x K)

M = number of stations K = number of classes Q[i,r] = average number of jobs of class r at station i

Return type:

Q

Raises:

RuntimeError – If solver not run yet

Example

>>> Q = solver.getAvgQLen()
>>> print(f"Queue length at station 1, class 1: {Q[0,0]}")
getAvgUtil()[source]

Get average utilizations.

Returns:

Utilization matrix (M x K)

U[i,r] = utilization of station i by class r Range: [0, 1] for single-server, [0, ∞) for multi-server

Return type:

U

Example

>>> U = solver.getAvgUtil()
>>> print(f"Utilization at station 1: {U[0,:].sum()}")
getAvgRespT()[source]

Get average response times.

Returns:

Response times matrix (M x K)

R[i,r] = average time spent at station i for class r Includes both service and waiting time

Return type:

R

Example

>>> R = solver.getAvgRespT()
>>> print(f"Response time at station 1, class 1: {R[0,0]}")
getAvgResidT()[source]

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

Returns:

Residence times matrix (M x K)

Return type:

ResidT

getAvgWaitT()[source]

Get average waiting times.

Returns:

Waiting times matrix (M x K)

W[i,r] = R[i,r] - S[i,r] where S[i,r] is the mean service time W[i,r] = 0 for Delay (think time) stations

Return type:

W

Example

>>> W = solver.getAvgWaitT()
>>> print(f"Waiting time at station 1: {W[0,:].sum()}")
getAvgTput()[source]

Get average throughputs.

Returns:

Throughput matrix (M x K)

T[i,r] = average throughput at station i for class r jobs/time unit

Return type:

T

Example

>>> T = solver.getAvgTput()
>>> print(f"Throughput at station 1, class 1: {T[0,0]}")
getAvgArvR()[source]

Get average arrival rates.

Returns:

Arrival rates matrix (M x K)

A[i,r] = arrival rate to station i for class r

Return type:

A

Note

For closed networks, arrival rates are derived from throughputs and visit ratios

getAvgSysRespT()[source]

Get system response times (cycle times) per CHAIN.

Returns:

Cycle time vector (C,), one entry per chain.

For a closed chain this is Little’s law on the chain population; for an open one the visit-weighted sum of per-class residence times. It is the CNchain of MATLAB @NetworkSolver/getAvgSys.m.

Return type:

C

getAvgSysTput()[source]

Get system throughputs per CHAIN.

Returns:

System throughput vector (C,), one entry per chain: the rate of

completing classes routed back into that chain’s reference station. This is the XNchain of MATLAB @NetworkSolver/getAvgSys.m, which is what getAvgSysTput.m returns and what the JAR publishes as result.XN.

Return type:

X

Note

It used to return the analyzer’s per-CLASS result[‘XN’], which agrees with the above only when every chain holds one class – the common case, and the reason the difference went unseen.

getAvgQLenChain()[source]

Get average queue lengths aggregated by chain.

getAvgUtilChain()[source]

Get average utilizations aggregated by chain.

Cleans up tiny numerical values (< 1e-10) to exactly 0.

getAvgRespTChain()[source]

Get average response times aggregated by chain.

Uses alpha-weighted sum matching MATLAB: RN(:,c) = sum(RNclass(:,inchain).*alpha(:,inchain),2)

getAvgResidTChain()[source]

Get average residence times aggregated by chain.

Residence time accounts for visit ratios, computed as: WN(i,c) = sum(WNclass(i, inchain)) where WNclass = sn_get_residt_from_respt converts response times to residence times.

getAvgTputChain()[source]

Get average throughputs aggregated by chain.

Sums per-station throughputs for all classes in chain: TN(:,c) = sum(TNclass(:, inchain), 2)

getAvgArvRChain()[source]

Get average arrival rates aggregated by chain.

For most stations, arrival rate equals throughput at steady state. For Source nodes, arrival rate is 0 (jobs don’t arrive TO a source, they depart FROM it).

getAvgChain()[source]

Get all average metrics aggregated by chain.

getAvgChainTable()[source]

Get average metrics by chain as DataFrame.

getAvgNode()[source]

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Cache). For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgNodeTable()[source]

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvgCacheTable()[source]

Detailed per-class cache performance metrics (see cache_table).

get_avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

getAvgItemTable()[source]

Item-level cache occupancy table (see cache_table).

get_avg_item_table()

Item-level cache occupancy table (see cache_table).

avg_item_table()

Item-level cache occupancy table (see cache_table).

getAvgNodeChain()[source]

Get average metrics by node and chain.

getAvgNodeChainTable()[source]

Get average metrics by node and chain as DataFrame.

getAvgNodeQLenChain()[source]

Get average queue lengths by node aggregated by chain.

getAvgNodeUtilChain()[source]

Get average utilizations by node aggregated by chain.

getAvgNodeRespTChain()[source]

Get average response times by node aggregated by chain.

getAvgNodeResidTChain()[source]

Get average residence times by node aggregated by chain.

getAvgNodeTputChain()[source]

Get average throughputs by node aggregated by chain.

getAvgNodeArvRChain()[source]

Get average arrival rates by node aggregated by chain.

getAvgSys()[source]

Get system-level average metrics.

getAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

GetAvgQLen()

Get average queue lengths.

Returns:

Queue lengths matrix (M x K)

M = number of stations K = number of classes Q[i,r] = average number of jobs of class r at station i

Return type:

Q

Raises:

RuntimeError – If solver not run yet

Example

>>> Q = solver.getAvgQLen()
>>> print(f"Queue length at station 1, class 1: {Q[0,0]}")
GetAvgUtil()

Get average utilizations.

Returns:

Utilization matrix (M x K)

U[i,r] = utilization of station i by class r Range: [0, 1] for single-server, [0, ∞) for multi-server

Return type:

U

Example

>>> U = solver.getAvgUtil()
>>> print(f"Utilization at station 1: {U[0,:].sum()}")
GetAvgRespT()

Get average response times.

Returns:

Response times matrix (M x K)

R[i,r] = average time spent at station i for class r Includes both service and waiting time

Return type:

R

Example

>>> R = solver.getAvgRespT()
>>> print(f"Response time at station 1, class 1: {R[0,0]}")
GetAvgResidT()

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

Returns:

Residence times matrix (M x K)

Return type:

ResidT

GetAvgWaitT()

Get average waiting times.

Returns:

Waiting times matrix (M x K)

W[i,r] = R[i,r] - S[i,r] where S[i,r] is the mean service time W[i,r] = 0 for Delay (think time) stations

Return type:

W

Example

>>> W = solver.getAvgWaitT()
>>> print(f"Waiting time at station 1: {W[0,:].sum()}")
GetAvgTput()

Get average throughputs.

Returns:

Throughput matrix (M x K)

T[i,r] = average throughput at station i for class r jobs/time unit

Return type:

T

Example

>>> T = solver.getAvgTput()
>>> print(f"Throughput at station 1, class 1: {T[0,0]}")
GetAvgArvR()

Get average arrival rates.

Returns:

Arrival rates matrix (M x K)

A[i,r] = arrival rate to station i for class r

Return type:

A

Note

For closed networks, arrival rates are derived from throughputs and visit ratios

GetAvgSysRespT()

Get system response times (cycle times) per CHAIN.

Returns:

Cycle time vector (C,), one entry per chain.

For a closed chain this is Little’s law on the chain population; for an open one the visit-weighted sum of per-class residence times. It is the CNchain of MATLAB @NetworkSolver/getAvgSys.m.

Return type:

C

GetAvgSysTput()

Get system throughputs per CHAIN.

Returns:

System throughput vector (C,), one entry per chain: the rate of

completing classes routed back into that chain’s reference station. This is the XNchain of MATLAB @NetworkSolver/getAvgSys.m, which is what getAvgSysTput.m returns and what the JAR publishes as result.XN.

Return type:

X

Note

It used to return the analyzer’s per-CLASS result[‘XN’], which agrees with the above only when every chain holds one class – the common case, and the reason the difference went unseen.

getCdfRespT(R=None)[source]

Get response time cumulative distribution function (CDF).

Uses exponential approximation: CDF(t) = 1 - exp(-t / E[R]) where E[R] is the mean response time from MVA.

Parameters:

R (ndarray | None) – Optional response times matrix (M x K) If None, uses results from runAnalyzer()

Returns:

List of dicts, one per (station, class) pair with service

Each dict contains: - ‘station’: Station index (1-based) - ‘class’: Job class (1-based) - ‘t’: Time points (100 points from 0.001 to 0.999 quantile) - ‘p’: CDF values at each time point

Return type:

RD

Notes

  • Uses exponential approximation for single-class exponential service

  • For multi-phase or non-exponential, this is approximate

  • Returns empty list for stations with zero response time

Example

>>> cdf_list = solver.getCdfRespT()
>>> for cdf_data in cdf_list:
...     station = cdf_data['station']
...     class_id = cdf_data['class']
...     print(f"Station {station}, Class {class_id}")
...     # Access t and p arrays for plotting
...     t = cdf_data['t']
...     p = cdf_data['p']
getPerctRespT(percentiles=None, jobclass=None, method='default')[source]

Extract percentiles from response time distribution.

Computes percentile points from the exponential CDF approximation.

Parameters:
  • percentiles (List[float] | None) – List of percentiles to extract (0-100) Default: [10, 25, 50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class index to filter results (1-based) If None, returns all classes

Returns:

(PercRT, PercTable) where PercRT is a list of dicts with percentile data for each (station, class), each holding ‘station’ (station index), ‘class’ (job class), ‘percentiles’ (input percentile values) and ‘values’ (percentile response times), and PercTable is a pandas DataFrame with columns Station, Class, P10, P25, P50, P75, P90, P95, P99, …

Return type:

Tuple[List[Dict], DataFrame]

Algorithm:

For an exponential CDF with rate lambda = 1/E[R], the percentile p is t_p = -ln(1-p) * E[R] with p in [0,1].

Notes

Percentiles outside (0,100) are clipped, stations with zero response time give empty lists, and results match the exponential percentile formula.

Example:

>>> perc_list, perc_table = solver.getPerctRespT([90, 95, 99])
>>> print(perc_table)
>>> # Extract 90th percentile response time
>>> p90_values = perc_list[0]['values']
listValidMethods()[source]

List all valid solution methods for this network model.

Returns the set of applicable methods based on network characteristics (single-class/multi-class, open/closed, etc.).

Returns:

  • Base methods: ‘default’, ‘mva’, ‘exact’, ‘amva’, ‘qna’

  • AMVA variants: ‘bs’, ‘sqni’, ‘tay’, ‘lin’, ‘gflin’, ‘egflin’, ‘schmidt’, ‘schmidt-ext’, ‘ab’

  • Queueing formulas (2-station open): ‘mm1’, ‘mmk’, ‘mg1’, ‘mgi1’, ‘gm1’, ‘gig1’, etc.

Return type:

List of method names available for this model

Notes

  • MVA solver always supports ‘default’ and ‘exact’ methods

  • AMVA (approximate MVA) available for most networks

  • Bounds methods useful for single-class networks

  • All methods also available with ‘amva.’ prefix (e.g., ‘amva.lin’)

Example

>>> methods = solver.listValidMethods()
>>> print(f"Available methods: {methods}")
>>> # Can then call: solver.method = 'amva.lin'
resolveMethod(options)[source]

Feature-driven resolution of method=’default’: a bursty single-class open network has a non-renewal (MAP/MMPP) arrival process, so the default dispatch selects RQNA. The gate then admits the MAP family only on this RQNA path. Mirrors the MATLAB/JAR SolverMVA.resolveMethod and the analyzer’s default->RQNA dispatch below.

resolve_method(options)

Feature-driven resolution of method=’default’: a bursty single-class open network has a non-renewal (MAP/MMPP) arrival process, so the default dispatch selects RQNA. The gate then admits the MAP family only on this RQNA path. Mirrors the MATLAB/JAR SolverMVA.resolveMethod and the analyzer’s default->RQNA dispatch below.

getMethodFeatureSet(method)[source]

Per-method feature deltas applied to the base MVA envelope. QNA is a two-moment open-network method, so it drops closed-class support. The queueing-system and bounds methods are already structurally restricted by listValidMethods and inherit the base envelope. RQNA adds the non-renewal MAP/MMPP family (open only); mirrors the MATLAB/JAR SolverMVA.getMethodFeatureSet.

get_method_feature_set(method)

Per-method feature deltas applied to the base MVA envelope. QNA is a two-moment open-network method, so it drops closed-class support. The queueing-system and bounds methods are already structurally restricted by listValidMethods and inherit the base envelope. RQNA adds the non-renewal MAP/MMPP family (open only); mirrors the MATLAB/JAR SolverMVA.getMethodFeatureSet.

supportsModelMethod(method)[source]

Finite station/class capacity has no registry feature name, so the coarse per-method feature gate cannot see it. Apply the structural capacity check on top of it, otherwise MVA silently returns the unconstrained product-form answer for models built with setCapacity / a finite classCap (BUG-39). Mirrors MATLAB SolverMVA.supportsModelMethod.

supports_model_method(method)

Finite station/class capacity has no registry feature name, so the coarse per-method feature gate cannot see it. Apply the structural capacity check on top of it, otherwise MVA silently returns the unconstrained product-form answer for models built with setCapacity / a finite classCap (BUG-39). Mirrors MATLAB SolverMVA.supportsModelMethod.

static supportsExactness(model, method)[source]

(bool, reason) Method ‘exact’ requires a product-form solution, the same rule the analyzer enforces at solve time. Order-independent and pass-and-swap stations are exempt: solver_mva_oi_analyzer is exact for them regardless of the product-form test. Single-station open systems are exempt too: they go to a queueing-system formula (M/G/1 PK, M/M/k, Cobham, matrix-geometric, …) that holds outside product form, never to the MVA recursion. Product form has no registry feature name, so the check cannot live in getMethodFeatureSet. Mirrors MATLAB SolverMVA.supportsExactness.

static supports_exactness(model, method)

(bool, reason) Method ‘exact’ requires a product-form solution, the same rule the analyzer enforces at solve time. Order-independent and pass-and-swap stations are exempt: solver_mva_oi_analyzer is exact for them regardless of the product-form test. Single-station open systems are exempt too: they go to a queueing-system formula (M/G/1 PK, M/M/k, Cobham, matrix-geometric, …) that holds outside product form, never to the MVA recursion. Product form has no registry feature name, so the check cannot live in getMethodFeatureSet. Mirrors MATLAB SolverMVA.supportsExactness.

static supportsFiniteCapacity(model)[source]

(bool, reason) MVA-specific finite-capacity gate: Blocking-After-Service models are exempt because MVA offers the Smith queue-decomposition method ‘sqd’, and the analyzer routes a BAS model to solver_sqd under the default method too, so the finite buffers ARE honoured on every MVA path. Everything else defers to the shared product-form gate. Mirrors MATLAB SolverMVA.supportsFiniteCapacity.

static supports_finite_capacity(model)

(bool, reason) MVA-specific finite-capacity gate: Blocking-After-Service models are exempt because MVA offers the Smith queue-decomposition method ‘sqd’, and the analyzer routes a BAS model to solver_sqd under the default method too, so the finite buffers ARE honoured on every MVA path. Everything else defers to the shared product-form gate. Mirrors MATLAB SolverMVA.supportsFiniteCapacity.

static getFeatureSet()[source]

Get set of features supported by the MVA solver.

Returns the canonical feature names (mirrors MATLAB SolverMVA.getFeatureSet and the JAR SolverMVA).

static supports(model, extra_features=None)[source]

Check if MVA solver supports the given network model.

Performs basic model validation to ensure compatibility.

Parameters:

model – Network model to check (native or wrapper)

Returns:

True if model is supported, False otherwise

Return type:

bool

Notes

  • Checks for product-form network structure

  • Verifies presence of required network components

  • Returns True for most standard queueing networks

Example

>>> if SolverMVA.supports(model):
...     solver = SolverMVA(model)
... else:
...     print("Model not supported by MVA")
static defaultOptions()[source]

Get default solver options.

Returns:

  • ‘method’: ‘default’ (auto-selects exact/amva, as MATLAB/JAR do)

  • ’tol’: 1e-4 (general-purpose tolerance)

  • ’max_iter’: 1000 (maximum iterations)

  • ’verbose’: default_verbose() (inherits GlobalConstants verbosity)

  • ’config’: {} (per-method switches, e.g. ‘map_env_method’)

Return type:

Dictionary with default option values

config is present but EMPTY, as MATLAB’s SolverMVA.defaultOptions carries an empty config struct: every consumer reads it with a default, so an absent key and an unset one mean the same thing, and the attribute must exist for options.config[‘key’] = … to work.

Example

>>> opts = SolverMVA.defaultOptions()
>>> opts['method'] = 'amva'  # Override for approximate MVA
>>> solver = SolverMVA(model, **opts)
sample(node, numEvents)[source]

Sample from the response time distribution.

Not supported by MVA solver - MVA is an analytical solver. For sampling, use simulation-based solvers.

Parameters:
  • node (int) – Node/station index (1-based)

  • numEvents (int) – Number of samples to generate

Returns:

NotImplementedError (sampling not supported)

Raises:

NotImplementedError – Always - MVA does not support sampling

Return type:

ndarray

Recommendation:

Use SolverSSA (Stochastic State-space Analysis) or SolverJMT (JMT simulator) for sampling-based analysis.

Example

>>> # Instead of sampling from MVA:
>>> # solver = SolverMVA(model)
>>> # This will raise NotImplementedError
>>> solver.sample(1, 1000)
sampleAggr(node, numEvents)[source]

Aggregate sampling (not supported by MVA).

sampleSys(numEvents)[source]

System-level sampling (not supported by MVA).

sampleSysAggr(numEvents)[source]

Aggregate system-level sampling (not supported by MVA).

getCdfPassT(R=None)[source]

Get passage time CDF (not supported by MVA).

Passage time = time to reach target station from source. Not computed by analytical MVA solver.

Parameters:

R (ndarray | None) – Optional response times (ignored)

Raises:

NotImplementedError – Passage time analysis not available

Recommendation:

Use simulation-based solvers for detailed path analysis.

getTranCdfRespT(R=None)[source]

Get transient response time CDF (not supported by MVA).

Transient analysis (time-dependent) not available from steady-state MVA.

Parameters:

R (ndarray | None) – Optional response times (ignored)

Raises:

NotImplementedError – Transient analysis not available

Recommendation:

Use SolverCTMC (Markov chain) or simulation solvers for transient.

getTranCdfPassT(R=None)[source]

Transient passage time CDF (not supported by MVA).

getTranAvg()[source]

Get transient average metrics (not supported by MVA).

MVA computes only steady-state metrics.

Raises:

NotImplementedError – Transient analysis not available

ListValidMethods()

List all valid solution methods for this network model.

Returns the set of applicable methods based on network characteristics (single-class/multi-class, open/closed, etc.).

Returns:

  • Base methods: ‘default’, ‘mva’, ‘exact’, ‘amva’, ‘qna’

  • AMVA variants: ‘bs’, ‘sqni’, ‘tay’, ‘lin’, ‘gflin’, ‘egflin’, ‘schmidt’, ‘schmidt-ext’, ‘ab’

  • Queueing formulas (2-station open): ‘mm1’, ‘mmk’, ‘mg1’, ‘mgi1’, ‘gm1’, ‘gig1’, etc.

Return type:

List of method names available for this model

Notes

  • MVA solver always supports ‘default’ and ‘exact’ methods

  • AMVA (approximate MVA) available for most networks

  • Bounds methods useful for single-class networks

  • All methods also available with ‘amva.’ prefix (e.g., ‘amva.lin’)

Example

>>> methods = solver.listValidMethods()
>>> print(f"Available methods: {methods}")
>>> # Can then call: solver.method = 'amva.lin'
static GetFeatureSet()

Get set of features supported by the MVA solver.

Returns the canonical feature names (mirrors MATLAB SolverMVA.getFeatureSet and the JAR SolverMVA).

static Supports(model, extra_features=None)

Check if MVA solver supports the given network model.

Performs basic model validation to ensure compatibility.

Parameters:

model – Network model to check (native or wrapper)

Returns:

True if model is supported, False otherwise

Return type:

bool

Notes

  • Checks for product-form network structure

  • Verifies presence of required network components

  • Returns True for most standard queueing networks

Example

>>> if SolverMVA.supports(model):
...     solver = SolverMVA(model)
... else:
...     print("Model not supported by MVA")
static DefaultOptions()

Get default solver options.

Returns:

  • ‘method’: ‘default’ (auto-selects exact/amva, as MATLAB/JAR do)

  • ’tol’: 1e-4 (general-purpose tolerance)

  • ’max_iter’: 1000 (maximum iterations)

  • ’verbose’: default_verbose() (inherits GlobalConstants verbosity)

  • ’config’: {} (per-method switches, e.g. ‘map_env_method’)

Return type:

Dictionary with default option values

config is present but EMPTY, as MATLAB’s SolverMVA.defaultOptions carries an empty config struct: every consumer reads it with a default, so an absent key and an unset one mean the same thing, and the attribute must exist for options.config[‘key’] = … to work.

Example

>>> opts = SolverMVA.defaultOptions()
>>> opts['method'] = 'amva'  # Override for approximate MVA
>>> solver = SolverMVA(model, **opts)
Sample(node, numEvents)

Sample from the response time distribution.

Not supported by MVA solver - MVA is an analytical solver. For sampling, use simulation-based solvers.

Parameters:
  • node (int) – Node/station index (1-based)

  • numEvents (int) – Number of samples to generate

Returns:

NotImplementedError (sampling not supported)

Raises:

NotImplementedError – Always - MVA does not support sampling

Return type:

ndarray

Recommendation:

Use SolverSSA (Stochastic State-space Analysis) or SolverJMT (JMT simulator) for sampling-based analysis.

Example

>>> # Instead of sampling from MVA:
>>> # solver = SolverMVA(model)
>>> # This will raise NotImplementedError
>>> solver.sample(1, 1000)
SampleAggr(node, numEvents)

Aggregate sampling (not supported by MVA).

SampleSys(numEvents)

System-level sampling (not supported by MVA).

SampleSysAggr(numEvents)

Aggregate system-level sampling (not supported by MVA).

GetCdfPassT(R=None)

Get passage time CDF (not supported by MVA).

Passage time = time to reach target station from source. Not computed by analytical MVA solver.

Parameters:

R (ndarray | None) – Optional response times (ignored)

Raises:

NotImplementedError – Passage time analysis not available

Recommendation:

Use simulation-based solvers for detailed path analysis.

GetTranCdfRespT(R=None)

Get transient response time CDF (not supported by MVA).

Transient analysis (time-dependent) not available from steady-state MVA.

Parameters:

R (ndarray | None) – Optional response times (ignored)

Raises:

NotImplementedError – Transient analysis not available

Recommendation:

Use SolverCTMC (Markov chain) or simulation solvers for transient.

GetTranCdfPassT(R=None)

Transient passage time CDF (not supported by MVA).

GetTranAvg()

Get transient average metrics (not supported by MVA).

MVA computes only steady-state metrics.

Raises:

NotImplementedError – Transient analysis not available

GetCdfRespT(R=None)

Get response time cumulative distribution function (CDF).

Uses exponential approximation: CDF(t) = 1 - exp(-t / E[R]) where E[R] is the mean response time from MVA.

Parameters:

R (ndarray | None) – Optional response times matrix (M x K) If None, uses results from runAnalyzer()

Returns:

List of dicts, one per (station, class) pair with service

Each dict contains: - ‘station’: Station index (1-based) - ‘class’: Job class (1-based) - ‘t’: Time points (100 points from 0.001 to 0.999 quantile) - ‘p’: CDF values at each time point

Return type:

RD

Notes

  • Uses exponential approximation for single-class exponential service

  • For multi-phase or non-exponential, this is approximate

  • Returns empty list for stations with zero response time

Example

>>> cdf_list = solver.getCdfRespT()
>>> for cdf_data in cdf_list:
...     station = cdf_data['station']
...     class_id = cdf_data['class']
...     print(f"Station {station}, Class {class_id}")
...     # Access t and p arrays for plotting
...     t = cdf_data['t']
...     p = cdf_data['p']
GetPerctRespT(percentiles=None, jobclass=None, method='default')

Extract percentiles from response time distribution.

Computes percentile points from the exponential CDF approximation.

Parameters:
  • percentiles (List[float] | None) – List of percentiles to extract (0-100) Default: [10, 25, 50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class index to filter results (1-based) If None, returns all classes

Returns:

(PercRT, PercTable) where PercRT is a list of dicts with percentile data for each (station, class), each holding ‘station’ (station index), ‘class’ (job class), ‘percentiles’ (input percentile values) and ‘values’ (percentile response times), and PercTable is a pandas DataFrame with columns Station, Class, P10, P25, P50, P75, P90, P95, P99, …

Return type:

Tuple[List[Dict], DataFrame]

Algorithm:

For an exponential CDF with rate lambda = 1/E[R], the percentile p is t_p = -ln(1-p) * E[R] with p in [0,1].

Notes

Percentiles outside (0,100) are clipped, stations with zero response time give empty lists, and results match the exponential percentile formula.

Example:

>>> perc_list, perc_table = solver.getPerctRespT([90, 95, 99])
>>> print(perc_table)
>>> # Extract 90th percentile response time
>>> p90_values = perc_list[0]['values']
GetProbAggr(ist)

Get probability of current per-class job distribution at a station.

Returns P(n_1, …, n_K at station i) using binomial approximation.

Parameters:

ist (int) – Station index (1-based, MATLAB style)

Returns:

Tuple of log probability and probability value

Return type:

(log_prob, prob)

Raises:

ValueError – If station index invalid or analysis not run

Example

>>> solver = SolverMVA(model)
>>> solver.runAnalyzer()
>>> log_p, p = solver.getProbAggr(1)  # Station 1
GetProbMarg(ist, jobclass, state_m=None)

Get marginal queue-length distribution for a class at a station.

Returns P(n | station i, class r) for n = 0, 1, …, N[r].

Parameters:
  • ist (int) – Station index (1-based)

  • jobclass (int) – Job class index (1-based)

  • state_m (ndarray | None) – Optional state vector (for future use)

Returns:

Array of state indices and probabilities
  • states: [0, 1, …, N[jobclass]]

  • probs: Probability distribution summing to 1.0

Return type:

(states, probs)

Example

>>> states, probs = solver.getProbMarg(1, 1)
>>> print(f"P(n=2 at station 1, class 1) = {probs[2]}")
GetProbSysAggr()

Get joint probability of current system state.

Returns P(full system state) using product of station marginals.

Returns:

Log probability and probability value

Return type:

(log_prob, prob)

Notes

  • Assumes station independence (valid for product-form networks)

  • Requires network state to be set

Example

>>> log_p, p = solver.getProbSysAggr()
>>> print(f"System state probability: {p:.6e}")
GetProbNormConstAggr()

Get log normalizing constant for closed queueing network.

Returns log(G) where G = ∑_state P(state).

Returns:

Natural logarithm of normalizing constant
  • For open networks: returns inf

  • For closed networks: exact value from MVA or approximation

Return type:

log_G

Notes

  • Only applies to closed queueing networks

  • Returns inf for open networks (normalizing constant is infinite)

Example

>>> log_G = solver.getProbNormConstAggr()
>>> G = np.exp(log_G)  # Reconstruct if needed
getAvgT()

Get comprehensive average performance metrics table.

Returns node-based results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork) are included with zero metrics.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

avgT()

Get comprehensive average performance metrics table.

Returns node-based results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork) are included with zero metrics.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aT()

Get comprehensive average performance metrics table.

Returns node-based results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork) are included with zero metrics.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

static default_options()

Get default solver options.

Returns:

  • ‘method’: ‘default’ (auto-selects exact/amva, as MATLAB/JAR do)

  • ’tol’: 1e-4 (general-purpose tolerance)

  • ’max_iter’: 1000 (maximum iterations)

  • ’verbose’: default_verbose() (inherits GlobalConstants verbosity)

  • ’config’: {} (per-method switches, e.g. ‘map_env_method’)

Return type:

Dictionary with default option values

config is present but EMPTY, as MATLAB’s SolverMVA.defaultOptions carries an empty config struct: every consumer reads it with a default, so an absent key and an unset one mean the same thing, and the attribute must exist for options.config[‘key’] = … to work.

Example

>>> opts = SolverMVA.defaultOptions()
>>> opts['method'] = 'amva'  # Override for approximate MVA
>>> solver = SolverMVA(model, **opts)
GetAvg()

Average station metrics (Q, U, R, T, A, W) as station x class matrices.

Single analyzer funnel of the native solvers, mirroring MATLAB @NetworkSolver/getAvg.m and JAR NetworkSolver.getAvg(): it runs the analyzer if there is no cached result, then reads the averages from whichever result store the solver uses. Every solver used to carry its own copy of this body, differing only in that store (‘_result’ vs ‘result’) and in the field naming (‘QN’ vs ‘Q’), which _AVG_FIELDS already reconciles; the duplication also meant there was no single place to intercept a solve, as the other two codebases have.

Returns:

queue lengths, utilizations, response times, throughputs, arrival rates and residence times.

Return type:

(Q, U, R, T, A, W)

GetAvgChain()

Get all average metrics aggregated by chain.

GetAvgChainTable()

Get average metrics by chain as DataFrame.

GetAvgQLenChain()

Get average queue lengths aggregated by chain.

GetAvgUtilChain()

Get average utilizations aggregated by chain.

Cleans up tiny numerical values (< 1e-10) to exactly 0.

GetAvgRespTChain()

Get average response times aggregated by chain.

Uses alpha-weighted sum matching MATLAB: RN(:,c) = sum(RNclass(:,inchain).*alpha(:,inchain),2)

GetAvgResidTChain()

Get average residence times aggregated by chain.

Residence time accounts for visit ratios, computed as: WN(i,c) = sum(WNclass(i, inchain)) where WNclass = sn_get_residt_from_respt converts response times to residence times.

GetAvgTputChain()

Get average throughputs aggregated by chain.

Sums per-station throughputs for all classes in chain: TN(:,c) = sum(TNclass(:, inchain), 2)

GetAvgArvRChain()

Get average arrival rates aggregated by chain.

For most stations, arrival rate equals throughput at steady state. For Source nodes, arrival rate is 0 (jobs don’t arrive TO a source, they depart FROM it).

GetAvgNode()

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Cache). For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

GetAvgNodeTable()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

GetAvgNodeChain()

Get average metrics by node and chain.

GetAvgNodeChainTable()

Get average metrics by node and chain as DataFrame.

GetAvgNodeQLenChain()

Get average queue lengths by node aggregated by chain.

GetAvgNodeUtilChain()

Get average utilizations by node aggregated by chain.

GetAvgNodeRespTChain()

Get average response times by node aggregated by chain.

GetAvgNodeResidTChain()

Get average residence times by node aggregated by chain.

GetAvgNodeTputChain()

Get average throughputs by node aggregated by chain.

GetAvgNodeArvRChain()

Get average arrival rates by node aggregated by chain.

GetAvgSys()

Get system-level average metrics.

GetAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

aNT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aCT()

Get average metrics by chain as DataFrame.

aNCT()

Get average metrics by node and chain as DataFrame.

aST()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

nodeAvgT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

chainAvgT()

Get average metrics by chain as DataFrame.

nodeChainAvgT()

Get average metrics by node and chain as DataFrame.

sysAvgT()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_node_table()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

avg_chain_table()

Get average metrics by chain as DataFrame.

avg_node_chain_table()

Get average metrics by node and chain as DataFrame.

avg_sys_table()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_qlen()

Get average queue lengths.

Returns:

Queue lengths matrix (M x K)

M = number of stations K = number of classes Q[i,r] = average number of jobs of class r at station i

Return type:

Q

Raises:

RuntimeError – If solver not run yet

Example

>>> Q = solver.getAvgQLen()
>>> print(f"Queue length at station 1, class 1: {Q[0,0]}")
avg_util()

Get average utilizations.

Returns:

Utilization matrix (M x K)

U[i,r] = utilization of station i by class r Range: [0, 1] for single-server, [0, ∞) for multi-server

Return type:

U

Example

>>> U = solver.getAvgUtil()
>>> print(f"Utilization at station 1: {U[0,:].sum()}")
avg_respt()

Get average response times.

Returns:

Response times matrix (M x K)

R[i,r] = average time spent at station i for class r Includes both service and waiting time

Return type:

R

Example

>>> R = solver.getAvgRespT()
>>> print(f"Response time at station 1, class 1: {R[0,0]}")
avg_resid_t()

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

Returns:

Residence times matrix (M x K)

Return type:

ResidT

avg_wait_t()

Get average waiting times.

Returns:

Waiting times matrix (M x K)

W[i,r] = R[i,r] - S[i,r] where S[i,r] is the mean service time W[i,r] = 0 for Delay (think time) stations

Return type:

W

Example

>>> W = solver.getAvgWaitT()
>>> print(f"Waiting time at station 1: {W[0,:].sum()}")
avg_tput()

Get average throughputs.

Returns:

Throughput matrix (M x K)

T[i,r] = average throughput at station i for class r jobs/time unit

Return type:

T

Example

>>> T = solver.getAvgTput()
>>> print(f"Throughput at station 1, class 1: {T[0,0]}")
avg_arv_r()

Get average arrival rates.

Returns:

Arrival rates matrix (M x K)

A[i,r] = arrival rate to station i for class r

Return type:

A

Note

For closed networks, arrival rates are derived from throughputs and visit ratios

avg_sys_resp_t()

Get system response times (cycle times) per CHAIN.

Returns:

Cycle time vector (C,), one entry per chain.

For a closed chain this is Little’s law on the chain population; for an open one the visit-weighted sum of per-class residence times. It is the CNchain of MATLAB @NetworkSolver/getAvgSys.m.

Return type:

C

avg_sys_tput()

Get system throughputs per CHAIN.

Returns:

System throughput vector (C,), one entry per chain: the rate of

completing classes routed back into that chain’s reference station. This is the XNchain of MATLAB @NetworkSolver/getAvgSys.m, which is what getAvgSysTput.m returns and what the JAR publishes as result.XN.

Return type:

X

Note

It used to return the analyzer’s per-CLASS result[‘XN’], which agrees with the above only when every chain holds one class – the common case, and the reason the difference went unseen.

run_analyzer()

Run the MVA analysis.

cdf_resp_t(R=None)

Get response time cumulative distribution function (CDF).

Uses exponential approximation: CDF(t) = 1 - exp(-t / E[R]) where E[R] is the mean response time from MVA.

Parameters:

R (ndarray | None) – Optional response times matrix (M x K) If None, uses results from runAnalyzer()

Returns:

List of dicts, one per (station, class) pair with service

Each dict contains: - ‘station’: Station index (1-based) - ‘class’: Job class (1-based) - ‘t’: Time points (100 points from 0.001 to 0.999 quantile) - ‘p’: CDF values at each time point

Return type:

RD

Notes

  • Uses exponential approximation for single-class exponential service

  • For multi-phase or non-exponential, this is approximate

  • Returns empty list for stations with zero response time

Example

>>> cdf_list = solver.getCdfRespT()
>>> for cdf_data in cdf_list:
...     station = cdf_data['station']
...     class_id = cdf_data['class']
...     print(f"Station {station}, Class {class_id}")
...     # Access t and p arrays for plotting
...     t = cdf_data['t']
...     p = cdf_data['p']
perct_resp_t(percentiles=None, jobclass=None, method='default')

Extract percentiles from response time distribution.

Computes percentile points from the exponential CDF approximation.

Parameters:
  • percentiles (List[float] | None) – List of percentiles to extract (0-100) Default: [10, 25, 50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class index to filter results (1-based) If None, returns all classes

Returns:

(PercRT, PercTable) where PercRT is a list of dicts with percentile data for each (station, class), each holding ‘station’ (station index), ‘class’ (job class), ‘percentiles’ (input percentile values) and ‘values’ (percentile response times), and PercTable is a pandas DataFrame with columns Station, Class, P10, P25, P50, P75, P90, P95, P99, …

Return type:

Tuple[List[Dict], DataFrame]

Algorithm:

For an exponential CDF with rate lambda = 1/E[R], the percentile p is t_p = -ln(1-p) * E[R] with p in [0,1].

Notes

Percentiles outside (0,100) are clipped, stations with zero response time give empty lists, and results match the exponential percentile formula.

Example:

>>> perc_list, perc_table = solver.getPerctRespT([90, 95, 99])
>>> print(perc_table)
>>> # Extract 90th percentile response time
>>> p90_values = perc_list[0]['values']
class SolverCTMC(model, method_or_options=None, **kwargs)[source]

Bases: FJTagTransformMixin, TransformSolveMixin, NetworkSolver

Native Python CTMC (Continuous-Time Markov Chain) solver.

This solver analyzes queueing networks through exact state-space enumeration using pure Python/NumPy, providing the same functionality as the Java wrapper without requiring the JVM.

Supported methods:
  • ‘default’: Basic state-space enumeration

  • ‘gpu’: the gpuArray backend of ctmc_solve, which falls back to the plain direct solve when no GPU is present

Parameters:
  • model – Network model (Python wrapper or native structure)

  • method – Solution method (default: ‘default’)

  • **kwargs – Additional solver options

isChainSolver()[source]

True when the solver was built from a MarkovProcess or a MarkovChain.

is_chain_solver()

True when the solver was built from a MarkovProcess or a MarkovChain.

isDiscreteChain()[source]

True in chain mode when the user supplied a DTMC (MarkovChain).

is_discrete_chain()

True in chain mode when the user supplied a DTMC (MarkovChain).

getTransMat()[source]

Transition matrix of the user-supplied DTMC (chain mode only).

get_trans_mat()

Transition matrix of the user-supplied DTMC (chain mode only).

reset()[source]

Clear cached results so the solver re-runs on next query.

supportsTransientAnalysis()[source]

Transient averages are available (uniformization of the generator over options.timespan).

supports_transient_analysis()

Transient averages are available (uniformization of the generator over options.timespan).

runAnalyzer()[source]

Run the CTMC analysis.

getAvgTable()[source]

Get comprehensive average performance metrics table.

Returns node-level results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork, ClassSwitch) are included with computed metrics. Cache nodes include HitClass/MissClass throughputs using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvgQLen()[source]

Get average queue lengths (M x K).

getAvgUtil()[source]

Get average utilizations (M x K).

getAvgRespT()[source]

Get average response times (M x K).

getAvgResidT()[source]

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

getAvgWaitT()[source]

Get average waiting times (M x K).

getAvgTput()[source]

Get average throughputs (M x K).

getAvgArvR()[source]

Get average arrival rates (M x K).

getAvgSysRespT()[source]

Get chain-level system response times (nchains,).

Uses the shared chain-based algorithm (a faithful port of MATLAB @NetworkSolver/getAvgSys.m): open chains sum alpha-weighted class residence times, closed chains apply Little’s law nJobsChain/XNchain.

getAvgSysTput()[source]

Get chain-level system (carried) throughputs (nchains,).

Matches MATLAB/JAR getAvgSys: the throughput of completing classes routed back into the chain reference station (carried rate), not the offered/source arrival rate.

getStateSpace()[source]

Get the enumerated state space.

Returns:

(stateSpace, localStateSpace) where stateSpace is the global

state matrix and localStateSpace is a list of per-station state arrays. For FCFS queues with multiple servers, localStateSpace includes buffer and phase columns (matching MATLAB’s nodeStateSpace format).

Return type:

tuple

getSteadyState()[source]

Get the steady-state probability distribution.

getInfGen()[source]

Get the infinitesimal generator matrix.

getCdfRespT(R=None)[source]

Response-time distribution by tagged-chain analysis.

One job of each chain is tagged, the tagged model is solved with its event filtration kept, and for each station the arrival and departure events OF THE TAGGED JOB split the generator into TWO maps:

A = map_normalize(Q - A1, A1) A1: tagged job arrives at station D = map_normalize(Q - D1, D1) D1: tagged job departs station pie = map_pie(A) the state seen ON ARRIVAL F(t) = 1 - pie expm(D.D0 t) 1

The two maps are not interchangeable: pie must come from the ARRIVAL map, and D0 from the DEPARTURE one.

THIS REPLACED AN EXPONENTIAL FIT that returned 1 - exp(-t/R) from the mean response time, with no tagging and no filtration, and was therefore exact only for an M/M/1.

Reference: matlab/src/solvers/CTMC/@SolverCTMC/getCdfRespT.m.

Returns:

List of dicts with ‘station’, ‘class’, ‘t’, ‘p’ keys

Return type:

List[Dict]

getPerctRespT(percentiles=None, jobclass=None)[source]

Extract percentiles from response time distribution.

Parameters:
  • percentiles (List[float] | None) – List of percentiles (0-100). Default: [10, 25, 50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class filter (1-based)

Returns:

Tuple of (percentile_list, percentile_table)

Return type:

Tuple[List[Dict], DataFrame]

getProbAggr(ist)[source]

Get probability of a specific per-class job distribution at a station.

Returns P(n1 jobs of class 1, n2 jobs of class 2, …) for the state that was set via setState() on the station.

Matches MATLAB: solver_ctmc_margaggr.m

Parameters:

ist – Station index (0-based) or node object

Returns:

Probability that station ist is in the specified state (scalar).

Return type:

float

getProbSysAggr()[source]

Get probability of the entire system being in the specified aggregated state.

Returns the joint probability of the system being in the aggregated state configuration set via setState() on all stations.

Matches MATLAB: solver_ctmc_jointaggr.m

Returns:

Joint probability of the system state.

Return type:

float

getProbSys()[source]

Get joint probability for the detailed (non-aggregated) system state.

Matches MATLAB: solver_ctmc_joint.m

In chain mode this returns the stationary vector of the user-supplied chain, one entry per state of the chain state space.

Returns:

Joint probability of the detailed system state.

Return type:

float

getProb(station=None)[source]

Get probability for the detailed state at station.

Returns the probability that the station is in the state that was set via setState(). This includes phase information from service distributions.

Matches MATLAB: solver_ctmc_marg.m

In chain mode the argument is a state of the user-supplied chain: a row of its state space, or a 1-based state index when the chain carries none.

Parameters:

station – Station index (0-based) or node object. If None, returns steady-state.

Returns:

Probability that station is in the specified detailed state.

Return type:

float

getGenerator()[source]

Get the infinitesimal generator matrix and event filters.

Returns:

(infGen, eventFilt) where infGen is the infinitesimal generator

matrix and eventFilt is a dictionary mapping event types to sparse matrices

Return type:

tuple

getAsymptoticVariance(f)[source]

The asymptotic variance of the time-average of a reward f along a sample path of this model’s CTMC.

WHAT IT IS FOR. A simulation estimate of a steady-state mean has a standard error that shrinks like sqrt(sigma^2/t), where sigma^2 is NOT the stationary variance of f but its ASYMPTOTIC variance, which also carries the autocorrelation of the path. That number is what says how long a run has to be, and sim_runlength() turns it into a run length for a target precision. It cannot be guessed from the stationary variance: on M/M/1 the two differ by a factor that blows up like (1-rho)^-2.

Parameters:

f – one reward value per CTMC state, in the state order getGenerator() returns, or a callable applied to each row of the state space

Returns:

mean, variance, asymptoticVariance and the deviation vector.

Return type:

The dict of sim_asymvar_ctmc()

References

W. Whitt (1989). Planning queueing simulations. Management Science 35(11), 1341-1366.

getStartRate()[source]

(nstations x nclasses) rate at which a class-r job BEGINS or RESUMES holding a server at station i, i.e. pi*F*e over the START filtration.

At a lossless station with no in-service abandonment

getStartRate == getAvgTput + getPreemptRate

because every job starts service once per entry into a server and every preemption is followed by exactly one later resume or restart. At a non-preemptive station this collapses to startRate == throughput.

An accessor, not a MetricType: it adds no getAvgTable column.

getPreemptRate()[source]

(nstations x nclasses) rate at which a class-r job HOLDING A SERVER at station i is pushed back into the buffer. Identically zero at a non-preemptive station; preempt-resume and preempt-independent stations report the SAME rate, since which phase the displaced job resumes in is not a property of how often it is displaced.

getEventFiltration(event_type)[source]

Filtration of a DERIVED event type, indexed [station][class]: the (s,ns) entry is the rate at which the transition s -> ns carries one such event at that station for that class.

EVENT_TYPE must be EventType.START or EventType.PREEMPT. The two are not synchronizations: they are tags on the ARV and DEP arcs that cause them, so they are NOT part of the event filtration getGenerator returns (which pairs one-to-one with sn.sync and is summed as D1) and are kept here.

getStateSpaceAggr()[source]

Get aggregated state space (jobs per station per class).

Returns:

Array of shape (nstates, nstations * nclasses) where column (ist * nclasses + k) = jobs of class k at station ist (0-indexed)

Return type:

ndarray

getCdfSysRespT()[source]

The SYSTEM response-time distribution: one law per CHAIN.

THE QUANTITY IS THE CYCLE TIME. The split is the tagged job’s ARRIVAL AT ITS OWN REFERENCE STATION, so a passage runs from one such arrival to the next: the job’s whole trip round the network, not its stay at one station. A single MAP suffices here where getCdfRespT needs two, because the arrival that starts the passage and the one that ends it are the same event.

THIS REPLACED AN EXPONENTIAL FIT to the mean system response time, which returned one entry per CLASS. The law is per CHAIN, as it is in MATLAB (RD = cell(1, sn.nchains)) and C++, so the ‘chain’ key replaces ‘class’.

Two constants differ from the per-station getter on purpose, matching the reference: the grid is 10000 intervals rather than 100000, and the truncation is at 1 - 1e-8 rather than 1e-3, because a cycle time is longer and its tail matters more.

Reference: matlab/src/solvers/CTMC/@SolverCTMC/getCdfSysRespT.m.

Returns:

List of dicts with ‘chain’, ‘t’, ‘p’ keys

Return type:

List[Dict]

getReward(reward_vector=None)[source]

Compute reward function over steady-state distribution.

Parameters:

reward_vector (ndarray | None) – Reward for each state. If None, uses queue length.

Returns:

Expected reward

Return type:

float

getAvgReward()[source]

Get steady-state expected reward values.

Computes the steady-state expected reward for reward functions previously defined using model.setReward().

Returns:

  • R: numpy array of expected reward values

  • names: list of reward function names

Return type:

Tuple of (R, names) where

Example

>>> model.setReward('QueueLength', lambda state: state.at(queue, oclass))
>>> solver = CTMC(model)
>>> R, names = solver.getAvgReward()
get_avg_reward()

Get steady-state expected reward values.

Computes the steady-state expected reward for reward functions previously defined using model.setReward().

Returns:

  • R: numpy array of expected reward values

  • names: list of reward function names

Return type:

Tuple of (R, names) where

Example

>>> model.setReward('QueueLength', lambda state: state.at(queue, oclass))
>>> solver = CTMC(model)
>>> R, names = solver.getAvgReward()
getTranCdfRespT(t_max=10.0, n_points=100)[source]

Not supported, as in the reference, whose base class raises.

Returning the steady-state law under the transient getter’s name would be indistinguishable, to the caller, from a transient analysis.

getTranProb(node, t=1.0)[source]

Get transient state probabilities at a node.

Computes π(t) = π(0) * exp(Q*t) using matrix exponential.

Parameters:
  • node (int) – Node/station index (0-based)

  • t (float) – Time point for transient analysis

Returns:

Transient probability vector at time t

Return type:

ndarray

getTranProbAggr(node, t=1.0)[source]

Get transient aggregated state probabilities at a node.

Parameters:
  • node (int) – Node/station index (0-based)

  • t (float) – Time point for transient analysis

Returns:

Transient aggregated probability vector at time t

Return type:

ndarray

getTranProbSys(t=1.0)[source]

Get transient system state probabilities.

Computes full system state probability at time t.

In chain mode the distribution starts from options.init_sol, or from the uniform distribution when none is given; a DTMC advances one step per unit of time, so t must then be a non-negative integer.

Parameters:

t (float) – Time point for transient analysis

Returns:

Transient system probability vector at time t

Return type:

ndarray

getTranProbSysAggr(t=1.0)[source]

Get transient aggregated system state probabilities.

Parameters:

t (float) – Time point for transient analysis

Returns:

Transient system probability vector at time t

Return type:

ndarray

getSymbolicGenerator(invert_symbol=False)[source]

Get symbolic generator matrix with per-event symbolic variables.

Each event filtration matrix is normalized and multiplied by a symbolic variable (x1, x2, …), matching MATLAB’s getSymbolicGenerator.m.

Parameters:

invert_symbol (bool) – If True, divide by symbol instead of multiplying

Returns:

  • infGen: Symbolic infinitesimal generator (sympy.Matrix)

  • eventFilt: List of per-event symbolic filtration matrices (None for events with no positive rates, matching MATLAB’s empty cells)

  • syncInfo: Sync data structure from the model

  • stateSpace: State space matrix

  • nodeStateSpace: Per-node state space

Return type:

Tuple of (infGen, eventFilt, syncInfo, stateSpace, nodeStateSpace)

get_symbolic_generator(invert_symbol=False)

Get symbolic generator matrix with per-event symbolic variables.

Each event filtration matrix is normalized and multiplied by a symbolic variable (x1, x2, …), matching MATLAB’s getSymbolicGenerator.m.

Parameters:

invert_symbol (bool) – If True, divide by symbol instead of multiplying

Returns:

  • infGen: Symbolic infinitesimal generator (sympy.Matrix)

  • eventFilt: List of per-event symbolic filtration matrices (None for events with no positive rates, matching MATLAB’s empty cells)

  • syncInfo: Sync data structure from the model

  • stateSpace: State space matrix

  • nodeStateSpace: Per-node state space

Return type:

Tuple of (infGen, eventFilt, syncInfo, stateSpace, nodeStateSpace)

symbolicBackend()[source]

Value of options.config[‘symbolic’], or ‘auto’ when unset.

‘auto’ keeps the native engine (sympy), exactly as MATLAB’s ‘auto’ keeps the Symbolic Math Toolbox when it is licensed.

symbolic_backend()

Value of options.config[‘symbolic’], or ‘auto’ when unset.

‘auto’ keeps the native engine (sympy), exactly as MATLAB’s ‘auto’ keeps the Symbolic Math Toolbox when it is licensed.

getSymbolicSolution()[source]

Symbolic stationary distribution as a function of x1, …, xE.

The solution of pi*Q = 0 with sum(pi) = 1 over the field of rational functions in the event rate symbols. Port of MATLAB @SolverCTMC/getSymbolicSolution.m and the twin of the JAR’s SolverCTMC.getSymbolicSolution.

The generator is assembled by getSymbolicGenerator, which needs no computer algebra because it is linear in the symbols. Solving with it does, and that is delegated to whatever ctmc_solve resolves from options.config[‘symbolic’]: sympy locally, or the line-sage-rest service when the backend names it.

The expressions are not comparable with another codebase’s BY TEXT: symbol numbering follows event enumeration order and the printed normal form depends on the engine. Substitute rates and compare numbers.

Returns:

pi the stationary law as a sympy row, num and den the same vector over one common denominator, and stateSpace the rows pi is indexed by.

Return type:

tuple (pi, num, den, stateSpace)

get_symbolic_solution()

Symbolic stationary distribution as a function of x1, …, xE.

The solution of pi*Q = 0 with sum(pi) = 1 over the field of rational functions in the event rate symbols. Port of MATLAB @SolverCTMC/getSymbolicSolution.m and the twin of the JAR’s SolverCTMC.getSymbolicSolution.

The generator is assembled by getSymbolicGenerator, which needs no computer algebra because it is linear in the symbols. Solving with it does, and that is delegated to whatever ctmc_solve resolves from options.config[‘symbolic’]: sympy locally, or the line-sage-rest service when the backend names it.

The expressions are not comparable with another codebase’s BY TEXT: symbol numbering follows event enumeration order and the printed normal form depends on the engine. Substitute rates and compare numbers.

Returns:

pi the stationary law as a sympy row, num and den the same vector over one common denominator, and stateSpace the rows pi is indexed by.

Return type:

tuple (pi, num, den, stateSpace)

getCdfFirstPassT(A, B)[source]

Distribution of the FIRST PASSAGE TIME from state set A into set B.

Mirrors MATLAB @SolverCTMC/getCdfFirstPassT.m. RD is an (n, 2) array whose first column is F(t) and whose second is t, the column order every other CDF getter in LINE uses.

A and B name states either as 1-based ROW INDICES into the state space returned by getStateSpace, or as matrices of state rows, which are resolved against that space. An empty A starts from the conditional stationary law on the complement of B.

THIS IS NOT getCdfRespT. That getter times a tagged job between an arrival at a station and its departure, through the event filtration; this one times the chain between two sets of states the caller names, and answers questions the filtration cannot express – the writer cycle time of a readers-writers model, the time to fill a buffer, the time to leave a degraded region.

Parameters:
  • A – source state set, or empty for the conditional stationary law

  • B – target state set, which may not be empty

Returns:

(RD, out) with RD the (n, 2) [F(t), t] array and out the dict returned by ctmc_passage_time, extended with tset, density, source, target and runtime.

References

P. G. Harrison and W. J. Knottenbelt, “Passage Time Distributions in Large Markov Chains”, 2002.

get_cdf_first_pass_t(A, B)

Distribution of the FIRST PASSAGE TIME from state set A into set B.

Mirrors MATLAB @SolverCTMC/getCdfFirstPassT.m. RD is an (n, 2) array whose first column is F(t) and whose second is t, the column order every other CDF getter in LINE uses.

A and B name states either as 1-based ROW INDICES into the state space returned by getStateSpace, or as matrices of state rows, which are resolved against that space. An empty A starts from the conditional stationary law on the complement of B.

THIS IS NOT getCdfRespT. That getter times a tagged job between an arrival at a station and its departure, through the event filtration; this one times the chain between two sets of states the caller names, and answers questions the filtration cannot express – the writer cycle time of a readers-writers model, the time to fill a buffer, the time to leave a degraded region.

Parameters:
  • A – source state set, or empty for the conditional stationary law

  • B – target state set, which may not be empty

Returns:

(RD, out) with RD the (n, 2) [F(t), t] array and out the dict returned by ctmc_passage_time, extended with tset, density, source, target and runtime.

References

P. G. Harrison and W. J. Knottenbelt, “Passage Time Distributions in Large Markov Chains”, 2002.

getFirstPassTMoments(A, B, nmax=3)[source]

Moments of order 1..nmax of the first passage time from A into B.

Mirrors MATLAB @SolverCTMC/getFirstPassTMoments.m.

NO TRANSFORM INVERSION AND NO TIME GRID ARE INVOLVED. The moments come from Eq. 3 of Harrison and Knottenbelt (2002) – one linear solve per order – so they are exact and are not limited by the horizon a CDF would have to be truncated at. This is the cheapest way to get the variance or the skewness of a passage time in LINE.

Parameters:
  • A – source state set, named as in getCdfFirstPassT

  • B – target state set, which may not be empty

  • nmax (int) – highest moment order, default 3

Returns:

(m, mall) with m the (nmax,) moment vector for a passage started uniformly in A, and mall (nstates, nmax) one row per starting state, zero on B and inf where B cannot be reached.

get_first_pass_t_moments(A, B, nmax=3)

Moments of order 1..nmax of the first passage time from A into B.

Mirrors MATLAB @SolverCTMC/getFirstPassTMoments.m.

NO TRANSFORM INVERSION AND NO TIME GRID ARE INVOLVED. The moments come from Eq. 3 of Harrison and Knottenbelt (2002) – one linear solve per order – so they are exact and are not limited by the horizon a CDF would have to be truncated at. This is the cheapest way to get the variance or the skewness of a passage time in LINE.

Parameters:
  • A – source state set, named as in getCdfFirstPassT

  • B – target state set, which may not be empty

  • nmax (int) – highest moment order, default 3

Returns:

(m, mall) with m the (nmax,) moment vector for a passage started uniformly in A, and mall (nstates, nmax) one row per starting state, zero on B and inf where B cannot be reached.

getSensitivity(param, reward=None, method='fd')[source]

Parametric sensitivity of a steady-state reward to a scalar model parameter, following Trivedi and Bobbio (2017), Sec. 9.7.

Mirrors MATLAB @SolverCTMC/getSensitivity.m.

Parameters:
  • param – dict describing the parameter theta and how to set it: name identifier used in reports; value nominal value theta; set callable (model, value) -> None applying theta; step optional finite-difference step, default value*1e-6.

  • reward – reward rate vector over the states, or a callable mapping the state space to one. If omitted, dpi is returned and S is None.

  • method (str) –

    ‘fd’ (default) or ‘symbolic’.

    ’fd’ obtains the generator derivative dQ/dtheta by central differences on the rate with the state space held fixed. This is exact to O(step^2) and requires no symbolic differentiation of the rate assembly; the state space is unaffected because it depends on the topology and the cutoff, not on rate values. The steady-state sensitivity then follows from one linear solve, see ctmc_sens.

    ’symbolic’ solves the stationary distribution as a rational function of the event rate symbols x1..xE and differentiates it exactly with respect to each of them, then combines by the chain rule:

    d(pi)/d(theta) = sum_e d(pi)/d(x_e) * d(x_e)/d(theta).
    

    Only the rate map x_e(theta) is still differenced, and that map is affine in theta in the common cases (a rate set to theta, or scaled by it), where the central difference reproduces it exactly. The whole O(step^2) error of ‘fd’ comes from differencing through the solve, which this avoids entirely. It refuses rather than approximates when perturbing theta reshapes an event’s filtration instead of scaling it.

Returns:

(S, SS, dpi, pi) with S the unscaled sensitivity d(E[r])/dtheta, Eq. (9.79); SS the scaled sensitivity (theta/E[r]) d(E[r])/dtheta, Eq. (9.80); dpi the sensitivity of the steady-state distribution; pi the steady-state distribution.

Note

This returns d(E[r])/dtheta with dr/dtheta = 0, i.e. it assumes the reward rates do not themselves depend on theta. Rewards that depend on theta need the second term of Eq. (9.83) and are not handled here.

get_sensitivity(param, reward=None, method='fd')

Parametric sensitivity of a steady-state reward to a scalar model parameter, following Trivedi and Bobbio (2017), Sec. 9.7.

Mirrors MATLAB @SolverCTMC/getSensitivity.m.

Parameters:
  • param – dict describing the parameter theta and how to set it: name identifier used in reports; value nominal value theta; set callable (model, value) -> None applying theta; step optional finite-difference step, default value*1e-6.

  • reward – reward rate vector over the states, or a callable mapping the state space to one. If omitted, dpi is returned and S is None.

  • method (str) –

    ‘fd’ (default) or ‘symbolic’.

    ’fd’ obtains the generator derivative dQ/dtheta by central differences on the rate with the state space held fixed. This is exact to O(step^2) and requires no symbolic differentiation of the rate assembly; the state space is unaffected because it depends on the topology and the cutoff, not on rate values. The steady-state sensitivity then follows from one linear solve, see ctmc_sens.

    ’symbolic’ solves the stationary distribution as a rational function of the event rate symbols x1..xE and differentiates it exactly with respect to each of them, then combines by the chain rule:

    d(pi)/d(theta) = sum_e d(pi)/d(x_e) * d(x_e)/d(theta).
    

    Only the rate map x_e(theta) is still differenced, and that map is affine in theta in the common cases (a rate set to theta, or scaled by it), where the central difference reproduces it exactly. The whole O(step^2) error of ‘fd’ comes from differencing through the solve, which this avoids entirely. It refuses rather than approximates when perturbing theta reshapes an event’s filtration instead of scaling it.

Returns:

(S, SS, dpi, pi) with S the unscaled sensitivity d(E[r])/dtheta, Eq. (9.79); SS the scaled sensitivity (theta/E[r]) d(E[r])/dtheta, Eq. (9.80); dpi the sensitivity of the steady-state distribution; pi the steady-state distribution.

Note

This returns d(E[r])/dtheta with dr/dtheta = 0, i.e. it assumes the reward rates do not themselves depend on theta. Rewards that depend on theta need the second term of Eq. (9.83) and are not handled here.

getMarkedCTMC()[source]

Get a marked CTMC object representation.

Returns a dictionary containing the CTMC with marked transitions for reward and passage time analysis.

Returns:

Dictionary with ‘Q’ (generator), ‘space’ (state space), ‘pi’ (steady-state), and ‘marks’ (transition markings)

Return type:

Dict[str, Any]

runRewardAnalyzer(reward_vector=None)[source]

Run reward analysis on the CTMC.

Computes expected rewards in steady-state and optionally transient.

Parameters:

reward_vector (ndarray | None) – Reward for each state. If None, uses queue length.

Returns:

Dictionary with ‘steady_state_reward’, ‘reward_per_state’, etc.

Return type:

Dict[str, Any]

getTranReward(t=1.0, reward_vector=None)[source]

Get transient reward at time t.

Computes expected reward at time t using matrix exponential.

Parameters:
  • t (float) – Time point for transient analysis

  • reward_vector (ndarray | None) – Reward for each state. If None, uses queue length.

Returns:

Expected reward at time t

Return type:

float

get_tran_reward(name=None)[source]

Transient expected reward E[r(X(t))] over time for each reward.

This is the transient counterpart of get_avg_reward: instead of the equilibrium value it returns the time-indexed trajectory E[r(X(t))], where X(t) is the system state at time t and the expectation is taken over the CTMC transient distribution starting from the initial state. The named reward functions defined via model.setReward are evaluated on the aggregated state space. A finite timespan is required, e.g. CTMC(model, timespan=[0, T]).

Parameters:

name (str | None) – optional reward name; if given, only that reward is returned.

Returns:

Tuple (Rt, t, names) where Rt is a list of dicts with keys ‘t’, ‘metric’, ‘name’ (one per reward), or a single dict when name is specified; t is the array of time points; names is the list of reward names (or a single name when name is specified).

reward_matrix_over(space, rewards_dict, nstates=None)[source]

The (nrewards x nstates) matrix of the declared rewards on space.

The reward map is a function of the AGGREGATE state row and of nothing else, so the same evaluation serves whichever engine produced the space: the native transient below reads it off self._result, and the lang=’cpp’ path reads it off line-cli’s labelsAggr. Keeping one evaluation is what makes a bare callable answer identically under both, since no wire format can carry the callable itself.

GetGenerator()

Get the infinitesimal generator matrix and event filters.

Returns:

(infGen, eventFilt) where infGen is the infinitesimal generator

matrix and eventFilt is a dictionary mapping event types to sparse matrices

Return type:

tuple

GetStateSpaceAggr()

Get aggregated state space (jobs per station per class).

Returns:

Array of shape (nstates, nstations * nclasses) where column (ist * nclasses + k) = jobs of class k at station ist (0-indexed)

Return type:

ndarray

GetProb(station=None)

Get probability for the detailed state at station.

Returns the probability that the station is in the state that was set via setState(). This includes phase information from service distributions.

Matches MATLAB: solver_ctmc_marg.m

In chain mode the argument is a state of the user-supplied chain: a row of its state space, or a 1-based state index when the chain carries none.

Parameters:

station – Station index (0-based) or node object. If None, returns steady-state.

Returns:

Probability that station is in the specified detailed state.

Return type:

float

GetCdfSysRespT()

The SYSTEM response-time distribution: one law per CHAIN.

THE QUANTITY IS THE CYCLE TIME. The split is the tagged job’s ARRIVAL AT ITS OWN REFERENCE STATION, so a passage runs from one such arrival to the next: the job’s whole trip round the network, not its stay at one station. A single MAP suffices here where getCdfRespT needs two, because the arrival that starts the passage and the one that ends it are the same event.

THIS REPLACED AN EXPONENTIAL FIT to the mean system response time, which returned one entry per CLASS. The law is per CHAIN, as it is in MATLAB (RD = cell(1, sn.nchains)) and C++, so the ‘chain’ key replaces ‘class’.

Two constants differ from the per-station getter on purpose, matching the reference: the grid is 10000 intervals rather than 100000, and the truncation is at 1 - 1e-8 rather than 1e-3, because a cycle time is longer and its tail matters more.

Reference: matlab/src/solvers/CTMC/@SolverCTMC/getCdfSysRespT.m.

Returns:

List of dicts with ‘chain’, ‘t’, ‘p’ keys

Return type:

List[Dict]

GetReward(reward_vector=None)

Compute reward function over steady-state distribution.

Parameters:

reward_vector (ndarray | None) – Reward for each state. If None, uses queue length.

Returns:

Expected reward

Return type:

float

GetAvgReward()

Get steady-state expected reward values.

Computes the steady-state expected reward for reward functions previously defined using model.setReward().

Returns:

  • R: numpy array of expected reward values

  • names: list of reward function names

Return type:

Tuple of (R, names) where

Example

>>> model.setReward('QueueLength', lambda state: state.at(queue, oclass))
>>> solver = CTMC(model)
>>> R, names = solver.getAvgReward()
GetTranCdfRespT(t_max=10.0, n_points=100)

Not supported, as in the reference, whose base class raises.

Returning the steady-state law under the transient getter’s name would be indistinguishable, to the caller, from a transient analysis.

GetTranProb(node, t=1.0)

Get transient state probabilities at a node.

Computes π(t) = π(0) * exp(Q*t) using matrix exponential.

Parameters:
  • node (int) – Node/station index (0-based)

  • t (float) – Time point for transient analysis

Returns:

Transient probability vector at time t

Return type:

ndarray

GetTranProbAggr(node, t=1.0)

Get transient aggregated state probabilities at a node.

Parameters:
  • node (int) – Node/station index (0-based)

  • t (float) – Time point for transient analysis

Returns:

Transient aggregated probability vector at time t

Return type:

ndarray

GetTranProbSys(t=1.0)

Get transient system state probabilities.

Computes full system state probability at time t.

In chain mode the distribution starts from options.init_sol, or from the uniform distribution when none is given; a DTMC advances one step per unit of time, so t must then be a non-negative integer.

Parameters:

t (float) – Time point for transient analysis

Returns:

Transient system probability vector at time t

Return type:

ndarray

GetTranProbSysAggr(t=1.0)

Get transient aggregated system state probabilities.

Parameters:

t (float) – Time point for transient analysis

Returns:

Transient system probability vector at time t

Return type:

ndarray

GetSymbolicGenerator(invert_symbol=False)

Get symbolic generator matrix with per-event symbolic variables.

Each event filtration matrix is normalized and multiplied by a symbolic variable (x1, x2, …), matching MATLAB’s getSymbolicGenerator.m.

Parameters:

invert_symbol (bool) – If True, divide by symbol instead of multiplying

Returns:

  • infGen: Symbolic infinitesimal generator (sympy.Matrix)

  • eventFilt: List of per-event symbolic filtration matrices (None for events with no positive rates, matching MATLAB’s empty cells)

  • syncInfo: Sync data structure from the model

  • stateSpace: State space matrix

  • nodeStateSpace: Per-node state space

Return type:

Tuple of (infGen, eventFilt, syncInfo, stateSpace, nodeStateSpace)

GetMarkedCTMC()

Get a marked CTMC object representation.

Returns a dictionary containing the CTMC with marked transitions for reward and passage time analysis.

Returns:

Dictionary with ‘Q’ (generator), ‘space’ (state space), ‘pi’ (steady-state), and ‘marks’ (transition markings)

Return type:

Dict[str, Any]

RunRewardAnalyzer(reward_vector=None)

Run reward analysis on the CTMC.

Computes expected rewards in steady-state and optionally transient.

Parameters:

reward_vector (ndarray | None) – Reward for each state. If None, uses queue length.

Returns:

Dictionary with ‘steady_state_reward’, ‘reward_per_state’, etc.

Return type:

Dict[str, Any]

GetTranReward(t=1.0, reward_vector=None)

Get transient reward at time t.

Computes expected reward at time t using matrix exponential.

Parameters:
  • t (float) – Time point for transient analysis

  • reward_vector (ndarray | None) – Reward for each state. If None, uses queue length.

Returns:

Expected reward at time t

Return type:

float

getAvgQLenChain()[source]

Get average queue lengths aggregated by chain.

getAvgUtilChain()[source]

Get average utilizations aggregated by chain.

getAvgRespTChain()[source]

Get average response times aggregated by chain.

getAvgResidTChain()[source]

Get average residence times aggregated by chain.

getAvgTputChain()[source]

Get average throughputs aggregated by chain.

getAvgArvRChain()[source]

Get average arrival rates aggregated by chain.

getAvgChain()[source]

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgChainTable()[source]

Get average metrics by chain as DataFrame.

getAvgNode()[source]

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Cache). For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgNodeTable()[source]

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvgCacheTable()[source]

Detailed per-class cache performance metrics (see cache_table).

get_avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

getAvgItemTable()[source]

Item-level cache occupancy table (see cache_table).

get_avg_item_table()

Item-level cache occupancy table (see cache_table).

avg_item_table()

Item-level cache occupancy table (see cache_table).

getAvgNodeChain()[source]

Get average metrics by node and chain.

getAvgNodeChainTable()[source]

Get average metrics by node and chain as DataFrame.

getAvgNodeQLenChain()[source]

Get average queue lengths by node aggregated by chain.

getAvgNodeUtilChain()[source]

Get average utilizations by node aggregated by chain.

getAvgNodeRespTChain()[source]

Get average response times by node aggregated by chain.

getAvgNodeResidTChain()[source]

Get average residence times by node aggregated by chain.

getAvgNodeTputChain()[source]

Get average throughputs by node aggregated by chain.

getAvgNodeArvRChain()[source]

Get average arrival rates by node aggregated by chain.

getTranAvg(*args)[source]

Get transient average metrics.

Computes time-dependent queue lengths, utilizations, and throughputs using transient CTMC analysis with matrix exponential method.

Supports state prior iteration: when the model has multiple possible initial states (e.g., uniform prior from initFromMarginal + setStatePrior), runs the transient analysis for each state weighted by its prior probability. Matches MATLAB SolverCTMC/runAnalyzer.m lines 114-183.

Parameters:

*args – Optional transient handles (Qt, Ut, Tt) for MATLAB API compatibility.

Returns:

Tuple of (QNt, UNt, TNt) where each is a nested list [M][K] of TranResult objects.

getAvgSys()[source]

Get system-level average metrics.

Returns:

Tuple of (R, T) where R is chain-level system response time and T is chain-level system (carried) throughput.

Return type:

Tuple[ndarray, ndarray]

getAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

sampleAggr(node, numEvents=1000)[source]

Sample aggregated states at node (not supported for CTMC).

Raises:

NotImplementedError – CTMC is an analytical solver

sampleSys(numEvents=1000)[source]

Sample system states.

In chain mode this returns a sample path of the user-supplied chain, started from options.init_sol when given and from the uniform distribution otherwise; a DTMC advances one unit of time per step. For a Network model CTMC is an analytical solver and sampling is refused.

Raises:

NotImplementedError – CTMC is an analytical solver on a Network model

sampleSysAggr(numEvents=1000)[source]

Sample aggregated system states using CTMC simulation.

Uses the MMAP (Marked Markovian Arrival Process) approach matching MATLAB’s sampleSysAggr. The CTMC generator is decomposed into event filter matrices (one per sync event) to build an MMAP, which is then sampled to produce exactly numEvents actual events (arrivals/departures).

When event filtration is not available, falls back to direct CTMC simulation with enough transitions to produce numEvents actual events detected via population changes.

Parameters:

numEvents (int) – Number of actual events (arrivals + departures) to generate

Returns:

SampleResult containing timestamps, states, and event information

Return type:

SampleResult

unsupportedMethodReason(method)[source]

The forwarding address for the QRF reduction bounds, SolverBA’s now.

Asks nothing of the model, which is what lets the name gate in runAnalyzerChecks call it; runAnalyzer reads the text from here too, so the two cannot drift into two answers.

unsupported_method_reason(method)

The forwarding address for the QRF reduction bounds, SolverBA’s now.

Asks nothing of the model, which is what lets the name gate in runAnalyzerChecks call it; runAnalyzer reads the text from here too, so the two cannot drift into two answers.

listValidMethods()[source]

List valid solution methods.

‘exact’ is an explicit alias for the default state-space path: it pins the intent at the call site so an example or test cannot be re-baselined by a later change of what ‘default’ selects. It must stay behaviourally identical to ‘default’.

‘gpu’ NAMES A BACKEND AND FALLS BACK, which is what the reference does: ctmc_solve.m wraps the gpuArray solve in a try/catch and runs the plain direct solve when no GPU is present, so SolverCTMC(model,’gpu’) returns the exact answer on a host without one. This list used to name ‘basic’ instead – a spelling no other codebase knows – so ‘gpu’ was refused here and ‘basic’ was refused everywhere else.

‘mdd’ holds the reachable set in a decision diagram and solves K coupled level-CTMCs instead of the |S|-state generator; it is exact on product-form models and approximate otherwise, and is restricted to closed single-class networks (solver_ctmc_mdd_analyzer).

static getFeatureSet()[source]

Get supported features.

getMethodFeatureSet(method)[source]

Per-method feature deltas applied to the base CTMC envelope.

Four of the six methods share it; ‘cftp’/’cftp.approx’ and ‘mdd’ narrow it, because neither builds the explicit generator that carries the rest of the envelope. Mirrors MATLAB SolverCTMC.getMethodFeatureSet.

supportsModelMethod(method)[source]

The per-method rules the feature registry has no name for, asked of the SAME predicates the analyzers use so that the report and the run cannot answer differently.

Three of them: the class count and the station count that ‘cftp’ and ‘mdd’ need (a class count is not a model feature), and the state-space size that the explicit-generator methods need. The last one is why ‘default’/’exact’/’gpu’ were offered on models whose chain does not fit memory – the analyzer priced the state space and refused, and nothing above it had asked. Mirrors MATLAB @SolverCTMC/supportsModelMethod.

THE TWO STRUCTURAL PREDICATES ARE ASKED BEFORE THE FEATURE GATE, which is the reverse of the usual order and deliberate: each is the analyzer’s own assert, so it refuses a strict superset of what the per-method feature deltas refuse, and its wording names the offending station or class count instead of a feature. Asking the feature gate first would replace ‘the cftp method supports closed models only’ with ‘(feature: OpenClass)’ on the very run the caller is about to make.

static supports(model)[source]

Check if model is supported.

Mirrors MATLAB SolverCTMC.supports: gates the model’s used language features against getFeatureSet(). Struct-like inputs without a feature registry fall back to a structural sanity check.

static isStateSpaceTractable(model, options=None)[source]

Whether the worst-case CTMC state space of model fits memory.

Same estimator and gate the analyzer runs, exposed so a caller (e.g. SolverAUTO) can rank CTMC out before paying for state-space generation. Mirrors MATLAB SolverCTMC.isStateSpaceTractable and JAR SolverCTMC.isStateSpaceTractable.

Parameters:
  • model – the Network under analysis.

  • options – solver options carrying cutoff, force and safety fraction.

Returns:

(ok, message, log_nstates).

static defaultOptions()[source]

Get default solver options.

static printInfGen(infGen, stateSpace)[source]

Print the infinitesimal generator matrix in MATLAB-compatible format.

Output format matches MATLAB’s CTMC.printInfGen(): [from_state]->[to_state]: rate

Parameters:
  • infGen (ndarray) – Infinitesimal generator matrix

  • stateSpace (ndarray) – State space matrix

static print_inf_gen(infGen, stateSpace)

Print the infinitesimal generator matrix in MATLAB-compatible format.

Output format matches MATLAB’s CTMC.printInfGen(): [from_state]->[to_state]: rate

Parameters:
  • infGen (ndarray) – Infinitesimal generator matrix

  • stateSpace (ndarray) – State space matrix

static printEventFilt(eventFilt, SS, sync=None, events=None)[source]

Print non-zero transitions per event in the event filter matrices.

Output format matches MATLAB’s SolverCTMC.printEventFilt() and JAR’s SolverCTMC.printEventFilt().

Parameters:
  • eventFilt – List of event filter matrices (one per event).

  • SS – State space matrix (nstates x state_dim).

  • sync – Optional list of sync structures with active/passive node/class info.

  • events – Optional list of event indices to print (1-based for MATLAB compat). If None, prints all events.

static print_event_filt(eventFilt, SS, sync=None, events=None)

Print non-zero transitions per event in the event filter matrices.

Output format matches MATLAB’s SolverCTMC.printEventFilt() and JAR’s SolverCTMC.printEventFilt().

Parameters:
  • eventFilt – List of event filter matrices (one per event).

  • SS – State space matrix (nstates x state_dim).

  • sync – Optional list of sync structures with active/passive node/class info.

  • events – Optional list of event indices to print (1-based for MATLAB compat). If None, prints all events.

sample(node, numEvents)[source]

Sampling not supported by CTMC (analytical solver).

GetAvg()

Average station metrics (Q, U, R, T, A, W) as station x class matrices.

Single analyzer funnel of the native solvers, mirroring MATLAB @NetworkSolver/getAvg.m and JAR NetworkSolver.getAvg(): it runs the analyzer if there is no cached result, then reads the averages from whichever result store the solver uses. Every solver used to carry its own copy of this body, differing only in that store (‘_result’ vs ‘result’) and in the field naming (‘QN’ vs ‘Q’), which _AVG_FIELDS already reconciles; the duplication also meant there was no single place to intercept a solve, as the other two codebases have.

Returns:

queue lengths, utilizations, response times, throughputs, arrival rates and residence times.

Return type:

(Q, U, R, T, A, W)

GetAvgTable()

Get comprehensive average performance metrics table.

Returns node-level results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork, ClassSwitch) are included with computed metrics. Cache nodes include HitClass/MissClass throughputs using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

GetAvgQLen()

Get average queue lengths (M x K).

GetAvgUtil()

Get average utilizations (M x K).

GetAvgRespT()

Get average response times (M x K).

GetAvgResidT()

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

GetAvgWaitT()

Get average waiting times (M x K).

GetAvgTput()

Get average throughputs (M x K).

GetAvgArvR()

Get average arrival rates (M x K).

GetAvgSysRespT()

Get chain-level system response times (nchains,).

Uses the shared chain-based algorithm (a faithful port of MATLAB @NetworkSolver/getAvgSys.m): open chains sum alpha-weighted class residence times, closed chains apply Little’s law nJobsChain/XNchain.

GetAvgSysTput()

Get chain-level system (carried) throughputs (nchains,).

Matches MATLAB/JAR getAvgSys: the throughput of completing classes routed back into the chain reference station (carried rate), not the offered/source arrival rate.

GetStateSpace()

Get the enumerated state space.

Returns:

(stateSpace, localStateSpace) where stateSpace is the global

state matrix and localStateSpace is a list of per-station state arrays. For FCFS queues with multiple servers, localStateSpace includes buffer and phase columns (matching MATLAB’s nodeStateSpace format).

Return type:

tuple

GetSteadyState()

Get the steady-state probability distribution.

GetInfGen()

Get the infinitesimal generator matrix.

GetCdfRespT(R=None)

Response-time distribution by tagged-chain analysis.

One job of each chain is tagged, the tagged model is solved with its event filtration kept, and for each station the arrival and departure events OF THE TAGGED JOB split the generator into TWO maps:

A = map_normalize(Q - A1, A1) A1: tagged job arrives at station D = map_normalize(Q - D1, D1) D1: tagged job departs station pie = map_pie(A) the state seen ON ARRIVAL F(t) = 1 - pie expm(D.D0 t) 1

The two maps are not interchangeable: pie must come from the ARRIVAL map, and D0 from the DEPARTURE one.

THIS REPLACED AN EXPONENTIAL FIT that returned 1 - exp(-t/R) from the mean response time, with no tagging and no filtration, and was therefore exact only for an M/M/1.

Reference: matlab/src/solvers/CTMC/@SolverCTMC/getCdfRespT.m.

Returns:

List of dicts with ‘station’, ‘class’, ‘t’, ‘p’ keys

Return type:

List[Dict]

GetPerctRespT(percentiles=None, jobclass=None)

Extract percentiles from response time distribution.

Parameters:
  • percentiles (List[float] | None) – List of percentiles (0-100). Default: [10, 25, 50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class filter (1-based)

Returns:

Tuple of (percentile_list, percentile_table)

Return type:

Tuple[List[Dict], DataFrame]

ListValidMethods()

List valid solution methods.

‘exact’ is an explicit alias for the default state-space path: it pins the intent at the call site so an example or test cannot be re-baselined by a later change of what ‘default’ selects. It must stay behaviourally identical to ‘default’.

‘gpu’ NAMES A BACKEND AND FALLS BACK, which is what the reference does: ctmc_solve.m wraps the gpuArray solve in a try/catch and runs the plain direct solve when no GPU is present, so SolverCTMC(model,’gpu’) returns the exact answer on a host without one. This list used to name ‘basic’ instead – a spelling no other codebase knows – so ‘gpu’ was refused here and ‘basic’ was refused everywhere else.

‘mdd’ holds the reachable set in a decision diagram and solves K coupled level-CTMCs instead of the |S|-state generator; it is exact on product-form models and approximate otherwise, and is restricted to closed single-class networks (solver_ctmc_mdd_analyzer).

static GetFeatureSet()

Get supported features.

static Supports(model)

Check if model is supported.

Mirrors MATLAB SolverCTMC.supports: gates the model’s used language features against getFeatureSet(). Struct-like inputs without a feature registry fall back to a structural sanity check.

static DefaultOptions()

Get default solver options.

static default_options()

Get default solver options.

GetAvgChain()

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

GetAvgChainTable()

Get average metrics by chain as DataFrame.

GetAvgQLenChain()

Get average queue lengths aggregated by chain.

GetAvgUtilChain()

Get average utilizations aggregated by chain.

GetAvgRespTChain()

Get average response times aggregated by chain.

GetAvgResidTChain()

Get average residence times aggregated by chain.

GetAvgTputChain()

Get average throughputs aggregated by chain.

GetAvgArvRChain()

Get average arrival rates aggregated by chain.

GetAvgNode()

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Cache). For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

GetAvgNodeTable()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

GetAvgNodeChain()

Get average metrics by node and chain.

GetAvgNodeChainTable()

Get average metrics by node and chain as DataFrame.

GetAvgNodeQLenChain()

Get average queue lengths by node aggregated by chain.

GetAvgNodeUtilChain()

Get average utilizations by node aggregated by chain.

GetAvgNodeRespTChain()

Get average response times by node aggregated by chain.

GetAvgNodeResidTChain()

Get average residence times by node aggregated by chain.

GetAvgNodeTputChain()

Get average throughputs by node aggregated by chain.

GetAvgNodeArvRChain()

Get average arrival rates by node aggregated by chain.

GetAvgSys()

Get system-level average metrics.

Returns:

Tuple of (R, T) where R is chain-level system response time and T is chain-level system (carried) throughput.

Return type:

Tuple[ndarray, ndarray]

GetAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

GetTranAvg(*args)

Get transient average metrics.

Computes time-dependent queue lengths, utilizations, and throughputs using transient CTMC analysis with matrix exponential method.

Supports state prior iteration: when the model has multiple possible initial states (e.g., uniform prior from initFromMarginal + setStatePrior), runs the transient analysis for each state weighted by its prior probability. Matches MATLAB SolverCTMC/runAnalyzer.m lines 114-183.

Parameters:

*args – Optional transient handles (Qt, Ut, Tt) for MATLAB API compatibility.

Returns:

Tuple of (QNt, UNt, TNt) where each is a nested list [M][K] of TranResult objects.

SampleAggr(node, numEvents=1000)

Sample aggregated states at node (not supported for CTMC).

Raises:

NotImplementedError – CTMC is an analytical solver

SampleSys(numEvents=1000)

Sample system states.

In chain mode this returns a sample path of the user-supplied chain, started from options.init_sol when given and from the uniform distribution otherwise; a DTMC advances one unit of time per step. For a Network model CTMC is an analytical solver and sampling is refused.

Raises:

NotImplementedError – CTMC is an analytical solver on a Network model

SampleSysAggr(numEvents=1000)

Sample aggregated system states using CTMC simulation.

Uses the MMAP (Marked Markovian Arrival Process) approach matching MATLAB’s sampleSysAggr. The CTMC generator is decomposed into event filter matrices (one per sync event) to build an MMAP, which is then sampled to produce exactly numEvents actual events (arrivals/departures).

When event filtration is not available, falls back to direct CTMC simulation with enough transitions to produce numEvents actual events detected via population changes.

Parameters:

numEvents (int) – Number of actual events (arrivals + departures) to generate

Returns:

SampleResult containing timestamps, states, and event information

Return type:

SampleResult

aT()

Get comprehensive average performance metrics table.

Returns node-level results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork, ClassSwitch) are included with computed metrics. Cache nodes include HitClass/MissClass throughputs using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aNT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aCT()

Get average metrics by chain as DataFrame.

aNCT()

Get average metrics by node and chain as DataFrame.

aST()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avgT()

Get comprehensive average performance metrics table.

Returns node-level results (one row per node per class) to match MATLAB output format. Non-station nodes (e.g., Fork, ClassSwitch) are included with computed metrics. Cache nodes include HitClass/MissClass throughputs using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

nodeAvgT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

chainAvgT()

Get average metrics by chain as DataFrame.

nodeChainAvgT()

Get average metrics by node and chain as DataFrame.

sysAvgT()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_qlen()

Get average queue lengths (M x K).

avg_util()

Get average utilizations (M x K).

avg_respt()

Get average response times (M x K).

avg_resid_t()

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

avg_wait_t()

Get average waiting times (M x K).

avg_tput()

Get average throughputs (M x K).

avg_arv_r()

Get average arrival rates (M x K).

avg_sys_resp_t()

Get chain-level system response times (nchains,).

Uses the shared chain-based algorithm (a faithful port of MATLAB @NetworkSolver/getAvgSys.m): open chains sum alpha-weighted class residence times, closed chains apply Little’s law nJobsChain/XNchain.

avg_sys_tput()

Get chain-level system (carried) throughputs (nchains,).

Matches MATLAB/JAR getAvgSys: the throughput of completing classes routed back into the chain reference station (carried rate), not the offered/source arrival rate.

avg_sys_table()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_node()

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Cache). For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

avg_node_table()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

avg_node_chain()

Get average metrics by node and chain.

avg_node_chain_table()

Get average metrics by node and chain as DataFrame.

avg_chain()

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

avg_chain_table()

Get average metrics by chain as DataFrame.

state_space()

Get the enumerated state space.

Returns:

(stateSpace, localStateSpace) where stateSpace is the global

state matrix and localStateSpace is a list of per-station state arrays. For FCFS queues with multiple servers, localStateSpace includes buffer and phase columns (matching MATLAB’s nodeStateSpace format).

Return type:

tuple

state_space_aggr()

Get aggregated state space (jobs per station per class).

Returns:

Array of shape (nstates, nstations * nclasses) where column (ist * nclasses + k) = jobs of class k at station ist (0-indexed)

Return type:

ndarray

run_analyzer()

Run the CTMC analysis.

generator()

Get the infinitesimal generator matrix and event filters.

Returns:

(infGen, eventFilt) where infGen is the infinitesimal generator

matrix and eventFilt is a dictionary mapping event types to sparse matrices

Return type:

tuple

steady_state()

Get the steady-state probability distribution.

sample_sys_aggr(numEvents=1000)

Sample aggregated system states using CTMC simulation.

Uses the MMAP (Marked Markovian Arrival Process) approach matching MATLAB’s sampleSysAggr. The CTMC generator is decomposed into event filter matrices (one per sync event) to build an MMAP, which is then sampled to produce exactly numEvents actual events (arrivals/departures).

When event filtration is not available, falls back to direct CTMC simulation with enough transitions to produce numEvents actual events detected via population changes.

Parameters:

numEvents (int) – Number of actual events (arrivals + departures) to generate

Returns:

SampleResult containing timestamps, states, and event information

Return type:

SampleResult

sample_sys(numEvents=1000)

Sample system states.

In chain mode this returns a sample path of the user-supplied chain, started from options.init_sol when given and from the uniform distribution otherwise; a DTMC advances one unit of time per step. For a Network model CTMC is an analytical solver and sampling is refused.

Raises:

NotImplementedError – CTMC is an analytical solver on a Network model

sample_aggr(node, numEvents=1000)

Sample aggregated states at node (not supported for CTMC).

Raises:

NotImplementedError – CTMC is an analytical solver

inf_gen()

Get the infinitesimal generator matrix.

prob_aggr(ist)

Get probability of a specific per-class job distribution at a station.

Returns P(n1 jobs of class 1, n2 jobs of class 2, …) for the state that was set via setState() on the station.

Matches MATLAB: solver_ctmc_margaggr.m

Parameters:

ist – Station index (0-based) or node object

Returns:

Probability that station ist is in the specified state (scalar).

Return type:

float

prob_sys_aggr()

Get probability of the entire system being in the specified aggregated state.

Returns the joint probability of the system being in the aggregated state configuration set via setState() on all stations.

Matches MATLAB: solver_ctmc_jointaggr.m

Returns:

Joint probability of the system state.

Return type:

float

prob(station=None)

Get probability for the detailed state at station.

Returns the probability that the station is in the state that was set via setState(). This includes phase information from service distributions.

Matches MATLAB: solver_ctmc_marg.m

In chain mode the argument is a state of the user-supplied chain: a row of its state space, or a 1-based state index when the chain carries none.

Parameters:

station – Station index (0-based) or node object. If None, returns steady-state.

Returns:

Probability that station is in the specified detailed state.

Return type:

float

tran_prob(node, t=1.0)

Get transient state probabilities at a node.

Computes π(t) = π(0) * exp(Q*t) using matrix exponential.

Parameters:
  • node (int) – Node/station index (0-based)

  • t (float) – Time point for transient analysis

Returns:

Transient probability vector at time t

Return type:

ndarray

tran_prob_aggr(node, t=1.0)

Get transient aggregated state probabilities at a node.

Parameters:
  • node (int) – Node/station index (0-based)

  • t (float) – Time point for transient analysis

Returns:

Transient aggregated probability vector at time t

Return type:

ndarray

tran_prob_sys(t=1.0)

Get transient system state probabilities.

Computes full system state probability at time t.

In chain mode the distribution starts from options.init_sol, or from the uniform distribution when none is given; a DTMC advances one step per unit of time, so t must then be a non-negative integer.

Parameters:

t (float) – Time point for transient analysis

Returns:

Transient system probability vector at time t

Return type:

ndarray

tran_prob_sys_aggr(t=1.0)

Get transient aggregated system state probabilities.

Parameters:

t (float) – Time point for transient analysis

Returns:

Transient system probability vector at time t

Return type:

ndarray

class SolverFLD(network, method_or_options='default', options=None, **kwargs)[source]

Bases: ForkJoinDriverMixin, NetworkSolver

Native Python solver for fluid approximation of queueing networks.

Provides a unified interface to multiple fluid approximation algorithms, allowing seamless switching between different solution methods while maintaining consistent result formats and performance metrics.

This class follows the SolverMAM design pattern with: - Lazy method resolution (user-facing aliases map to internal implementation) - Consistent result accessors (getAvgQLen, getAvgRespT, etc.) - Method chaining support (runAnalyzer returns self) - Optional verbose output for debugging

Variables:
  • network (object) – Input network model (either a NetworkStruct or object with compileStruct method)

  • sn (NetworkStruct) – Compiled network structure (internal representation)

  • options (SolverFLDOptions) – Configuration parameters for solver behavior

  • result (FLDResult or None) – Solution results (None until runAnalyzer is called)

  • runtime (float) – Elapsed time in seconds for last analysis

Examples

Basic usage:

>>> solver = SolverFLD(network, method='mfq')
>>> solver.runAnalyzer()
>>> QN = solver.result.QN  # Access raw results
>>> qlen = solver.getAvgQLen()  # Access aggregated metrics

Method comparison:

>>> results = {}
>>> for method in ['matrix', 'mfq']:
...     s = SolverFLD(network, method=method)
...     s.runAnalyzer()
...     results[method] = s.result

Custom configuration:

>>> opts = SolverFLDOptions(tol=1e-6, pstar=50, verbose=True)
>>> solver = SolverFLD(network, options=opts)
>>> solver.runAnalyzer()
>>> metrics = solver.getAvgTable()  # Returns pandas DataFrame

Initialize SolverFLD.

Parameters:
  • network (NetworkStruct or object) –

    Network model specification. Can be either:

    • A NetworkStruct (compiled network structure)

    • An object with compileStruct() method (will be compiled automatically)

  • method (str, optional) –

    Solution method to use. Valid options:

    • ’default’, ‘matrix’, ‘fluid.matrix’, ‘pnorm’, ‘fluid.pnorm’: Matrix method with p-norm smoothing (default, recommended for most networks)

    • ’softmin’, ‘fluid.softmin’: Softmin smoothing (open networks only)

    • ’statedep’, ‘fluid.statedep’: State-dependent constraints (open networks only)

    • ’closing’, ‘fluid.closing’: Closing approximation with FCFS iteration

    • ’diffusion’, ‘fluid.diffusion’: Euler-Maruyama SDE (closed networks only)

    • ’mfq’, ‘fluid.mfq’, ‘butools’: Markovian fluid queue - exact M/M/c (single-queue networks only)

    Default is ‘matrix’ (mapped from ‘default’).

  • options (SolverFLDOptions, optional) – Configuration object. If not provided, defaults are used. If both method and options.method are specified, method parameter takes precedence. See SolverFLDOptions for available parameters.

Raises:

ValueError – If network is neither NetworkStruct nor has compileStruct method.

Notes

Method selection guidelines:

  • matrix (default): Recommended starting point. Works for open and closed networks. Fast and numerically stable. Parameters: pstar (smoothing parameter, default 20)

  • mfq: If analyzing single-queue bottleneck (M/M/1, M/M/c). Provides exact analytical solution via Erlang-C formula.

  • diffusion: For closed networks needing stochastic dynamics. Useful for variance and percentile analysis.

  • closing: For networks dominated by FCFS service. Requires iterations to converge. Parameters: iter_max, iter_tol

Examples

Using with NetworkStruct directly:

>>> from line_solver.api.sn import NetworkStruct
>>> sn = NetworkStruct()  # ... configure ...
>>> solver = SolverFLD(sn, method='mfq')

Using with Network object:

>>> model = Network('TestModel')
>>> # ... configure network ...
>>> solver = SolverFLD(model, method='matrix')

With custom options:

>>> opts = SolverFLDOptions(tol=1e-6, pstar=50)
>>> solver = SolverFLD(model, options=opts)
METHODS = {'aoi': 'mfq', 'butools': 'mfq', 'closing': 'closing', 'dae': 'dae', 'default': 'matrix', 'diffusion': 'diffusion', 'fluid.aoi': 'mfq', 'fluid.closing': 'closing', 'fluid.dae': 'dae', 'fluid.diffusion': 'diffusion', 'fluid.ggisgi': 'ggisgi.fluid', 'fluid.kp': 'kp', 'fluid.matrix': 'matrix', 'fluid.mfq': 'mfq', 'fluid.minnormal': 'minnormal', 'fluid.mol': 'mol', 'fluid.mtginf': 'mtginf', 'fluid.pnorm': 'matrix', 'fluid.refined': 'minnormal', 'fluid.rmf': 'rmf', 'fluid.softmin': 'closing', 'fluid.statedep': 'matrix', 'fluid.tbi': 'tbi', 'fluid.tga': 'ggingi.tga', 'fluid.tvms': 'tvms', 'ggingi.tga': 'ggingi.tga', 'ggisgi': 'ggisgi.fluid', 'ggisgi.fluid': 'ggisgi.fluid', 'kp': 'kp', 'matrix': 'matrix', 'mfq': 'mfq', 'minnormal': 'minnormal', 'mol': 'mol', 'mtginf': 'mtginf', 'pnorm': 'matrix', 'refined': 'minnormal', 'rmf': 'rmf', 'softmin': 'closing', 'statedep': 'matrix', 'tbi': 'tbi', 'tga': 'ggingi.tga', 'tvms': 'tvms'}
__init__(network, method_or_options='default', options=None, **kwargs)[source]

Initialize SolverFLD.

Parameters:
  • network (NetworkStruct or object) –

    Network model specification. Can be either:

    • A NetworkStruct (compiled network structure)

    • An object with compileStruct() method (will be compiled automatically)

  • method (str, optional) –

    Solution method to use. Valid options:

    • ’default’, ‘matrix’, ‘fluid.matrix’, ‘pnorm’, ‘fluid.pnorm’: Matrix method with p-norm smoothing (default, recommended for most networks)

    • ’softmin’, ‘fluid.softmin’: Softmin smoothing (open networks only)

    • ’statedep’, ‘fluid.statedep’: State-dependent constraints (open networks only)

    • ’closing’, ‘fluid.closing’: Closing approximation with FCFS iteration

    • ’diffusion’, ‘fluid.diffusion’: Euler-Maruyama SDE (closed networks only)

    • ’mfq’, ‘fluid.mfq’, ‘butools’: Markovian fluid queue - exact M/M/c (single-queue networks only)

    Default is ‘matrix’ (mapped from ‘default’).

  • options (SolverFLDOptions, optional) – Configuration object. If not provided, defaults are used. If both method and options.method are specified, method parameter takes precedence. See SolverFLDOptions for available parameters.

Raises:

ValueError – If network is neither NetworkStruct nor has compileStruct method.

Notes

Method selection guidelines:

  • matrix (default): Recommended starting point. Works for open and closed networks. Fast and numerically stable. Parameters: pstar (smoothing parameter, default 20)

  • mfq: If analyzing single-queue bottleneck (M/M/1, M/M/c). Provides exact analytical solution via Erlang-C formula.

  • diffusion: For closed networks needing stochastic dynamics. Useful for variance and percentile analysis.

  • closing: For networks dominated by FCFS service. Requires iterations to converge. Parameters: iter_max, iter_tol

Examples

Using with NetworkStruct directly:

>>> from line_solver.api.sn import NetworkStruct
>>> sn = NetworkStruct()  # ... configure ...
>>> solver = SolverFLD(sn, method='mfq')

Using with Network object:

>>> model = Network('TestModel')
>>> # ... configure network ...
>>> solver = SolverFLD(model, method='matrix')

With custom options:

>>> opts = SolverFLDOptions(tol=1e-6, pstar=50)
>>> solver = SolverFLD(model, options=opts)
reset()[source]

Reset the solver, clearing cached results and struct cache.

Matches MATLAB behavior where reset() invalidates the cached struct so the solver re-reads the model state on the next analysis run.

setInitialState(Q)[source]

Set initial state from queue length marginals.

Parameters:

Q (ndarray) – Queue lengths array of shape (M,) or (M, K) where M=stations, K=classes

exportODEs(filename='', notation='scalar')[source]

Export the system of ODEs integrated by the mean-field methods of this solver (default/matrix, pnorm, closing, statedep, softmin) as a standalone LaTeX document, in a symbolic form that is both human and machine readable. Mirrors the MATLAB SolverFLD.exportODEs method.

Parameters:
  • filename (str, optional) – Path of the .tex file to write; empty returns the source only.

  • notation (str, optional) – ‘scalar’ (default) for one expanded ODE per state variable, or ‘matrix’ for the compact matrix notation (dx/dt = W'*theta(x) + lambda for the matrix/pnorm methods, dx/dt = J*r(x) for the closing/statedep/softmin methods).

Returns:

LaTeX source of the exported ODE system.

Return type:

str

export_odes(filename='', notation='scalar')

Export the system of ODEs integrated by the mean-field methods of this solver (default/matrix, pnorm, closing, statedep, softmin) as a standalone LaTeX document, in a symbolic form that is both human and machine readable. Mirrors the MATLAB SolverFLD.exportODEs method.

Parameters:
  • filename (str, optional) – Path of the .tex file to write; empty returns the source only.

  • notation (str, optional) – ‘scalar’ (default) for one expanded ODE per state variable, or ‘matrix’ for the compact matrix notation (dx/dt = W'*theta(x) + lambda for the matrix/pnorm methods, dx/dt = J*r(x) for the closing/statedep/softmin methods).

Returns:

LaTeX source of the exported ODE system.

Return type:

str

getSymbolicDrift(options=None)[source]

Right-hand side of the mean-field ODE system as expression strings, one per state variable, together with the variable names they are written in.

This is the input the computer algebra backend needs to produce a Jacobian or an equilibrium (see getJacobian), and it is the same system solver_fluid_symodes describes and exportODEs typesets, written out variable by variable instead of in matrix form.

ONLY SMOOTH DRIFTS ARE EXPORTED. The default, matrix, closing and statedep methods scale rates by min(n_i, S_i), which is not differentiable at n_i = S_i, so their Jacobian does not exist there; emitting a one-sided derivative would be a silent lie exactly at the regime switch that matters. Use the p-norm smoothing (options.pstar, method matrix or pnorm) or the softmin method, whose drifts are smooth everywhere, and this function refuses the others by name.

Parameters:

options (SolverFLDOptions, optional) – Solver options; defaults to the solver’s own.

Returns:

(rhs, vars, sys) with rhs the expression strings, vars the variable names x1 … xn, and sys the structural description returned by solver_fluid_symodes.

Return type:

tuple

get_symbolic_drift(options=None)

Right-hand side of the mean-field ODE system as expression strings, one per state variable, together with the variable names they are written in.

This is the input the computer algebra backend needs to produce a Jacobian or an equilibrium (see getJacobian), and it is the same system solver_fluid_symodes describes and exportODEs typesets, written out variable by variable instead of in matrix form.

ONLY SMOOTH DRIFTS ARE EXPORTED. The default, matrix, closing and statedep methods scale rates by min(n_i, S_i), which is not differentiable at n_i = S_i, so their Jacobian does not exist there; emitting a one-sided derivative would be a silent lie exactly at the regime switch that matters. Use the p-norm smoothing (options.pstar, method matrix or pnorm) or the softmin method, whose drifts are smooth everywhere, and this function refuses the others by name.

Parameters:

options (SolverFLDOptions, optional) – Solver options; defaults to the solver’s own.

Returns:

(rhs, vars, sys) with rhs the expression strings, vars the variable names x1 … xn, and sys the structural description returned by solver_fluid_symodes.

Return type:

tuple

getJacobian(options=None, equilibria=False)[source]

Jacobian of the mean-field ODE right-hand side, d f_i / d x_j, as a matrix of expression strings, computed exactly by the computer algebra engine.

The Jacobian is what tells a fixed point apart from a limit cycle and gives the local convergence rate of the fluid approximation, neither of which a numerical integration reports. The equilibria, returned only when asked for, are the solutions of f(x) = 0; they can be empty when the system is beyond what the engine solves in closed form, which is a limitation of the solve and not an assertion that none exist.

Only smooth drifts have a Jacobian: see getSymbolicDrift, which refuses the min-scaled methods by name rather than returning a one-sided derivative.

The engine is the one named by options.config[‘symbolic’]: sympy natively, or the line-sage-rest service when ‘sage’ or a URL is asked for, mirroring the toolbox/service split of MATLAB’s SAGE.m.

Parameters:
  • options (SolverFLDOptions, optional) – Solver options; defaults to the solver’s own.

  • equilibria (bool, optional) – Also solve f(x) = 0.

Returns:

(J, rhs, vars, equilibria) with J[i][j] = d f_i / d x_j as an expression string written with ‘^’ for powers, rhs the drift itself, vars the state variable names, and equilibria a list of dicts mapping variable name to expression string (None when not requested).

Return type:

tuple

get_jacobian(options=None, equilibria=False)

Jacobian of the mean-field ODE right-hand side, d f_i / d x_j, as a matrix of expression strings, computed exactly by the computer algebra engine.

The Jacobian is what tells a fixed point apart from a limit cycle and gives the local convergence rate of the fluid approximation, neither of which a numerical integration reports. The equilibria, returned only when asked for, are the solutions of f(x) = 0; they can be empty when the system is beyond what the engine solves in closed form, which is a limitation of the solve and not an assertion that none exist.

Only smooth drifts have a Jacobian: see getSymbolicDrift, which refuses the min-scaled methods by name rather than returning a one-sided derivative.

The engine is the one named by options.config[‘symbolic’]: sympy natively, or the line-sage-rest service when ‘sage’ or a URL is asked for, mirroring the toolbox/service split of MATLAB’s SAGE.m.

Parameters:
  • options (SolverFLDOptions, optional) – Solver options; defaults to the solver’s own.

  • equilibria (bool, optional) – Also solve f(x) = 0.

Returns:

(J, rhs, vars, equilibria) with J[i][j] = d f_i / d x_j as an expression string written with ‘^’ for powers, rhs the drift itself, vars the state variable names, and equilibria a list of dicts mapping variable name to expression string (None when not requested).

Return type:

tuple

supportsTransientAnalysis()[source]

Transient averages are available (fluid ODE integrated over options.timespan).

supports_transient_analysis()

Transient averages are available (fluid ODE integrated over options.timespan).

supportsTransientVariance()[source]

A transient covariance is available only from the two methods that integrate one: ‘kp’ (the Ko-Pender diffusion limit) and ‘dae’ (the linear-noise covariance solved with the min-normal mean). This is exactly the gate getTranAvgVar enforces, asked as a predicate.

supports_transient_variance()

A transient covariance is available only from the two methods that integrate one: ‘kp’ (the Ko-Pender diffusion limit) and ‘dae’ (the linear-noise covariance solved with the min-normal mean). This is exactly the gate getTranAvgVar enforces, asked as a predicate.

runAnalyzer()[source]

Execute the fluid analysis using the configured method.

Supports state prior iteration (pprod loop): when the model has multiple possible initial states weighted by priors, runs the analysis for each state and accumulates weighted results. Matches MATLAB SolverFLD/runAnalyzer.m lines 112-208.

Returns:

Returns self to enable method chaining and fluent interface

Return type:

SolverFLD

resolveMethod(options)[source]

Concrete method the feature gate must validate.

The gate in NetworkSolver.runAnalyzerChecks validates getMethodFeatureSet(method), and ‘default’ is not ‘minnormal’, so without this override a GPS model would be rejected before the resolution ever ran. Keeping the decision in one place is what stops the gate and the dispatch from disagreeing.

resolve_method(options)

Concrete method the feature gate must validate.

The gate in NetworkSolver.runAnalyzerChecks validates getMethodFeatureSet(method), and ‘default’ is not ‘minnormal’, so without this override a GPS model would be rejected before the resolution ever ran. Keeping the decision in one place is what stops the gate and the dispatch from disagreeing.

getAvgTable()[source]

Get average performance metrics as DataFrame.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

DataFrame with columns

getAvgQLen()[source]

Get average queue lengths per station and class.

Returns the mean queue length (number of customers in system) for each station and job class.

Returns:

Shape (M, K) array where M = number of stations and K = number of classes. QN[i, c] is the average number of class-c customers at station i, including the one in service.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For Little’s Law validation: L = λ × W, where λ is arrival rate and W is mean response time. This relationship should hold for stable networks.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> qlen = solver.getAvgQLen()
>>> print(f"Queue length at station 0: {qlen[0, :].sum():.3f}")
getAvgUtil()[source]

Get average server utilizations per station and class.

Returns the fraction of time each server is busy on each job class.

Returns:

Shape (M, K) array where M = number of stations and K = number of classes. UN[i, c] is the fraction of station i’s service capacity spent on class c; the station’s utilization is the row sum.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For stable single-server queue (M/M/1): ρ = λ/μ. For multi-server queue (M/M/c): ρ = λ/(c×μ). Stability requires ρ < 1.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> util = solver.getAvgUtil().sum(axis=1)
>>> bottleneck = np.argmax(util)
>>> print(f"Bottleneck station: {bottleneck} (util={util[bottleneck]:.1%})")
getAvgRespT()[source]

Get average response times per station and class.

Returns mean time customers spend at each station (waiting + service) for each job class. Includes both queueing delay and service time.

Returns:

Shape (M, K) array where M = number of stations, K = number of classes. RN[i, c] is the average response time at station i for class c.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For M/M/1 queue: W = 1/(μ - λ) = ρ/(μ(1 - ρ)) where ρ = λ/μ. Verifies Little’s Law: L = λ × W for each station.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> resp_time = solver.getAvgRespT()
>>> print(f"System response time: {np.sum(resp_time):.3f}")
getAvgSysRespT()[source]

Get average system response time per job class.

Returns the total time a customer spends in the system for each job class.

Returns:

Shape (K,) array where K = number of job classes. CN[k] is the average system response time for class k.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For closed networks: uses Little’s Law C = N/X For open networks: sum of response times across all stations

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> sys_resp = solver.getAvgSysRespT()
>>> print(f"Mean system response time: {np.mean(sys_resp):.3f}")
getAvgSysTput()[source]

Get average system throughput per CHAIN.

Returns:

Shape (C,) array where C = number of chains. X[c] is the rate of completing classes routed back into chain c’s reference station.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

This is XNchain of MATLAB @NetworkSolver/getAvgSys.m, which is what getAvgSysTput.m returns and what the JAR’s result.XN holds. It used to return mean(XN), a single number over the classes, which made getAvgSysTable raise IndexError on every multi-chain model: the table indexes one throughput per chain into what had become a length-1 array.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> sys_tput = solver.getAvgSysTput()
>>> print(f"System throughput of chain 0: {sys_tput[0]:.4f} customers/time")
getCdfRespT(station=None, job_class=None, t_span=None)[source]

Get response time CDF for a station/class or all stations/classes.

Computes the cumulative distribution function (CDF) of response times (passage time distribution) for jobs of a given class at a station using network augmentation and transient class fluid tracking.

Parameters:
  • station (int, optional) – Station index. If None, returns CDF for all stations.

  • job_class (int, optional) – Job class index. If None, returns CDF for all classes.

  • t_span (tuple, optional) – Time interval (t_min, t_max) for CDF evaluation If None, automatically estimated based on mean response time

Returns:

  • When station and job_class are both None – List of lists where RD[station][class] is a 2D array with columns [cdf, time]

  • When station and job_class are specified – dict with keys ‘t’, ‘cdf’, ‘mean’, ‘var’, ‘method’

getTranCdfPassT(station=0, job_class=0, t=1.0)[source]

Get response time CDF value at specific time.

Returns the cumulative probability P(response_time ≤ t) at a given time.

Parameters:
  • station (int, optional) – Station index (default: 0)

  • job_class (int, optional) – Job class index (default: 0)

  • t (float) – Time point for CDF evaluation (default: 1.0)

Returns:

CDF value F(t) = P(response_time ≤ t) at specified time

Return type:

float

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> prob_less_than_1 = solver.getTranCdfPassT(station=0, t=1.0)
>>> print(f"P(response_time <= 1.0) = {prob_less_than_1:.4f}")
static listValidMethods()[source]

List all valid solution method names.

Returns a list of all method identifiers that can be passed to the method parameter of __init__, including both primary names and aliases.

Returns:

Valid method identifiers: - ‘default’: Maps to ‘matrix’ - ‘matrix’, ‘fluid.matrix’, ‘pnorm’, ‘fluid.pnorm’: Matrix method - ‘softmin’, ‘fluid.softmin’: Softmin smoothing variant - ‘statedep’, ‘fluid.statedep’: State-dependent variant - ‘closing’, ‘fluid.closing’: Closing approximation - ‘minnormal’, ‘fluid.minnormal’: Second-order moment closure - ‘refined’, ‘fluid.refined’: O(1/N) refined mean field (Gast) - ‘diffusion’, ‘fluid.diffusion’: Diffusion SDE method - ‘mfq’, ‘fluid.mfq’, ‘butools’: Markovian fluid queue - ‘aoi’, ‘fluid.aoi’: explicit AoI MFQ solver

Return type:

list of str

Examples

>>> methods = SolverFLD.listValidMethods()
>>> print(methods)
>>> for m in methods:
...     print(f"  - {m}")
static supports(sn, method)[source]

Check if a method can theoretically solve a given network.

Performs basic validation of method availability. More specific constraints (topology, network properties) are checked at solve time.

Parameters:
  • sn (NetworkStruct) – Network structure to validate against

  • method (str) – Method name to check

Returns:

(can_solve, reason) where: - can_solve: bool, whether method is valid - reason: str or None, explanation if not supported

Return type:

tuple

Examples

>>> sn = NetworkStruct()  # ... configure ...
>>> ok, reason = SolverFLD.supports(sn, 'matrix')
>>> if not ok:
...     print(f"Cannot use matrix: {reason}")
static canonicalMethod(method)[source]

The one spelling of a fluid method that every gate tests against.

NOT METHODS, which is a DISPATCH map: it sends ‘refined’ to ‘minnormal’ because they share a solver routine, while their feature envelopes differ (‘refined’ is closed-only). This collapses SPELLING only: the ‘fluid.’ qualifier, the MFQ backend aliases ‘butools’ and ‘aoi’, and the short spellings of the two single-station limits.

Canonicalizing once is what keeps an alias from carrying a different envelope than the name it resolves to, and it is the reason the four codebases can no longer drift apart over a spelling. MATLAB SolverFLD.canonicalMethod, the JAR and C++ apply the same three rules in the same order.

static canonical_method(method)

The one spelling of a fluid method that every gate tests against.

NOT METHODS, which is a DISPATCH map: it sends ‘refined’ to ‘minnormal’ because they share a solver routine, while their feature envelopes differ (‘refined’ is closed-only). This collapses SPELLING only: the ‘fluid.’ qualifier, the MFQ backend aliases ‘butools’ and ‘aoi’, and the short spellings of the two single-station limits.

Canonicalizing once is what keeps an alias from carrying a different envelope than the name it resolves to, and it is the reason the four codebases can no longer drift apart over a spelling. MATLAB SolverFLD.canonicalMethod, the JAR and C++ apply the same three rules in the same order.

getMethodFeatureSet(method)[source]

Feature envelope of a method, narrowed for ‘kp’ and for GPS.

supportsModelMethod(method)[source]

The structural finite-capacity gate runAnalyzer enforces at solve time, stated here so that a CALLER can see it before running.

Nothing in the fluid tree reads sn.cap or sn.classcap, so every method but two integrates a capped station as an unbounded one. ‘dae’ carries the buffer as an algebraic constraint on the drift, and ‘mol’ is stated for the Mt/G/s/0 LOSS system, where the server count IS the buffer; the rest keep the guard. There is no registry feature name for plain capacity, hence the structural test – SolverNC and SolverMVA gate the same way.

Left only in runAnalyzer the rule was invisible to every gate above it, and SolverAUTO.listValidMethods offered all 29 fluid methods on the BAS-blocking model of cqn_bas_blocking, each of which then raised when asked to run. Mirrors MATLAB @SolverFLD/supportsModelMethod.

static forkJoinAdmits(sn, method)[source]

Can method run the fluid fork-join fixed point on this model?

A fork-join model is not integrated as one drift: the MMT transform replaces the fork by auxiliary classes and the answer is the fixed point of solving that transformed model repeatedly. On a CLOSED model the transform stays closed and every fluid method takes it. On an OPEN one the auxiliary classes arrive at a Source, and the DAE form has no unknowns for them: the inner solve fails on the class count rather than returning a drift, so the method is refused by name instead.

‘refined’ is NOT listed here even though it fails the same way, because it is already refused on every open model, fork-join or not, by its own closed-model restriction (see getMethodFeatureSet).

Called by runAnalyzer, so the run stops on it, and by supportsModelMethod, so a caller sees the same verdict before paying for the fixed point. One predicate, two callers. Mirrors MATLAB fluid_forkjoin_admits.

Parameters:
  • sn – NetworkStruct of the model.

  • method – the concrete method name.

Returns:

(ok, reason); reason is ‘’ when ok is True.

supports_model_method(method)

The structural finite-capacity gate runAnalyzer enforces at solve time, stated here so that a CALLER can see it before running.

Nothing in the fluid tree reads sn.cap or sn.classcap, so every method but two integrates a capped station as an unbounded one. ‘dae’ carries the buffer as an algebraic constraint on the drift, and ‘mol’ is stated for the Mt/G/s/0 LOSS system, where the server count IS the buffer; the rest keep the guard. There is no registry feature name for plain capacity, hence the structural test – SolverNC and SolverMVA gate the same way.

Left only in runAnalyzer the rule was invisible to every gate above it, and SolverAUTO.listValidMethods offered all 29 fluid methods on the BAS-blocking model of cqn_bas_blocking, each of which then raised when asked to run. Mirrors MATLAB @SolverFLD/supportsModelMethod.

static getFeatureSet()[source]

Get set of features supported by the fluid solver.

Returns the canonical feature names (mirrors MATLAB SolverFLD.getFeatureSet and the JAR SolverFluid).

static defaultOptions()[source]

Get default solver configuration.

Returns a SolverFLDOptions object initialized with default parameters. Use this as a starting point for custom configurations.

Returns:

Configuration object with default values: - method: ‘default’ (maps to ‘matrix’) - tol: 1e-4 (ODE integration tolerance) - iter_max: 200 (max FCFS iterations) - pstar: 20.0 (p-norm smoothing parameter) - verbose: False

Return type:

SolverFLDOptions

Examples

>>> opts = SolverFLD.defaultOptions()
>>> opts.verbose = True
>>> solver = SolverFLD(network, options=opts)
static default_options()

Get default solver configuration.

Returns a SolverFLDOptions object initialized with default parameters. Use this as a starting point for custom configurations.

Returns:

Configuration object with default values: - method: ‘default’ (maps to ‘matrix’) - tol: 1e-4 (ODE integration tolerance) - iter_max: 200 (max FCFS iterations) - pstar: 20.0 (p-norm smoothing parameter) - verbose: False

Return type:

SolverFLDOptions

Examples

>>> opts = SolverFLD.defaultOptions()
>>> opts.verbose = True
>>> solver = SolverFLD(network, options=opts)
getPerctRespT(percentiles=None, station=0, job_class=0)[source]

Get percentile response times.

Computes response time percentiles by inverting the CDF computed via passage time analysis. Returns both raw values and a formatted DataFrame.

Parameters:
  • percentiles (list of float, optional) – Percentile values to compute (0-100 scale). Default is [50, 90, 95, 99] (median, 90th, 95th, 99th percentiles)

  • station (int, optional) – Station index for CDF computation (default: 0)

  • job_class (int, optional) – Job class index (default: 0)

Returns:

(perct_values, perct_table) where:

  • perct_values: np.ndarray of shape (n_percentiles,) with response time values corresponding to each percentile

  • perct_table: pd.DataFrame with columns [‘Percentile’, ‘ResponseTime’] for display and export

Return type:

tuple

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

The percentile computation uses the CDF obtained from passage time analysis. For percentile p, finds t such that F(t) = p/100, using linear interpolation between CDF points.

For high percentiles (e.g., 99th), accuracy depends on the time span used for CDF computation. If the CDF doesn’t reach the requested percentile, the method extrapolates using exponential tail approximation.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> values, table = solver.getPerctRespT([50, 90, 95, 99])
>>> print(table)
   Percentile  ResponseTime
0        50.0         1.234
1        90.0         3.456
2        95.0         4.567
3        99.0         6.789
>>> # Get 95th percentile response time
>>> p95 = values[2]  # Index corresponds to percentiles list
getAvgResidT()[source]

Get average residence times per station (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

Returns:

(M, K) array of residence times

Return type:

ndarray

getAvgWaitT()[source]

Get average waiting times per station and class.

Waiting time is response time minus the mean service time.

Returns:

(M, K) array of waiting times

Return type:

ndarray

getAvgArvR()[source]

Get average arrival rates per station and class.

Returns:

(M, K) array of arrival rates

Return type:

ndarray

getAvgTput()[source]

Get average throughputs per station and class.

Returns:

(M, K) array of throughputs

Return type:

ndarray

getMoments()[source]

Second-order results of the moment-closure methods.

Mirrors MATLAB @SolverFLD/getMoments: state-level covariance Sigma, station-class queue-length variance QVar and standard deviation QStd, per-station population variance sigma2, and the state-coordinate index maps stationBlock/classBlock. None for every first-order method, which computes no second moment at all.

getProbAggr(ist)[source]

Probability of the current per-class job distribution at a station.

Returns P(n_1, …, n_K at station ist) for the state the model is in. Two evaluations are available and the analysis that ran decides which, as @SolverFLD/getProbAggr.m does:

moment closure (‘minnormal’) – the solved state carries a covariance,

so the JOINT law of the per-class populations at the station is the multivariate normal of the linear noise approximation and the answer is the probability it assigns to the unit cell around n. Correlation between the classes is accounted for.

first-order methods – no second moment exists, so the classes can only

be treated as independent: Schmidt’s binomial per closed class, Poisson (Delay) or multinomial-geometric (queue) per open class.

Parameters:

ist (int) – Station index (1-based) or a station node

Returns:

(log_prob, prob)

Return type:

Tuple[float, float]

getProbMarg(station, jobclass)[source]

Get marginal queue-length distribution at station for class.

Parameters:
  • station (int) – Station index (0-based)

  • jobclass (int) – Job class index (0-based)

Returns:

Marginal probability vector P(n_ir) for n=0,1,2,…

Return type:

ndarray

getProbSys()[source]

Get system state probabilities.

Returns:

System state probability vector

Return type:

ndarray

getProbSysAggr()[source]

Get aggregated system state probabilities.

Returns:

System state probability vector (aggregated over classes)

Return type:

ndarray

getProb(station=None)[source]

Get state probabilities at station.

Parameters:

station (int | None) – Station index (0-based). If None, returns for all stations.

Returns:

Probability vector or list of vectors

Return type:

ndarray

getAvgAoI()[source]

Get average AoI and Peak AoI statistics.

getCdfAoI(t_values=None)[source]

Get AoI and Peak AoI CDFs as [cdf, t] arrays.

getTranAvgVar(*args)[source]

Transient queue-length VARIANCE per station and class.

Two methods compute a second moment along the trajectory. ‘kp’ integrates the covariance of the Ko-Pender diffusion limit alongside the fluid mean; ‘dae’ integrates the linear-noise covariance alongside the min-normal mean as one differential-algebraic system.

Returns (t, QVart, Sigmat, QCovt). QVart is a dict keyed (station, class); Sigmat is the full state covariance in the method’s own phase layout, (dim, dim, nt); QCovt is Sigmat AGGREGATED onto station-class pairs, (M*K, M*K, nt) indexed ir = c*M + i, which is an index space a caller can use without knowing that layout. SolverENV’s ‘meancov’ coupling reads QCovt and seeds the next stage through config[‘init_qlen’] / config[‘init_qcov’], in the same index space.

getTranAvg(*args)[source]

Get transient average metrics in MATLAB-compatible format.

Parameters:

*args – Optional transient handles (Qt, Ut, Tt) for MATLAB API compatibility.

Returns:

Tuple of (QNt, UNt, TNt) where each is a nested list [M][K] of TranResult objects.

getAvgQLenChain()[source]

Get average queue lengths aggregated by chain.

getAvgUtilChain()[source]

Get average utilizations aggregated by chain.

getAvgRespTChain()[source]

Get average response times aggregated by chain.

getAvgResidTChain()[source]

Get average residence times aggregated by chain.

getAvgTputChain()[source]

Get average throughputs aggregated by chain.

getAvgArvRChain()[source]

Get average arrival rates aggregated by chain.

getAvgChain()[source]

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgChainTable()[source]

Get average metrics by chain as DataFrame.

getAvgNode()[source]

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics (one row per node) including non-station nodes such as Cache and ClassSwitch. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgCacheTable()[source]

Detailed per-class cache performance metrics (see cache_table).

MATLAB carries this on the base NetworkSolver, so every solver that can analyse a cache has it; here it is per-solver, and a fluid cache solve that could not be tabulated reported nothing at all.

get_avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

MATLAB carries this on the base NetworkSolver, so every solver that can analyse a cache has it; here it is per-solver, and a fluid cache solve that could not be tabulated reported nothing at all.

avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

MATLAB carries this on the base NetworkSolver, so every solver that can analyse a cache has it; here it is per-solver, and a fluid cache solve that could not be tabulated reported nothing at all.

getAvgItemTable()[source]

Item-level cache occupancy table (see cache_table).

get_avg_item_table()

Item-level cache occupancy table (see cache_table).

avg_item_table()

Item-level cache occupancy table (see cache_table).

getAvgNodeTable()[source]

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes such as Cache and ClassSwitch. All-zero rows are omitted, matching the other solvers’ node tables.

getAvgNodeChain()[source]

Get average metrics by node and chain.

getAvgNodeChainTable()[source]

Get average metrics by node and chain as DataFrame.

getAvgNodeQLenChain()[source]

Get average queue lengths by node aggregated by chain.

getAvgNodeUtilChain()[source]

Get average utilizations by node aggregated by chain.

getAvgNodeRespTChain()[source]

Get average response times by node aggregated by chain.

getAvgNodeResidTChain()[source]

Get average residence times by node aggregated by chain.

getAvgNodeTputChain()[source]

Get average throughputs by node aggregated by chain.

getAvgNodeArvRChain()[source]

Get average arrival rates by node aggregated by chain.

getAvgSys()[source]

Get system-level average metrics.

Returns:

per-class system response time and per-class system throughput.

Return type:

Tuple of (R, T), both (K,)

getAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

getCdfPassT(station=None, job_class=None, t_span=None)[source]

Get the steady-state passage time CDF for a station/class.

The passage time of a job of class r at station i is the time from its arrival at the station to its departure from it. Under the fluid approximation this passage is exactly the quantity reported by getCdfRespT: both come from the transient passage time analysis started from the steady-state ODE solution, so this delegates to it rather than duplicating the computation. The two names are kept distinct because the solver interface declares both, and other solvers may separate them.

For the passage time along a prescribed route, i.e. conditional on a given sequence of nodes rather than at a single station, no method is provided: the fluid passage time analysis is per station and combining stations would require an independence assumption across them.

Parameters:
  • station (int, optional) – Station index. If None, returns the CDF for all stations.

  • job_class (int, optional) – Job class index. If None, returns the CDF for all classes.

  • t_span (tuple, optional) – Time interval (t_min, t_max) for CDF evaluation. If None, it is estimated from the mean response time.

Returns:

  • When station and job_class are both None – List of lists where RD[station][class] is a 2D array with columns [cdf, time]

  • When station and job_class are specified – dict with keys ‘t’, ‘cdf’, ‘mean’, ‘var’, ‘method’

See also

getCdfRespT

steady-state response time distribution (same quantity)

getTranCdfPassT

passage time distribution during the transient

getCdfPT(station=None, job_class=None, t_span=None)[source]

Get the steady-state passage time CDF for a station/class.

Backward-compatible name for getCdfPassT, to which this delegates. See getCdfPassT for the contract.

sample(node=0, numEvents=1000)[source]

Sample from state distribution (not supported for FLD).

Raises:

NotImplementedError – FLD is an analytical solver

sampleAggr(node=0, numEvents=1000)[source]

Sample aggregated states (not supported for FLD).

Raises:

NotImplementedError – FLD is an analytical solver

sampleSys(numEvents=1000)[source]

Sample system states (not supported for FLD).

Raises:

NotImplementedError – FLD is an analytical solver

sampleSysAggr(numEvents=1000)[source]

Sample aggregated system states (not supported for FLD).

Raises:

NotImplementedError – FLD is an analytical solver

GetAvgQLen()[source]

Alias for getAvgQLen (MATLAB compatibility).

GetAvgUtil()[source]

Alias for getAvgUtil (MATLAB compatibility).

GetAvgRespT()[source]

Alias for getAvgRespT (MATLAB compatibility).

GetAvgResidT()[source]

Alias for getAvgResidT (MATLAB compatibility).

GetAvgWaitT()[source]

Alias for getAvgWaitT (MATLAB compatibility).

GetAvgArvR()[source]

Alias for getAvgArvR (MATLAB compatibility).

GetAvgTput()[source]

Alias for getAvgTput (MATLAB compatibility).

GetAvgSysRespT()[source]

Alias for getAvgSysRespT (MATLAB compatibility).

GetAvgSysTput()[source]

Alias for getAvgSysTput (MATLAB compatibility).

GetAvgTable()[source]

Alias for getAvgTable (MATLAB compatibility).

GetCdfRespT(station=0, job_class=0, t_span=None)[source]

Alias for getCdfRespT (MATLAB compatibility).

GetPerctRespT(percentiles=None, station=0, job_class=0)[source]

Alias for getPerctRespT (MATLAB compatibility).

GetTranCdfPassT(station=0, job_class=0, t=1.0)[source]

Alias for getTranCdfPassT (MATLAB compatibility).

GetProbAggr(station)[source]

Alias for getProbAggr (MATLAB compatibility).

GetProbMarg(station, jobclass)[source]

Alias for getProbMarg (MATLAB compatibility).

GetProbSys()[source]

Alias for getProbSys (MATLAB compatibility).

GetProbSysAggr()[source]

Alias for getProbSysAggr (MATLAB compatibility).

GetProb(station=None)[source]

Alias for getProb (MATLAB compatibility).

GetAvgAoI()[source]

Alias for getAvgAoI (MATLAB compatibility).

GetCdfAoI(t_values=None)[source]

Alias for getCdfAoI (MATLAB compatibility).

GetTranAvg()[source]

Alias for getTranAvg (MATLAB compatibility).

GetAvg()[source]

Alias for getAvg (MATLAB compatibility).

GetAvgChain()[source]

Alias for getAvgChain (MATLAB compatibility).

GetAvgChainTable()[source]

Alias for getAvgChainTable (MATLAB compatibility).

GetAvgQLenChain()[source]

Alias for getAvgQLenChain (MATLAB compatibility).

GetAvgUtilChain()[source]

Alias for getAvgUtilChain (MATLAB compatibility).

GetAvgRespTChain()[source]

Alias for getAvgRespTChain (MATLAB compatibility).

GetAvgResidTChain()[source]

Alias for getAvgResidTChain (MATLAB compatibility).

GetAvgTputChain()[source]

Alias for getAvgTputChain (MATLAB compatibility).

GetAvgArvRChain()[source]

Alias for getAvgArvRChain (MATLAB compatibility).

GetAvgNode()[source]

Alias for getAvgNode (MATLAB compatibility).

GetAvgNodeTable()[source]

Alias for getAvgNodeTable (MATLAB compatibility).

GetAvgNodeChain()[source]

Alias for getAvgNodeChain (MATLAB compatibility).

GetAvgNodeChainTable()[source]

Alias for getAvgNodeChainTable (MATLAB compatibility).

GetAvgSys()[source]

Alias for getAvgSys (MATLAB compatibility).

GetAvgSysTable()[source]

Alias for getAvgSysTable (MATLAB compatibility).

GetCdfPT(station=None, job_class=None, t_span=None)[source]

Alias for getCdfPT (MATLAB compatibility).

GetCdfPassT(station=None, job_class=None, t_span=None)[source]

Alias for getCdfPassT (MATLAB compatibility).

GetAvgNodeQLenChain()

Get average queue lengths by node aggregated by chain.

GetAvgNodeUtilChain()

Get average utilizations by node aggregated by chain.

GetAvgNodeRespTChain()

Get average response times by node aggregated by chain.

GetAvgNodeResidTChain()

Get average residence times by node aggregated by chain.

GetAvgNodeTputChain()

Get average throughputs by node aggregated by chain.

GetAvgNodeArvRChain()

Get average arrival rates by node aggregated by chain.

aT()

Get average performance metrics as DataFrame.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

DataFrame with columns

aNT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes such as Cache and ClassSwitch. All-zero rows are omitted, matching the other solvers’ node tables.

aCT()

Get average metrics by chain as DataFrame.

aNCT()

Get average metrics by node and chain as DataFrame.

aST()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avgT()

Get average performance metrics as DataFrame.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

DataFrame with columns

nodeAvgT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes such as Cache and ClassSwitch. All-zero rows are omitted, matching the other solvers’ node tables.

chainAvgT()

Get average metrics by chain as DataFrame.

nodeChainAvgT()

Get average metrics by node and chain as DataFrame.

sysAvgT()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_node_table()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes such as Cache and ClassSwitch. All-zero rows are omitted, matching the other solvers’ node tables.

avg_chain_table()

Get average metrics by chain as DataFrame.

avg_node_chain_table()

Get average metrics by node and chain as DataFrame.

avg_sys_table()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

run_analyzer()

Execute the fluid analysis using the configured method.

Supports state prior iteration (pprod loop): when the model has multiple possible initial states weighted by priors, runs the analysis for each state and accumulates weighted results. Matches MATLAB SolverFLD/runAnalyzer.m lines 112-208.

Returns:

Returns self to enable method chaining and fluent interface

Return type:

SolverFLD

cdf_resp_t(station=None, job_class=None, t_span=None)

Get response time CDF for a station/class or all stations/classes.

Computes the cumulative distribution function (CDF) of response times (passage time distribution) for jobs of a given class at a station using network augmentation and transient class fluid tracking.

Parameters:
  • station (int, optional) – Station index. If None, returns CDF for all stations.

  • job_class (int, optional) – Job class index. If None, returns CDF for all classes.

  • t_span (tuple, optional) – Time interval (t_min, t_max) for CDF evaluation If None, automatically estimated based on mean response time

Returns:

  • When station and job_class are both None – List of lists where RD[station][class] is a 2D array with columns [cdf, time]

  • When station and job_class are specified – dict with keys ‘t’, ‘cdf’, ‘mean’, ‘var’, ‘method’

cdf_respt(station=None, job_class=None, t_span=None)

Get response time CDF for a station/class or all stations/classes.

Computes the cumulative distribution function (CDF) of response times (passage time distribution) for jobs of a given class at a station using network augmentation and transient class fluid tracking.

Parameters:
  • station (int, optional) – Station index. If None, returns CDF for all stations.

  • job_class (int, optional) – Job class index. If None, returns CDF for all classes.

  • t_span (tuple, optional) – Time interval (t_min, t_max) for CDF evaluation If None, automatically estimated based on mean response time

Returns:

  • When station and job_class are both None – List of lists where RD[station][class] is a 2D array with columns [cdf, time]

  • When station and job_class are specified – dict with keys ‘t’, ‘cdf’, ‘mean’, ‘var’, ‘method’

get_cdf_resp_t(station=None, job_class=None, t_span=None)

Get response time CDF for a station/class or all stations/classes.

Computes the cumulative distribution function (CDF) of response times (passage time distribution) for jobs of a given class at a station using network augmentation and transient class fluid tracking.

Parameters:
  • station (int, optional) – Station index. If None, returns CDF for all stations.

  • job_class (int, optional) – Job class index. If None, returns CDF for all classes.

  • t_span (tuple, optional) – Time interval (t_min, t_max) for CDF evaluation If None, automatically estimated based on mean response time

Returns:

  • When station and job_class are both None – List of lists where RD[station][class] is a 2D array with columns [cdf, time]

  • When station and job_class are specified – dict with keys ‘t’, ‘cdf’, ‘mean’, ‘var’, ‘method’

perct_resp_t(percentiles=None, station=0, job_class=0)

Get percentile response times.

Computes response time percentiles by inverting the CDF computed via passage time analysis. Returns both raw values and a formatted DataFrame.

Parameters:
  • percentiles (list of float, optional) – Percentile values to compute (0-100 scale). Default is [50, 90, 95, 99] (median, 90th, 95th, 99th percentiles)

  • station (int, optional) – Station index for CDF computation (default: 0)

  • job_class (int, optional) – Job class index (default: 0)

Returns:

(perct_values, perct_table) where:

  • perct_values: np.ndarray of shape (n_percentiles,) with response time values corresponding to each percentile

  • perct_table: pd.DataFrame with columns [‘Percentile’, ‘ResponseTime’] for display and export

Return type:

tuple

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

The percentile computation uses the CDF obtained from passage time analysis. For percentile p, finds t such that F(t) = p/100, using linear interpolation between CDF points.

For high percentiles (e.g., 99th), accuracy depends on the time span used for CDF computation. If the CDF doesn’t reach the requested percentile, the method extrapolates using exponential tail approximation.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> values, table = solver.getPerctRespT([50, 90, 95, 99])
>>> print(table)
   Percentile  ResponseTime
0        50.0         1.234
1        90.0         3.456
2        95.0         4.567
3        99.0         6.789
>>> # Get 95th percentile response time
>>> p95 = values[2]  # Index corresponds to percentiles list
perct_respt(percentiles=None, station=0, job_class=0)

Get percentile response times.

Computes response time percentiles by inverting the CDF computed via passage time analysis. Returns both raw values and a formatted DataFrame.

Parameters:
  • percentiles (list of float, optional) – Percentile values to compute (0-100 scale). Default is [50, 90, 95, 99] (median, 90th, 95th, 99th percentiles)

  • station (int, optional) – Station index for CDF computation (default: 0)

  • job_class (int, optional) – Job class index (default: 0)

Returns:

(perct_values, perct_table) where:

  • perct_values: np.ndarray of shape (n_percentiles,) with response time values corresponding to each percentile

  • perct_table: pd.DataFrame with columns [‘Percentile’, ‘ResponseTime’] for display and export

Return type:

tuple

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

The percentile computation uses the CDF obtained from passage time analysis. For percentile p, finds t such that F(t) = p/100, using linear interpolation between CDF points.

For high percentiles (e.g., 99th), accuracy depends on the time span used for CDF computation. If the CDF doesn’t reach the requested percentile, the method extrapolates using exponential tail approximation.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> values, table = solver.getPerctRespT([50, 90, 95, 99])
>>> print(table)
   Percentile  ResponseTime
0        50.0         1.234
1        90.0         3.456
2        95.0         4.567
3        99.0         6.789
>>> # Get 95th percentile response time
>>> p95 = values[2]  # Index corresponds to percentiles list
avg_qlen()

Get average queue lengths per station and class.

Returns the mean queue length (number of customers in system) for each station and job class.

Returns:

Shape (M, K) array where M = number of stations and K = number of classes. QN[i, c] is the average number of class-c customers at station i, including the one in service.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For Little’s Law validation: L = λ × W, where λ is arrival rate and W is mean response time. This relationship should hold for stable networks.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> qlen = solver.getAvgQLen()
>>> print(f"Queue length at station 0: {qlen[0, :].sum():.3f}")
avg_util()

Get average server utilizations per station and class.

Returns the fraction of time each server is busy on each job class.

Returns:

Shape (M, K) array where M = number of stations and K = number of classes. UN[i, c] is the fraction of station i’s service capacity spent on class c; the station’s utilization is the row sum.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For stable single-server queue (M/M/1): ρ = λ/μ. For multi-server queue (M/M/c): ρ = λ/(c×μ). Stability requires ρ < 1.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> util = solver.getAvgUtil().sum(axis=1)
>>> bottleneck = np.argmax(util)
>>> print(f"Bottleneck station: {bottleneck} (util={util[bottleneck]:.1%})")
avg_respt()

Get average response times per station and class.

Returns mean time customers spend at each station (waiting + service) for each job class. Includes both queueing delay and service time.

Returns:

Shape (M, K) array where M = number of stations, K = number of classes. RN[i, c] is the average response time at station i for class c.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For M/M/1 queue: W = 1/(μ - λ) = ρ/(μ(1 - ρ)) where ρ = λ/μ. Verifies Little’s Law: L = λ × W for each station.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> resp_time = solver.getAvgRespT()
>>> print(f"System response time: {np.sum(resp_time):.3f}")
get_avg_respt()

Get average response times per station and class.

Returns mean time customers spend at each station (waiting + service) for each job class. Includes both queueing delay and service time.

Returns:

Shape (M, K) array where M = number of stations, K = number of classes. RN[i, c] is the average response time at station i for class c.

Return type:

np.ndarray

Raises:

RuntimeError – If runAnalyzer() has not been called yet

Notes

For M/M/1 queue: W = 1/(μ - λ) = ρ/(μ(1 - ρ)) where ρ = λ/μ. Verifies Little’s Law: L = λ × W for each station.

Examples

>>> solver = SolverFLD(network).runAnalyzer()
>>> resp_time = solver.getAvgRespT()
>>> print(f"System response time: {np.sum(resp_time):.3f}")
avg_tput()

Get average throughputs per station and class.

Returns:

(M, K) array of throughputs

Return type:

ndarray

class SolverMAM(network, method='default', options=None, **kwargs)[source]

Bases: AvgResultsMixin, NetworkSolver

Native Python solver for matrix-analytic methods.

Solves queueing networks using decomposition, MNA, RCAT, and related methods.

Initialize SolverMAM.

Parameters:
  • network – Network model (must be compiled to NetworkStruct)

  • method (str) – Solution method (‘default’, ‘dec.source’, ‘mna’, etc.)

  • options (SolverMAMOptions | None) – SolverMAMOptions instance

  • **kwargs – Additional parameters (verbose, seed, etc.) for compatibility

ALGORITHMS = {'bgchain': <class 'line_solver.solvers.solver_mam.algorithms.bgchain.BgchainAlgorithm'>, 'dec.mmap': <class 'line_solver.solvers.solver_mam.algorithms.dec_mmap.DecMMAPAlgorithm'>, 'dec.poisson': <class 'line_solver.solvers.solver_mam.algorithms.dec_poisson.DecPoissonAlgorithm'>, 'dec.source': <class 'line_solver.solvers.solver_mam.algorithms.dec_source.DecSourceAlgorithm'>, 'dec.source.mmap': <class 'line_solver.solvers.solver_mam.algorithms.dec_source_mmap.DecSourceMMAPAlgorithm'>, 'ldqbd': <class 'line_solver.solvers.solver_mam.algorithms.ldqbd_solver.LDQBDAlgorithm'>, 'mna': None, 'mna_closed': <class 'line_solver.solvers.solver_mam.algorithms.mna_closed.MNAClosedAlgorithm'>, 'mna_open': <class 'line_solver.solvers.solver_mam.algorithms.mna_open.MNAOpenAlgorithm'>}
__init__(network, method='default', options=None, **kwargs)[source]

Initialize SolverMAM.

Parameters:
  • network – Network model (must be compiled to NetworkStruct)

  • method (str) – Solution method (‘default’, ‘dec.source’, ‘mna’, etc.)

  • options (SolverMAMOptions | None) – SolverMAMOptions instance

  • **kwargs – Additional parameters (verbose, seed, etc.) for compatibility

reset()[source]

Reset the solver to force recomputation on next getAvg call.

runAnalyzer()[source]

Run the analyzer with selected method.

Returns:

self (for method chaining)

Return type:

SolverMAM

static listValidMethods()[source]

List all valid solution methods.

Returns:

List of method names

Return type:

List[str]

static supports(sn, method)[source]

Check if method can solve this network.

Parameters:
  • sn – NetworkStruct

  • method (str) – Method name

Returns:

(can_solve, reason_if_not)

Return type:

Tuple[bool, str | None]

resolveMethod(options)[source]

Feature-driven resolution of method=’default’ via the existing MAM topology router (_select_method). Part of the base NetworkSolver method-aware gating contract.

getMethodFeatureSet(method)[source]

Per-method feature deltas on the base MAM envelope. Only ‘mna’ resolves a round-robin split (npfqn_traffic_split_rr in solver_mna_open); the closed branch has no counterpart and is rejected in supportsModelMethod. Mirrors the MATLAB/JAR SolverMAM.getMethodFeatureSet.

get_method_feature_set(method)

Per-method feature deltas on the base MAM envelope. Only ‘mna’ resolves a round-robin split (npfqn_traffic_split_rr in solver_mna_open); the closed branch has no counterpart and is rejected in supportsModelMethod. Mirrors the MATLAB/JAR SolverMAM.getMethodFeatureSet.

unsupportedMethodReason(method)[source]

The forwarding address for the RCAT names, which are SolverAG’s now.

Asks nothing of the model, so runAnalyzerChecks can call it before the struct is built; supportsModelMethod and runAnalyzer return the same string, so a caller gets one answer whichever gate it meets first.

unsupported_method_reason(method)

The forwarding address for the RCAT names, which are SolverAG’s now.

Asks nothing of the model, so runAnalyzerChecks can call it before the struct is built; supportsModelMethod and runAnalyzer return the same string, so a caller gets one answer whichever gate it meets first.

supportsModelMethod(method)[source]

Method-aware gate for MAM. Each decomposition algorithm declares a structural applicability predicate (supports_network), so delegate to it. For the special/auto-dispatched methods (mna, ldqbd, fj, retrial, reneging) that have no flat per-algorithm predicate, fall back to the per-method feature set. Returns (bool, reason).

resolve_method(options)

Feature-driven resolution of method=’default’ via the existing MAM topology router (_select_method). Part of the base NetworkSolver method-aware gating contract.

supports_model_method(method)

Method-aware gate for MAM. Each decomposition algorithm declares a structural applicability predicate (supports_network), so delegate to it. For the special/auto-dispatched methods (mna, ldqbd, fj, retrial, reneging) that have no flat per-algorithm predicate, fall back to the per-method feature set. Returns (bool, reason).

static getFeatureSet()[source]

Get set of features supported by SolverMAM.

Returns the canonical feature names (mirrors MATLAB SolverMAM.getFeatureSet and the JAR SolverMAM).

static defaultOptions()[source]

Get default solver options.

Returns:

SolverMAMOptions with default values

Return type:

SolverMAMOptions

getCdfRespT(R=None)[source]

Get response time CDF as the exact matrix-analytic passage-time law.

The native twin of MATLAB @SolverMAM/getCdfRespT.m: it runs solver_mam_passage_time on the Source + single queue open model (FCFS/HOL through the MMAPPH1FCFS sojourn PH law, PS through the Masuyama-Takine MAP/M/1-PS distribution). This used to fit an exponential to the mean, which agreed with the reference on the mean by construction and nowhere else.

Parameters:

R (ndarray | None) – Optional response time handles, accepted for signature compatibility and not read (the passage-time analysis computes every class at the queue anyway).

Returns:

List of dicts with ‘station’, ‘class’, ‘t’, ‘p’ keys; empty, after a warning, on a topology the analysis does not cover.

Return type:

List[Dict]

getPerctRespT(percentiles=None, jobclass=None)[source]

Extract percentiles from response time distribution.

Parameters:
  • percentiles (List[float] | None) – List of percentiles (0-100). Default: [50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class filter (1-based)

Returns:

Tuple of (percentile_list, percentile_table)

Return type:

Tuple[List[Dict], DataFrame]

getProb(node, state=None)[source]

State probability of a (level, phase) pair, or the full matrix.

Port of MATLAB @SolverMAM/getProb.m. QBD analysis is a single-queue method, so a network with more than one queue station is refused by name rather than approximated.

Parameters:
  • node (int) – node index (0-based)

  • state – [level, phase] pair, or None for the whole matrix

Returns:

scalar probability, or the (levels x phases) matrix when state is None

getProbMarg(station, jobclass)[source]

Get marginal queue-length distribution at station for class.

Parameters:
  • station (int) – Station index (0-based)

  • jobclass (int) – Job class index (0-based)

Returns:

Marginal probability vector P(n_ir) for n=0,1,2,…

Return type:

ndarray

sample(node=0, numEvents=1000)[source]

Sample from state distribution (not supported for MAM).

Raises:

NotImplementedError – MAM is an analytical solver

sampleAggr(node=0, numEvents=1000)[source]

Sample aggregated states (not supported for MAM).

Raises:

NotImplementedError – MAM is an analytical solver

sampleSys(numEvents=1000)[source]

Sample system states (not supported for MAM).

Raises:

NotImplementedError – MAM is an analytical solver

sampleSysAggr(numEvents=1000)[source]

Sample aggregated system states (not supported for MAM).

Raises:

NotImplementedError – MAM is an analytical solver

getTranCdfRespT()[source]

Get transient response time CDF (not supported for MAM).

Raises:

NotImplementedError – MAM computes steady-state only

getTranCdfPassT()[source]

Get transient passage time CDF (not supported for MAM).

Raises:

NotImplementedError – MAM computes steady-state only

getTranAvg(*args)[source]

Get transient average metrics via QBD matrix exponentiation.

Returns:

Tuple of (QNt, UNt, TNt) where each is a nested list [M][K] of TranResult objects.

getCdfPassT()[source]

Get passage time CDF.

For SolverMAM the passage time IS the response time: both come from the same solver_mam_passage_time call, so this delegates, as the JAR and the C++ CLI arms do.

getAvgChainTable()[source]

Get average metrics by chain as DataFrame.

getAvgNode()[source]

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Router/VSink).

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgNodeTable()[source]

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvgNodeChain()[source]

Get average metrics by node and chain.

getAvgNodeChainTable()[source]

Get average metrics by node and chain as DataFrame.

getAvgNodeQLenChain()[source]

Get average queue lengths by node aggregated by chain.

getAvgNodeUtilChain()[source]

Get average utilizations by node aggregated by chain.

getAvgNodeRespTChain()[source]

Get average response times by node aggregated by chain.

getAvgNodeResidTChain()[source]

Get average residence times by node aggregated by chain.

getAvgNodeTputChain()[source]

Get average throughputs by node aggregated by chain.

getAvgNodeArvRChain()[source]

Get average arrival rates by node aggregated by chain.

getAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

getMAMResult()[source]

Intermediate quantities of the matrix-analytic analysis of a single-queue model, in addition to the mean performance measures returned by getAvg.

Mean values alone hide the objects the method is actually built on, so a matrix-analytic result cannot be inspected, taught, or checked against a published derivation. This accessor returns them.

For a BMAP (or MAP) arrival stream feeding an exponential single server the result is that of qsys_bmapm1 and carries the M/G/1-type quantities: the phase-process stationary vectors theta and alpha, the randomized blocks A0, A1, B0 and Bk, the matrix G, the drift, the measured decay rate and the level probabilities.

For a retrial station the result is that of qsys_bmapphnn_retrial and carries the orbit-level stationary distribution together with the truncation level and its residual.

get_mam_result()

Intermediate quantities of the matrix-analytic analysis of a single-queue model, in addition to the mean performance measures returned by getAvg.

Mean values alone hide the objects the method is actually built on, so a matrix-analytic result cannot be inspected, taught, or checked against a published derivation. This accessor returns them.

For a BMAP (or MAP) arrival stream feeding an exponential single server the result is that of qsys_bmapm1 and carries the M/G/1-type quantities: the phase-process stationary vectors theta and alpha, the randomized blocks A0, A1, B0 and Bk, the matrix G, the drift, the measured decay rate and the level probabilities.

For a retrial station the result is that of qsys_bmapphnn_retrial and carries the orbit-level stationary distribution together with the truncation level and its residual.

GetAvg()

Average station metrics (Q, U, R, T, A, W) as station x class matrices.

Single analyzer funnel of the native solvers, mirroring MATLAB @NetworkSolver/getAvg.m and JAR NetworkSolver.getAvg(): it runs the analyzer if there is no cached result, then reads the averages from whichever result store the solver uses. Every solver used to carry its own copy of this body, differing only in that store (‘_result’ vs ‘result’) and in the field naming (‘QN’ vs ‘Q’), which _AVG_FIELDS already reconciles; the duplication also meant there was no single place to intercept a solve, as the other two codebases have.

Returns:

queue lengths, utilizations, response times, throughputs, arrival rates and residence times.

Return type:

(Q, U, R, T, A, W)

GetAvgChainTable()

Get average metrics by chain as DataFrame.

GetAvgNode()

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Router/VSink).

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

GetAvgNodeTable()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

GetAvgNodeChain()

Get average metrics by node and chain.

GetAvgNodeChainTable()

Get average metrics by node and chain as DataFrame.

GetAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

GetCdfRespT(R=None)

Get response time CDF as the exact matrix-analytic passage-time law.

The native twin of MATLAB @SolverMAM/getCdfRespT.m: it runs solver_mam_passage_time on the Source + single queue open model (FCFS/HOL through the MMAPPH1FCFS sojourn PH law, PS through the Masuyama-Takine MAP/M/1-PS distribution). This used to fit an exponential to the mean, which agreed with the reference on the mean by construction and nowhere else.

Parameters:

R (ndarray | None) – Optional response time handles, accepted for signature compatibility and not read (the passage-time analysis computes every class at the queue anyway).

Returns:

List of dicts with ‘station’, ‘class’, ‘t’, ‘p’ keys; empty, after a warning, on a topology the analysis does not cover.

Return type:

List[Dict]

GetPerctRespT(percentiles=None, jobclass=None)

Extract percentiles from response time distribution.

Parameters:
  • percentiles (List[float] | None) – List of percentiles (0-100). Default: [50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class filter (1-based)

Returns:

Tuple of (percentile_list, percentile_table)

Return type:

Tuple[List[Dict], DataFrame]

GetProb(node, state=None)

State probability of a (level, phase) pair, or the full matrix.

Port of MATLAB @SolverMAM/getProb.m. QBD analysis is a single-queue method, so a network with more than one queue station is refused by name rather than approximated.

Parameters:
  • node (int) – node index (0-based)

  • state – [level, phase] pair, or None for the whole matrix

Returns:

scalar probability, or the (levels x phases) matrix when state is None

GetProbMarg(station, jobclass)

Get marginal queue-length distribution at station for class.

Parameters:
  • station (int) – Station index (0-based)

  • jobclass (int) – Job class index (0-based)

Returns:

Marginal probability vector P(n_ir) for n=0,1,2,…

Return type:

ndarray

GetTranAvg(*args)

Get transient average metrics via QBD matrix exponentiation.

Returns:

Tuple of (QNt, UNt, TNt) where each is a nested list [M][K] of TranResult objects.

GetAvgNodeQLenChain()

Get average queue lengths by node aggregated by chain.

GetAvgNodeUtilChain()

Get average utilizations by node aggregated by chain.

GetAvgNodeRespTChain()

Get average response times by node aggregated by chain.

GetAvgNodeResidTChain()

Get average residence times by node aggregated by chain.

GetAvgNodeTputChain()

Get average throughputs by node aggregated by chain.

GetAvgNodeArvRChain()

Get average arrival rates by node aggregated by chain.

aNT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aCT()

Get average metrics by chain as DataFrame.

aNCT()

Get average metrics by node and chain as DataFrame.

aST()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

nodeAvgT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

chainAvgT()

Get average metrics by chain as DataFrame.

nodeChainAvgT()

Get average metrics by node and chain as DataFrame.

sysAvgT()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_node_table()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

avg_chain_table()

Get average metrics by chain as DataFrame.

avg_node_chain_table()

Get average metrics by node and chain as DataFrame.

avg_sys_table()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

run_analyzer()

Run the analyzer with selected method.

Returns:

self (for method chaining)

Return type:

SolverMAM

cdf_resp_t(R=None)

Get response time CDF as the exact matrix-analytic passage-time law.

The native twin of MATLAB @SolverMAM/getCdfRespT.m: it runs solver_mam_passage_time on the Source + single queue open model (FCFS/HOL through the MMAPPH1FCFS sojourn PH law, PS through the Masuyama-Takine MAP/M/1-PS distribution). This used to fit an exponential to the mean, which agreed with the reference on the mean by construction and nowhere else.

Parameters:

R (ndarray | None) – Optional response time handles, accepted for signature compatibility and not read (the passage-time analysis computes every class at the queue anyway).

Returns:

List of dicts with ‘station’, ‘class’, ‘t’, ‘p’ keys; empty, after a warning, on a topology the analysis does not cover.

Return type:

List[Dict]

perct_resp_t(percentiles=None, jobclass=None)

Extract percentiles from response time distribution.

Parameters:
  • percentiles (List[float] | None) – List of percentiles (0-100). Default: [50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class filter (1-based)

Returns:

Tuple of (percentile_list, percentile_table)

Return type:

Tuple[List[Dict], DataFrame]

static list_valid_methods()

List all valid solution methods.

Returns:

List of method names

Return type:

List[str]

static default_options()

Get default solver options.

Returns:

SolverMAMOptions with default values

Return type:

SolverMAMOptions

class SolverNC(model, method_or_options=None, **kwargs)[source]

Bases: TransformSolveMixin, ForkJoinDriverMixin, NetworkSolver

Native Python NC (Normalizing Constant) solver.

This solver analyzes product-form queueing networks using normalizing constant computation methods in pure Python/NumPy, providing the same functionality as the Java wrapper without requiring the JVM.

Supported methods:
  • ‘default’: Automatic method selection

  • ‘exact’: Exact convolution

  • ‘comom’: Approximate method

Parameters:
  • model – Network model (Python wrapper or native structure)

  • method – Solution method (default: ‘default’)

  • **kwargs – Additional solver options

supportsExactSensitivity()[source]

The normalizing-constant solver is exact on the same product-form class that pfqn_sens differentiates, so getSensitivityTable uses the analytic branch.

supports_exact_sensitivity()

The normalizing-constant solver is exact on the same product-form class that pfqn_sens differentiates, so getSensitivityTable uses the analytic branch.

reset()[source]

Reset solver state to force recomputation on next getAvg() call.

This is called by ensemble solvers (like LN) after updating layer parameters to ensure the solver recomputes with new values.

runAnalyzer()[source]

Run the NC analysis.

getAvgTable()[source]

Get comprehensive average performance metrics table.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvgQLen()[source]

Get average queue lengths (M x K).

getAvgUtil()[source]

Get average utilizations (M x K).

getAvgRespT()[source]

Get average response times (M x K).

getAvgResidT()[source]

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

getAvgWaitT()[source]

Get average waiting times (M x K).

getAvgTput()[source]

Get average throughputs (M x K).

getAvgArvR()[source]

Get average arrival rates (M x K).

Uses routing matrix to compute proper arrival rates. Source stations have arrival rate = 0.

getAvgSysRespT()[source]

Get system response times (cycle times) per chain (nchains,).

Returns chain-level response times matching MATLAB/Java implementation. Uses the completes flag to determine which classes contribute to chain throughput.

Note

For closed chains: uses Little’s Law CNchain = nJobsChain / XNchain For open chains: weighted sum of class response times

getAvgSysTput()[source]

Get system throughputs per chain (nchains,).

Returns chain-level throughputs matching MATLAB/Java implementation. Uses the completes flag to determine which classes contribute to chain throughput.

getNormalizingConstant()[source]

Get the normalizing constant G.

Returns:

The normalizing constant (not log)

Return type:

float

getLogNormalizingConstant()[source]

Get the log normalizing constant log(G).

Returns:

log(G)

Return type:

float

getProbNormConstAggr()[source]

Get the log normalizing constant (alias).

Returns:

log(G)

Return type:

float

getAvgBusyPeriod(stations, n=1)[source]

Mean busy period of order n for a set of stations.

The busy period of order n runs from the instant a job entering the set finds n-1 jobs in it up to the next instant when fewer than n remain (H. Daduna, “Busy Periods for Subnetworks in Stochastic Networks: Mean Value Analysis”, J. ACM 35(3), 1988). Exact on the single-chain product-form class and, by the insensitivity of Section 5 of that paper, dependent on the service processes only through their mean rates.

Parameters:
  • stations – stations forming the subnetwork, as objects, names or zero-based station indexes.

  • n – busy period order or sequence of orders.

Returns:

mean busy period duration(s) and the log normalizing constants of the subnetwork and of its complement.

Return type:

(b, lG, lH)

getEffectiveServiceTimes()[source]

Get effective service times.

Returns:

Effective service times (M x K)

Return type:

np.ndarray

getIterationCount()[source]

Get the number of iterations used.

getRuntime()[source]

Get the solver runtime in seconds.

getMethodUsed()[source]

Get the method actually used (may differ from requested).

getCdfRespT(R=None)[source]

Get response time CDF using exponential approximation.

Ports MATLAB’s @SolverNC/getCdfRespT.m: the EXACT product-form sojourn law at FCFS stations via pfqn_stdf, not the base-class exponential approximation. Delay stations carry their own service CDF.

options.config[‘algorithm’] selects ‘exact’ (pfqn_stdf, default) or ‘rd’ (pfqn_stdf_heur), as the reference does.

Returns:

List of dicts with ‘station’, ‘class’, ‘t’, ‘p’ keys. Only FCFS and delay stations are populated, which is exactly the set the reference fills; a PS or LCFS queue has no entry.

Raises:

ValueError – on an open class. The tagged-job passage time is defined on a CLOSED network.

Return type:

List[Dict]

getPerctRespT(percentiles=None, jobclass=None, method='default')[source]

Extract percentiles from response time distribution.

Parameters:
  • percentiles (List[float] | None) – List of percentiles (0-100). Default: [10, 25, 50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class filter (1-based)

Returns:

Tuple of (percentile_list, percentile_table)

Return type:

Tuple[List[Dict], DataFrame]

listValidMethods()[source]

List valid solution methods.

Returns:

  • ‘default’: Auto-select based on problem size

  • ’exact’, ‘ca’: Exact convolution algorithm

  • ’imci’: Importance sampling Monte Carlo integration

  • ’ls’: Linearizer method

  • ’le’: Logistic expansion (Cas17)

  • ’ble’: Logistic expansion with an empirical correction to LE

  • ’mmint2’: Gauss-Legendre quadrature

  • ’gleint’: Gauss-Legendre integration

  • ’pana’: PANACEA asymptotic expansion (load-independent)

  • ’panald’: PANACEA asymptotic expansion (load-dependent)

  • ’kt’: Knessl-Tier expansion

  • ’bkt’: Knessl-Tier expansion with the Stirling-remainder correction (BKT)

  • ’lekt’: the estimator ‘ble’ and ‘bkt’ both compute, on the cheaper side

  • ’sampling’: Monte Carlo sampling

  • ’propfair’: Proportionally fair allocation

  • ’divdiff’: divided-difference closed form (Casale, SIGMETRICS 2017), no think time; load-dependent rates go through the limited load-dependent kernel of Casale-Harrison-Ong (Perform. Eval. 2021)

  • ’rgf’: Recursion by generating functions (single-class, grouped stations)

  • ’ger’: Gerasimov residue closed form (free in the eliminated class populations, costly in the class count)

  • ’comom’: Conditional moments

  • ’comomld’: Conditional moments, load-dependent

  • ’cub’: Controllable upper bound

  • ’gm’: Grundmann-Moeller cubature (alias of ‘cub’)

  • ’rd’: Reduction heuristic

  • ’nrl’: Norlund-Rice Logit approximation

  • ’nrp’: Norlund-Rice Probit approximation

  • ’nre’: Norlund-Rice saddle-tilted Edgeworth approximation

  • ’ms’: Manjunath-Sikdar transform of the loss-network analyzer, the only place it is admissible

  • ’sdr’, ‘sdr.mva’: Krzesinski state-dependent routing, the eq. (16) enumeration and its Section 4 MVA arm

  • ’morrison’: heavy-usage expansion for a closed think+DPS network

  • ’rec’: memoised decision-diagram walk of the reachable set, for product-form Petri nets and loss networks

  • ’mcmc’: Chen-O’Cinneide regularization, a Markov chain Monte Carlo estimator of the throughput ratios and the queue lengths

Return type:

List of valid method names for NC solver

‘ms’, ‘sdr’ and ‘sdr.mva’ are DISPATCHED here (runAnalyzer and api/solvers/nc/analyzers.py) and were missing from this list, so the shared gate in NetworkSolver.runAnalyzerChecks refused three methods this solver implements and the other three codebases advertise.

isStochasticMethod(method)[source]

NC is deterministic except for the Monte Carlo integration methods (mci/imci), logistic sampling (ls), the importance sampling method (is), the Chen-O’Cinneide Markov chain Monte Carlo method (mcmc), and the sampling method, whose estimates depend on the random seed. Method names are tokenized so that runtime-resolved names such as ‘default/imci’ and prefixed names such as ‘nc.ls’ classify correctly.

is_stochastic_method(method)

NC is deterministic except for the Monte Carlo integration methods (mci/imci), logistic sampling (ls), the importance sampling method (is), the Chen-O’Cinneide Markov chain Monte Carlo method (mcmc), and the sampling method, whose estimates depend on the random seed. Method names are tokenized so that runtime-resolved names such as ‘default/imci’ and prefixed names such as ‘nc.ls’ classify correctly.

resolveMethod(options)[source]

Feature-driven resolution of method=’default’: an open network with non-Markovian (non-unit SCV) variability within the MEM feature set is solved by the Maximum Entropy Method by default, since the normalizing-constant path would silently exponentialize it. Mirrors the dispatch below in runAnalyzer and the MATLAB SolverNC.resolveMethod.

getMethodFeatureSet(method)[source]

Per-method feature deltas applied to the base NC envelope, as a set of feature-name strings.

ONLY THE RESTRICTIONS A FEATURE NAME CAN CARRY LIVE HERE. A feature set declares what the method ACCEPTS, so it can refuse a model for HAVING a construct and never for lacking one: “closed population only” and “no think time” are expressible by dropping OpenClass and SchedStrategy_INF, while “requires a cache” or “requires a loss network” are not and belong to nc_method_refusal, which supportsModelMethod consults next. Mirrors the MATLAB/JAR SolverNC.getMethodFeatureSet and the C++ nc_feature_set.

get_method_feature_set(method)

Per-method feature deltas applied to the base NC envelope, as a set of feature-name strings.

ONLY THE RESTRICTIONS A FEATURE NAME CAN CARRY LIVE HERE. A feature set declares what the method ACCEPTS, so it can refuse a model for HAVING a construct and never for lacking one: “closed population only” and “no think time” are expressible by dropping OpenClass and SchedStrategy_INF, while “requires a cache” or “requires a loss network” are not and belong to nc_method_refusal, which supportsModelMethod consults next. Mirrors the MATLAB/JAR SolverNC.getMethodFeatureSet and the C++ nc_feature_set.

supportsModelMethod(method)[source]

Method-aware gate. MEM (Kouvatsos maximum entropy) has structural applicability rules beyond a flat feature set (open-only, no class switching, non-priority scheduling); delegate to solver_nc_mem_supports, which returns a precise reason. Every other method is gated on its own per-method feature set and then on nc_method_refusal, the single copy of the structural rules the analyzer enforces, so that a pair this gate offers is a pair the run accepts. The single-Delay DROP loss-network exception handled by solver_nc_lossn_analyzer is preserved.

static supportsExactness(model, method)[source]

(bool, reason) Product-form precondition of the normalizing-constant methods, the same rule runAnalyzer enforces at solve time. Only ‘exact’, ‘is’ and ‘panald’ require it (the others fall back to Seidmann’s comom on a non-product-form model), and ‘is’ on a pass-and-swap model is exempt (pfqn_pas_is). Product form has no registry feature name, so the check cannot live in getMethodFeatureSet. Mirrors MATLAB SolverNC.supportsModelMethod.

static supports_exactness(model, method)

(bool, reason) Product-form precondition of the normalizing-constant methods, the same rule runAnalyzer enforces at solve time. Only ‘exact’, ‘is’ and ‘panald’ require it (the others fall back to Seidmann’s comom on a non-product-form model), and ‘is’ on a pass-and-swap model is exempt (pfqn_pas_is). Product form has no registry feature name, so the check cannot live in getMethodFeatureSet. Mirrors MATLAB SolverNC.supportsModelMethod.

runAnalyzerChecks(options)[source]

NC feature gate. Unlike the base gate this does not raise on an unrecognized method name: NC intentionally tolerates internal/auto names (e.g. ‘default/comomld’) and, as a SolverLN layer backend, runs with checks disabled. Only method-aware feature applicability is enforced.

resolve_method(options)

Feature-driven resolution of method=’default’: an open network with non-Markovian (non-unit SCV) variability within the MEM feature set is solved by the Maximum Entropy Method by default, since the normalizing-constant path would silently exponentialize it. Mirrors the dispatch below in runAnalyzer and the MATLAB SolverNC.resolveMethod.

supports_model_method(method)

Method-aware gate. MEM (Kouvatsos maximum entropy) has structural applicability rules beyond a flat feature set (open-only, no class switching, non-priority scheduling); delegate to solver_nc_mem_supports, which returns a precise reason. Every other method is gated on its own per-method feature set and then on nc_method_refusal, the single copy of the structural rules the analyzer enforces, so that a pair this gate offers is a pair the run accepts. The single-Delay DROP loss-network exception handled by solver_nc_lossn_analyzer is preserved.

run_analyzer_checks(options)

NC feature gate. Unlike the base gate this does not raise on an unrecognized method name: NC intentionally tolerates internal/auto names (e.g. ‘default/comomld’) and, as a SolverLN layer backend, runs with checks disabled. Only method-aware feature applicability is enforced.

static getFeatureSet()[source]

Get supported features as a SolverFeatureSet.

NC supports limited features - notably not Cache with LRU replacement.

static supports(model)[source]

Check if model is supported.

Uses feature set checking to compare supported features against features used by the model. Prints warnings for unsupported features.

static defaultOptions()[source]

Get default solver options.

getProb(station=None)[source]

The LOG probability of the declared state at a station.

This is @SolverNC/getProb.m, and it returns a LOGARITHM even though its name does not say so: solver_nc_marg’s first output is lPr and both the MATLAB and the JAR reference hand it back unexponentiated (Pnir = ret.lPr; return Pnir.get(ist)). Its sibling getProbSys returns a plain probability, so the inconsistency is SolverNC’s own and is reproduced rather than repaired in one codebase alone.

It used to return (1-rho) * rho^n over a guessed range – an M/M/1 queue-length curve fitted to the mean utilization, which is neither this getter’s quantity nor any NC quantity, and which agreed with the two references on nothing. getProbAggr is the aggregate station probability and getProbMarg the queue-length law; neither was affected.

Parameters:

station (int | None) – Station index (0-based). With None, every station’s value.

Returns:

The log probability, or the per-station vector of them.

Return type:

float

getProbAggr(ist)[source]

Get probability of a specific per-class job distribution at a station.

Returns P(n1 jobs of class 1, n2 jobs of class 2, …) for the state that was set via setState() on the station.

Parameters:

ist – Station index (0-based) or node object

Returns:

Probability that station ist is in the specified state.

Return type:

float

getProbMarg(station, jobclass=None)[source]

Get marginal queue-length distribution at station.

Two routes, as in @SolverNC/getProbMarg.m and in the C++ solver_nc_getprob_marg: under method=’comom’ the whole vector comes from one pfqn_procomom solve, and otherwise every total n is written as a sum of the AGGREGATE marginal over the per-class partitions of n.

THE PROCOMOM ROUTE HAS NO ROW FOR A DELAY STATION – it is solved on the Seidmann-reduced model, where the delays are folded into Z. Returning zeros there, which this getter used to do, is not a marginal: a delay in a closed network holds jobs with probability one at some n, and the law is the one the reference reaches by enumeration. So a delay station falls back to the enumeration rather than reporting an impossible station, exactly as the reference warns and falls back.

Parameters:
  • station – Station index (0-based) or station object

  • jobclass – Job class index (unused here, kept for API compat)

Returns:

Marginal probability vector P(n_total = j) for j=0,1,…,sumN

Return type:

ndarray

getProbSys()[source]

Get joint system state probability for the detailed state.

For closed networks, this computes the probability of the current system state using the normalizing constant.

Returns:

Joint probability of the current system state.

Return type:

float

getProbSysAggr()[source]

Get aggregated system state probability.

Computes the joint probability of observing the current queue length distribution across all stations using normalizing constants.

Matches MATLAB: SolverNC.getProbSysAggr -> solver_nc_jointaggr

Returns:

Joint probability of the current system state.

Return type:

float

getProbSysMarg(nvec, engine='exact')[source]

Joint probability of the per-station TOTAL queue lengths.

Returns P(n_1 = nvec[0], …, n_M = nvec[M-1]), all classes summed out.

Compare with getProbSysAggr, which fixes the PER-CLASS population of every station and is a product form; each value returned here is the sum of getProbSysAggr over every per-class table with these row sums. Compare also with getProbMarg, which is the one-station marginal of this law.

The quantity is a matrix permanent of the demand matrix replicated once per job (Ryser 1963 for the evaluation), so it needs no enumeration of that fibre.

Parameters:
  • nvec – (nstations,) per-station total job counts; must sum to the total closed population

  • engine (str) – permanent engine, one of ‘exact’ (default), ‘bethe’, ‘spm’, ‘heur’, ‘huberlaw’, ‘adapart’. Only ‘exact’ is exact; the others are refused on a demand matrix with a structural zero rather than having it floored, since they need full support.

Returns:

the joint probability and its logarithm, which survives populations Pn underflows at

Return type:

(Pn, lPn)

getAvgQLenChain()[source]

Get average queue lengths aggregated by chain.

getAvgUtilChain()[source]

Get average utilizations aggregated by chain.

getAvgRespTChain()[source]

Get average response times aggregated by chain.

getAvgResidTChain()[source]

Get average residence times aggregated by chain.

getAvgTputChain()[source]

Get average throughputs aggregated by chain.

getAvgArvRChain()[source]

Get average arrival rates aggregated by chain.

getAvgChain()[source]

Get all average metrics aggregated by chain.

getAvgChainTable()[source]

Get average metrics by chain as DataFrame.

getAvgNode()[source]

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Router/VSink).

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgNodeTable()[source]

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvgCacheTable()[source]

Detailed per-class cache performance metrics (see cache_table).

get_avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

getAvgItemTable()[source]

Item-level cache occupancy table (see cache_table).

get_avg_item_table()

Item-level cache occupancy table (see cache_table).

avg_item_table()

Item-level cache occupancy table (see cache_table).

getAvgNodeChain()[source]

Get average metrics by node and chain.

getAvgNodeChainTable()[source]

Get average metrics by node and chain as DataFrame.

getAvgNodeQLenChain()[source]

Get average queue lengths by node aggregated by chain.

getAvgNodeUtilChain()[source]

Get average utilizations by node aggregated by chain.

getAvgNodeRespTChain()[source]

Get average response times by node aggregated by chain.

getAvgNodeResidTChain()[source]

Get average residence times by node aggregated by chain.

getAvgNodeTputChain()[source]

Get average throughputs by node aggregated by chain.

getAvgNodeArvRChain()[source]

Get average arrival rates by node aggregated by chain.

getTranAvg()[source]

Get transient average metrics (not supported for NC).

NC is a steady-state solver. Returns steady-state values.

Returns:

Tuple of (Q, U, T) steady-state values

Return type:

Tuple[ndarray, ndarray, ndarray]

getAvgSys()[source]

Get system-level average metrics.

getAvgSysTable()[source]

Get system-level metrics as DataFrame.

Returns chain-level metrics matching MATLAB/Java implementation. The table includes: - Chain: Chain name (Chain1, Chain2, …) - JobClasses: Class names within each chain - SysRespT: Chain response time - SysTput: Chain throughput

me_open(options=None)[source]

Maximum Entropy Method (MEM) for open queueing networks.

Implements the Kouvatsos (1994) entropy-maximisation algorithm (mirrors MATLAB @SolverNC/me_open.m and Java SolverNC.meOpen). Supports only open networks (no closed classes).

Parameters:

options (Any | None) – optional solver options; MEM tolerance/iteration limits are read from options.config (mem_tol, mem_maxiter, mem_verbose).

Returns:

dict with QN, UN, RN, TN (M x R arrays for queue lengths, utilizations, response times, throughputs), CN/XN (1 x R system response times and throughputs) and method='mem'.

Return type:

Dict[str, Any]

Reference:

D.D. Kouvatsos, “Entropy Maximisation and Queueing Network Models”, Annals of Operations Research, 48:63-126, 1994.

sample(node, numEvents)[source]

Sampling not supported by NC (analytical solver).

GetProb(station=None)

The LOG probability of the declared state at a station.

This is @SolverNC/getProb.m, and it returns a LOGARITHM even though its name does not say so: solver_nc_marg’s first output is lPr and both the MATLAB and the JAR reference hand it back unexponentiated (Pnir = ret.lPr; return Pnir.get(ist)). Its sibling getProbSys returns a plain probability, so the inconsistency is SolverNC’s own and is reproduced rather than repaired in one codebase alone.

It used to return (1-rho) * rho^n over a guessed range – an M/M/1 queue-length curve fitted to the mean utilization, which is neither this getter’s quantity nor any NC quantity, and which agreed with the two references on nothing. getProbAggr is the aggregate station probability and getProbMarg the queue-length law; neither was affected.

Parameters:

station (int | None) – Station index (0-based). With None, every station’s value.

Returns:

The log probability, or the per-station vector of them.

Return type:

float

GetProbAggr(ist)

Get probability of a specific per-class job distribution at a station.

Returns P(n1 jobs of class 1, n2 jobs of class 2, …) for the state that was set via setState() on the station.

Parameters:

ist – Station index (0-based) or node object

Returns:

Probability that station ist is in the specified state.

Return type:

float

GetProbMarg(station, jobclass=None)

Get marginal queue-length distribution at station.

Two routes, as in @SolverNC/getProbMarg.m and in the C++ solver_nc_getprob_marg: under method=’comom’ the whole vector comes from one pfqn_procomom solve, and otherwise every total n is written as a sum of the AGGREGATE marginal over the per-class partitions of n.

THE PROCOMOM ROUTE HAS NO ROW FOR A DELAY STATION – it is solved on the Seidmann-reduced model, where the delays are folded into Z. Returning zeros there, which this getter used to do, is not a marginal: a delay in a closed network holds jobs with probability one at some n, and the law is the one the reference reaches by enumeration. So a delay station falls back to the enumeration rather than reporting an impossible station, exactly as the reference warns and falls back.

Parameters:
  • station – Station index (0-based) or station object

  • jobclass – Job class index (unused here, kept for API compat)

Returns:

Marginal probability vector P(n_total = j) for j=0,1,…,sumN

Return type:

ndarray

GetProbSys()

Get joint system state probability for the detailed state.

For closed networks, this computes the probability of the current system state using the normalizing constant.

Returns:

Joint probability of the current system state.

Return type:

float

GetProbSysAggr()

Get aggregated system state probability.

Computes the joint probability of observing the current queue length distribution across all stations using normalizing constants.

Matches MATLAB: SolverNC.getProbSysAggr -> solver_nc_jointaggr

Returns:

Joint probability of the current system state.

Return type:

float

GetAvg()

Average station metrics (Q, U, R, T, A, W) as station x class matrices.

Single analyzer funnel of the native solvers, mirroring MATLAB @NetworkSolver/getAvg.m and JAR NetworkSolver.getAvg(): it runs the analyzer if there is no cached result, then reads the averages from whichever result store the solver uses. Every solver used to carry its own copy of this body, differing only in that store (‘_result’ vs ‘result’) and in the field naming (‘QN’ vs ‘Q’), which _AVG_FIELDS already reconciles; the duplication also meant there was no single place to intercept a solve, as the other two codebases have.

Returns:

queue lengths, utilizations, response times, throughputs, arrival rates and residence times.

Return type:

(Q, U, R, T, A, W)

GetAvgTable()

Get comprehensive average performance metrics table.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

GetAvgQLen()

Get average queue lengths (M x K).

GetAvgUtil()

Get average utilizations (M x K).

GetAvgRespT()

Get average response times (M x K).

GetAvgResidT()

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

GetAvgWaitT()

Get average waiting times (M x K).

GetAvgTput()

Get average throughputs (M x K).

GetAvgArvR()

Get average arrival rates (M x K).

Uses routing matrix to compute proper arrival rates. Source stations have arrival rate = 0.

GetAvgSysRespT()

Get system response times (cycle times) per chain (nchains,).

Returns chain-level response times matching MATLAB/Java implementation. Uses the completes flag to determine which classes contribute to chain throughput.

Note

For closed chains: uses Little’s Law CNchain = nJobsChain / XNchain For open chains: weighted sum of class response times

GetAvgSysTput()

Get system throughputs per chain (nchains,).

Returns chain-level throughputs matching MATLAB/Java implementation. Uses the completes flag to determine which classes contribute to chain throughput.

GetAvgChain()

Get all average metrics aggregated by chain.

GetAvgChainTable()

Get average metrics by chain as DataFrame.

GetAvgNode()

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Router/VSink).

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

GetAvgNodeTable()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

GetAvgNodeChain()

Get average metrics by node and chain.

GetAvgNodeChainTable()

Get average metrics by node and chain as DataFrame.

GetAvgSys()

Get system-level average metrics.

GetAvgSysTable()

Get system-level metrics as DataFrame.

Returns chain-level metrics matching MATLAB/Java implementation. The table includes: - Chain: Chain name (Chain1, Chain2, …) - JobClasses: Class names within each chain - SysRespT: Chain response time - SysTput: Chain throughput

GetAvgQLenChain()

Get average queue lengths aggregated by chain.

GetAvgUtilChain()

Get average utilizations aggregated by chain.

GetAvgRespTChain()

Get average response times aggregated by chain.

GetAvgResidTChain()

Get average residence times aggregated by chain.

GetAvgTputChain()

Get average throughputs aggregated by chain.

GetAvgArvRChain()

Get average arrival rates aggregated by chain.

GetNormalizingConstant()

Get the normalizing constant G.

Returns:

The normalizing constant (not log)

Return type:

float

GetLogNormalizingConstant()

Get the log normalizing constant log(G).

Returns:

log(G)

Return type:

float

GetProbNormConstAggr()

Get the log normalizing constant (alias).

Returns:

log(G)

Return type:

float

GetCdfRespT(R=None)

Get response time CDF using exponential approximation.

Ports MATLAB’s @SolverNC/getCdfRespT.m: the EXACT product-form sojourn law at FCFS stations via pfqn_stdf, not the base-class exponential approximation. Delay stations carry their own service CDF.

options.config[‘algorithm’] selects ‘exact’ (pfqn_stdf, default) or ‘rd’ (pfqn_stdf_heur), as the reference does.

Returns:

List of dicts with ‘station’, ‘class’, ‘t’, ‘p’ keys. Only FCFS and delay stations are populated, which is exactly the set the reference fills; a PS or LCFS queue has no entry.

Raises:

ValueError – on an open class. The tagged-job passage time is defined on a CLOSED network.

Return type:

List[Dict]

GetPerctRespT(percentiles=None, jobclass=None, method='default')

Extract percentiles from response time distribution.

Parameters:
  • percentiles (List[float] | None) – List of percentiles (0-100). Default: [10, 25, 50, 75, 90, 95, 99]

  • jobclass (int | None) – Optional class filter (1-based)

Returns:

Tuple of (percentile_list, percentile_table)

Return type:

Tuple[List[Dict], DataFrame]

MeOpen(options=None)

Maximum Entropy Method (MEM) for open queueing networks.

Implements the Kouvatsos (1994) entropy-maximisation algorithm (mirrors MATLAB @SolverNC/me_open.m and Java SolverNC.meOpen). Supports only open networks (no closed classes).

Parameters:

options (Any | None) – optional solver options; MEM tolerance/iteration limits are read from options.config (mem_tol, mem_maxiter, mem_verbose).

Returns:

dict with QN, UN, RN, TN (M x R arrays for queue lengths, utilizations, response times, throughputs), CN/XN (1 x R system response times and throughputs) and method='mem'.

Return type:

Dict[str, Any]

Reference:

D.D. Kouvatsos, “Entropy Maximisation and Queueing Network Models”, Annals of Operations Research, 48:63-126, 1994.

ListValidMethods()

List valid solution methods.

Returns:

  • ‘default’: Auto-select based on problem size

  • ’exact’, ‘ca’: Exact convolution algorithm

  • ’imci’: Importance sampling Monte Carlo integration

  • ’ls’: Linearizer method

  • ’le’: Logistic expansion (Cas17)

  • ’ble’: Logistic expansion with an empirical correction to LE

  • ’mmint2’: Gauss-Legendre quadrature

  • ’gleint’: Gauss-Legendre integration

  • ’pana’: PANACEA asymptotic expansion (load-independent)

  • ’panald’: PANACEA asymptotic expansion (load-dependent)

  • ’kt’: Knessl-Tier expansion

  • ’bkt’: Knessl-Tier expansion with the Stirling-remainder correction (BKT)

  • ’lekt’: the estimator ‘ble’ and ‘bkt’ both compute, on the cheaper side

  • ’sampling’: Monte Carlo sampling

  • ’propfair’: Proportionally fair allocation

  • ’divdiff’: divided-difference closed form (Casale, SIGMETRICS 2017), no think time; load-dependent rates go through the limited load-dependent kernel of Casale-Harrison-Ong (Perform. Eval. 2021)

  • ’rgf’: Recursion by generating functions (single-class, grouped stations)

  • ’ger’: Gerasimov residue closed form (free in the eliminated class populations, costly in the class count)

  • ’comom’: Conditional moments

  • ’comomld’: Conditional moments, load-dependent

  • ’cub’: Controllable upper bound

  • ’gm’: Grundmann-Moeller cubature (alias of ‘cub’)

  • ’rd’: Reduction heuristic

  • ’nrl’: Norlund-Rice Logit approximation

  • ’nrp’: Norlund-Rice Probit approximation

  • ’nre’: Norlund-Rice saddle-tilted Edgeworth approximation

  • ’ms’: Manjunath-Sikdar transform of the loss-network analyzer, the only place it is admissible

  • ’sdr’, ‘sdr.mva’: Krzesinski state-dependent routing, the eq. (16) enumeration and its Section 4 MVA arm

  • ’morrison’: heavy-usage expansion for a closed think+DPS network

  • ’rec’: memoised decision-diagram walk of the reachable set, for product-form Petri nets and loss networks

  • ’mcmc’: Chen-O’Cinneide regularization, a Markov chain Monte Carlo estimator of the throughput ratios and the queue lengths

Return type:

List of valid method names for NC solver

‘ms’, ‘sdr’ and ‘sdr.mva’ are DISPATCHED here (runAnalyzer and api/solvers/nc/analyzers.py) and were missing from this list, so the shared gate in NetworkSolver.runAnalyzerChecks refused three methods this solver implements and the other three codebases advertise.

static GetFeatureSet()

Get supported features as a SolverFeatureSet.

NC supports limited features - notably not Cache with LRU replacement.

static Supports(model)

Check if model is supported.

Uses feature set checking to compare supported features against features used by the model. Prints warnings for unsupported features.

static DefaultOptions()

Get default solver options.

static default_options()

Get default solver options.

GetTranAvg()

Get transient average metrics (not supported for NC).

NC is a steady-state solver. Returns steady-state values.

Returns:

Tuple of (Q, U, T) steady-state values

Return type:

Tuple[ndarray, ndarray, ndarray]

GetAvgNodeQLenChain()

Get average queue lengths by node aggregated by chain.

GetAvgNodeUtilChain()

Get average utilizations by node aggregated by chain.

GetAvgNodeRespTChain()

Get average response times by node aggregated by chain.

GetAvgNodeResidTChain()

Get average residence times by node aggregated by chain.

GetAvgNodeTputChain()

Get average throughputs by node aggregated by chain.

GetAvgNodeArvRChain()

Get average arrival rates by node aggregated by chain.

aT()

Get comprehensive average performance metrics table.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aNT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aCT()

Get average metrics by chain as DataFrame.

aNCT()

Get average metrics by node and chain as DataFrame.

aST()

Get system-level metrics as DataFrame.

Returns chain-level metrics matching MATLAB/Java implementation. The table includes: - Chain: Chain name (Chain1, Chain2, …) - JobClasses: Class names within each chain - SysRespT: Chain response time - SysTput: Chain throughput

avgT()

Get comprehensive average performance metrics table.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

nodeAvgT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

chainAvgT()

Get average metrics by chain as DataFrame.

nodeChainAvgT()

Get average metrics by node and chain as DataFrame.

sysAvgT()

Get system-level metrics as DataFrame.

Returns chain-level metrics matching MATLAB/Java implementation. The table includes: - Chain: Chain name (Chain1, Chain2, …) - JobClasses: Class names within each chain - SysRespT: Chain response time - SysTput: Chain throughput

avg_sys_table()

Get system-level metrics as DataFrame.

Returns chain-level metrics matching MATLAB/Java implementation. The table includes: - Chain: Chain name (Chain1, Chain2, …) - JobClasses: Class names within each chain - SysRespT: Chain response time - SysTput: Chain throughput

avg_node_table()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Router/VSink.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

avg_chain_table()

Get average metrics by chain as DataFrame.

avg_node_chain_table()

Get average metrics by node and chain as DataFrame.

run_analyzer()

Run the NC analysis.

get_normalizing_constant()

Get the normalizing constant G.

Returns:

The normalizing constant (not log)

Return type:

float

get_log_normalizing_constant()

Get the log normalizing constant log(G).

Returns:

log(G)

Return type:

float

prob(station=None)

The LOG probability of the declared state at a station.

This is @SolverNC/getProb.m, and it returns a LOGARITHM even though its name does not say so: solver_nc_marg’s first output is lPr and both the MATLAB and the JAR reference hand it back unexponentiated (Pnir = ret.lPr; return Pnir.get(ist)). Its sibling getProbSys returns a plain probability, so the inconsistency is SolverNC’s own and is reproduced rather than repaired in one codebase alone.

It used to return (1-rho) * rho^n over a guessed range – an M/M/1 queue-length curve fitted to the mean utilization, which is neither this getter’s quantity nor any NC quantity, and which agreed with the two references on nothing. getProbAggr is the aggregate station probability and getProbMarg the queue-length law; neither was affected.

Parameters:

station (int | None) – Station index (0-based). With None, every station’s value.

Returns:

The log probability, or the per-station vector of them.

Return type:

float

prob_aggr(ist)

Get probability of a specific per-class job distribution at a station.

Returns P(n1 jobs of class 1, n2 jobs of class 2, …) for the state that was set via setState() on the station.

Parameters:

ist – Station index (0-based) or node object

Returns:

Probability that station ist is in the specified state.

Return type:

float

prob_marg(station, jobclass=None)

Get marginal queue-length distribution at station.

Two routes, as in @SolverNC/getProbMarg.m and in the C++ solver_nc_getprob_marg: under method=’comom’ the whole vector comes from one pfqn_procomom solve, and otherwise every total n is written as a sum of the AGGREGATE marginal over the per-class partitions of n.

THE PROCOMOM ROUTE HAS NO ROW FOR A DELAY STATION – it is solved on the Seidmann-reduced model, where the delays are folded into Z. Returning zeros there, which this getter used to do, is not a marginal: a delay in a closed network holds jobs with probability one at some n, and the law is the one the reference reaches by enumeration. So a delay station falls back to the enumeration rather than reporting an impossible station, exactly as the reference warns and falls back.

Parameters:
  • station – Station index (0-based) or station object

  • jobclass – Job class index (unused here, kept for API compat)

Returns:

Marginal probability vector P(n_total = j) for j=0,1,…,sumN

Return type:

ndarray

prob_sys()

Get joint system state probability for the detailed state.

For closed networks, this computes the probability of the current system state using the normalizing constant.

Returns:

Joint probability of the current system state.

Return type:

float

prob_sys_aggr()

Get aggregated system state probability.

Computes the joint probability of observing the current queue length distribution across all stations using normalizing constants.

Matches MATLAB: SolverNC.getProbSysAggr -> solver_nc_jointaggr

Returns:

Joint probability of the current system state.

Return type:

float

prob_norm_const_aggr()

Get the log normalizing constant (alias).

Returns:

log(G)

Return type:

float

class SolverSSA(model, method_or_options=None, **kwargs)[source]

Bases: FJTagTransformMixin, NetworkSolver

Native Python SSA (Stochastic Simulation Algorithm) solver.

This solver analyzes queueing networks through discrete-event simulation using Gillespie’s algorithm in pure Python/NumPy.

Supported methods:
  • ‘default’: Serial Gillespie simulation

  • ‘serial’: Same as default

  • ‘ssa’: Same as default

Parameters:
  • model – Network model (Python wrapper or native structure)

  • method – Solution method (default: ‘default’)

  • **kwargs – Additional solver options

runAnalyzer()[source]

Run the SSA analysis.

getAvgTable()[source]

Get performance metrics table.

getAvgQLen()[source]

Get average queue lengths.

getAvgUtil()[source]

Get average utilizations.

getAvgRespT()[source]

Get average response times.

getAvgResidT()[source]

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

getAvgWaitT()[source]

Get average waiting times.

getAvgTput()[source]

Get average throughputs.

getStartRate()[source]

(nstations x nclasses) rate at which a class-r job BEGINS or RESUMES holding a server at station i, estimated over the simulated path.

At a lossless station with no in-service abandonment

getStartRate == getAvgTput + getPreemptRate

up to simulation error; SolverCTMC.getStartRate reports the exact value, so the two are compared with a two-sample t-test rather than an equality.

getPreemptRate()[source]

(nstations x nclasses) rate at which a class-r job HOLDING A SERVER at station i is pushed back into the buffer. Zero at a non-preemptive station.

getAvgArvR()[source]

Get average arrival rates.

getAvgSysRespT()[source]

Get system response times.

Note

For closed networks: uses Little’s Law C = N/X For open networks: sum of response times across all stations

getAvgSysTput()[source]

Get system throughputs.

getConfidenceIntervals()[source]

Get confidence intervals for all metrics.

Returns:

Dict with keys ‘Q_ci’, ‘U_ci’, ‘R_ci’, ‘T_ci’

Return type:

Dict[str, ndarray]

getTotalSimulatedTime()[source]

Get total simulated time.

getSampleCount()[source]

Get number of samples collected.

sample(node, numEvents=None)[source]

Generate a sample path with event traces at the given node.

Runs SSA simulation with event logging and returns a SamplePath object with event list matching MATLAB’s sampleNodeState format.

Parameters:
  • node – Node object or node index (1-based)

  • numEvents (int) – Number of events to simulate (default: solver’s samples option)

Returns:

SamplePath with .event list of SampleEvent objects

Return type:

SamplePath

sampleAggr(node, numEvents)[source]

Sample aggregated response times.

sampleSys(numEvents=None, aggregate=False)[source]

The system sample path: the state of EVERY stateful node over time.

state is a list with one block per stateful node, each block holding that node’s rows at the epochs in t, so the k-th row of every block is one instant of the system. With aggregate the blocks carry per-class job counts instead of the raw rows, which is sampleSysAggr.

This used to answer np.random.exponential(mean(C), numEvents): draws from a fitted exponential, returned under a getter whose contract is the simulated trajectory. Nothing about them came from the run, they were not even response times of the right model, and a caller could not tell.

getCdfRespT(R=None)[source]

Not available: SolverSSA does not record per-job response times.

A simulator must report what it measured. The base exponential fit carries no information about the tail and would be indistinguishable, to the caller, from a measured distribution. SSA samples state trajectories, not per-job sojourn times, so there is nothing to build an empirical CDF from – the reference @SolverSSA/getCdfRespT.m refuses by name, line-cli refuses -s ssa -a cdf, and so does this port.

getPerctRespT(percentiles=None, jobclass=None, method=None)[source]

Extract percentiles from response time distribution.

SSA records no per-job response times, so the default route – reading getCdfRespT, as the reference’s @NetworkSolver/getPerctRespT.m does – errors like the reference; only the ForkTail approximation is served. This used to fabricate exponential percentiles from the mean, which is indistinguishable, to the caller, from a measured tail.

getProb(station=None)[source]

Get probability for the state set on a station.

Returns the probability that the station is in the state that was set via setState(). Uses simulation statistics to estimate probability.

Parameters:

station – Station object or index (0-based).

Returns:

Probability (scalar float) for the specified state.

Return type:

float

getProbAggr(station)[source]

Get aggregated state probability at station.

Returns the probability that the station is in the aggregated state (per-class job counts) that was set via setState().

Parameters:

station – Station object or index (0-based).

Returns:

Probability (scalar float) for the specified aggregated state.

Return type:

float

getProbSys()[source]

Get joint system state probability.

Returns the probability that the entire system is in the state that was set via setState() on all stations, measured as the fraction of simulated time the run spent jointly in it.

Returns:

Joint probability (scalar float) for the system state.

Return type:

float

getProbSysAggr()[source]

Get system-level aggregated joint probability.

Returns the joint probability for the aggregated system state (per-class job counts at each station).

Returns:

Joint probability (scalar float) for the aggregated system state.

Return type:

float

getTranCdfRespT(t_max=10.0, n_points=100)[source]

Not supported, as in the reference, whose base class raises.

getTranCdfPassT(*args, **kwargs)[source]

Not supported, as in the reference, whose base class raises.

GetProb(station=None)

Get probability for the state set on a station.

Returns the probability that the station is in the state that was set via setState(). Uses simulation statistics to estimate probability.

Parameters:

station – Station object or index (0-based).

Returns:

Probability (scalar float) for the specified state.

Return type:

float

GetProbAggr(station)

Get aggregated state probability at station.

Returns the probability that the station is in the aggregated state (per-class job counts) that was set via setState().

Parameters:

station – Station object or index (0-based).

Returns:

Probability (scalar float) for the specified aggregated state.

Return type:

float

GetProbSys()

Get joint system state probability.

Returns the probability that the entire system is in the state that was set via setState() on all stations, measured as the fraction of simulated time the run spent jointly in it.

Returns:

Joint probability (scalar float) for the system state.

Return type:

float

GetProbSysAggr()

Get system-level aggregated joint probability.

Returns the joint probability for the aggregated system state (per-class job counts at each station).

Returns:

Joint probability (scalar float) for the aggregated system state.

Return type:

float

GetTranCdfRespT(t_max=10.0, n_points=100)

Not supported, as in the reference, whose base class raises.

GetTranCdfPassT(*args, **kwargs)

Not supported, as in the reference, whose base class raises.

getAvgQLenChain()[source]

Get average queue lengths aggregated by chain.

getAvgUtilChain()[source]

Get average utilizations aggregated by chain.

getAvgRespTChain()[source]

Get average response times aggregated by chain.

Uses alpha-weighted sum matching MATLAB: RN(:,c) = sum(RNclass(:,inchain).*alpha(:,inchain),2)

getAvgResidTChain()[source]

Get average residence times aggregated by chain.

getAvgTputChain()[source]

Get average throughputs aggregated by chain.

getAvgArvRChain()[source]

Get average arrival rates aggregated by chain.

getAvgChain()[source]

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgChainTable()[source]

Get average metrics by chain as DataFrame.

getAvgNode()[source]

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Cache). For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

getAvgNodeTable()[source]

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvgCacheTable()[source]

Detailed per-class cache performance metrics (see cache_table).

get_avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

avg_cache_table()

Detailed per-class cache performance metrics (see cache_table).

getAvgItemTable()[source]

Item-level cache occupancy table (see cache_table).

get_avg_item_table()

Item-level cache occupancy table (see cache_table).

avg_item_table()

Item-level cache occupancy table (see cache_table).

getAvgNodeChain()[source]

Get average metrics by node and chain.

getAvgNodeChainTable()[source]

Get average metrics by node and chain as DataFrame.

getAvgNodeQLenChain()[source]

Get average queue lengths by node aggregated by chain.

getAvgNodeUtilChain()[source]

Get average utilizations by node aggregated by chain.

getAvgNodeRespTChain()[source]

Get average response times by node aggregated by chain.

getAvgNodeResidTChain()[source]

Get average residence times by node aggregated by chain.

getAvgNodeTputChain()[source]

Get average throughputs by node aggregated by chain.

getAvgNodeArvRChain()[source]

Get average arrival rates by node aggregated by chain.

getTranAvg()[source]

Get transient average metrics from simulation.

SSA provides transient metrics from the simulation trajectory.

Returns:

Tuple of (Q, U, T) transient queue lengths, utilizations, and throughputs

Return type:

Tuple[ndarray, ndarray, ndarray]

getAvgSys()[source]

Get system-level average metrics.

Returns:

Tuple of (R, T) where R is system response time and T is system throughput

Return type:

Tuple[ndarray, ndarray]

getAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

sampleSysAggr(numEvents=None)[source]

The system sample path with per-class job counts; see sampleSys.

listValidMethods()[source]

List valid solution methods.

Returns:

  • ‘default’, ‘serial’, ‘ssa’: Serial Gillespie simulation

  • ’nrm’: Next Reaction Method

  • ’parallel’, ‘para’: Parallel simulation with multiple replicas

Return type:

List of valid method names

isStochasticMethod(method)[source]

All SSA methods are stochastic simulation.

is_stochastic_method(method)

All SSA methods are stochastic simulation.

supportsModelMethod(method)[source]

The fork-join model class, which EVERY SSA method has to clear.

runAnalyzer tag-augments a fork-join model through ModelAdapter.fjtag, whose first act is sn_fj_validate, so a model that validator refuses is refused whichever method was asked for. The featset cannot state it – Fork and Join are declared, and the rules are about how they are WIRED (the pairing, the join strategy, the tasks per link, whether an open class is routed through the fork) – so it is structural, and it is the validator’s own body of rules rather than a copy of them.

Without it the report offered every ssa.* row on a fork-join model whose Join names no fork, and each one then raised; SolverCTMC gates on the same predicate for the same reason. Mirrors MATLAB @SolverSSA/supportsModelMethod.

supports_model_method(method)

The fork-join model class, which EVERY SSA method has to clear.

runAnalyzer tag-augments a fork-join model through ModelAdapter.fjtag, whose first act is sn_fj_validate, so a model that validator refuses is refused whichever method was asked for. The featset cannot state it – Fork and Join are declared, and the rules are about how they are WIRED (the pairing, the join strategy, the tasks per link, whether an open class is routed through the fork) – so it is structural, and it is the validator’s own body of rules rather than a copy of them.

Without it the report offered every ssa.* row on a fork-join model whose Join names no fork, and each one then raised; SolverCTMC gates on the same predicate for the same reason. Mirrors MATLAB @SolverSSA/supportsModelMethod.

static getFeatureSet()[source]

Get supported features.

The native SSA solver now drives the same State.afterEvent machinery as the CTMC solver, so its capability set mirrors CTMC’s (plus PAS).

static supports(model)[source]

Check if model is supported.

Mirrors MATLAB SolverSSA.supports: gates the model’s used language features against getFeatureSet(). This previously checked only the station and class counts, so it accepted every model regardless of the features it used. Struct-like inputs without a feature registry fall back to a structural sanity check.

static defaultOptions()[source]

Get default solver options.

GetAvg()

Average station metrics (Q, U, R, T, A, W) as station x class matrices.

Single analyzer funnel of the native solvers, mirroring MATLAB @NetworkSolver/getAvg.m and JAR NetworkSolver.getAvg(): it runs the analyzer if there is no cached result, then reads the averages from whichever result store the solver uses. Every solver used to carry its own copy of this body, differing only in that store (‘_result’ vs ‘result’) and in the field naming (‘QN’ vs ‘Q’), which _AVG_FIELDS already reconciles; the duplication also meant there was no single place to intercept a solve, as the other two codebases have.

Returns:

queue lengths, utilizations, response times, throughputs, arrival rates and residence times.

Return type:

(Q, U, R, T, A, W)

GetAvgTable()

Get performance metrics table.

GetAvgQLen()

Get average queue lengths.

GetAvgUtil()

Get average utilizations.

GetAvgRespT()

Get average response times.

GetAvgResidT()

Get average residence times (M x K).

Residence time is computed from response time using visit ratios: WN[ist,k] = RN[ist,k] * V[ist,k] / V[refstat,refclass]

GetAvgWaitT()

Get average waiting times.

GetAvgTput()

Get average throughputs.

GetAvgArvR()

Get average arrival rates.

GetAvgSysRespT()

Get system response times.

Note

For closed networks: uses Little’s Law C = N/X For open networks: sum of response times across all stations

GetAvgSysTput()

Get system throughputs.

GetCdfRespT(R=None)

Not available: SolverSSA does not record per-job response times.

A simulator must report what it measured. The base exponential fit carries no information about the tail and would be indistinguishable, to the caller, from a measured distribution. SSA samples state trajectories, not per-job sojourn times, so there is nothing to build an empirical CDF from – the reference @SolverSSA/getCdfRespT.m refuses by name, line-cli refuses -s ssa -a cdf, and so does this port.

GetPerctRespT(percentiles=None, jobclass=None, method=None)

Extract percentiles from response time distribution.

SSA records no per-job response times, so the default route – reading getCdfRespT, as the reference’s @NetworkSolver/getPerctRespT.m does – errors like the reference; only the ForkTail approximation is served. This used to fabricate exponential percentiles from the mean, which is indistinguishable, to the caller, from a measured tail.

ListValidMethods()

List valid solution methods.

Returns:

  • ‘default’, ‘serial’, ‘ssa’: Serial Gillespie simulation

  • ’nrm’: Next Reaction Method

  • ’parallel’, ‘para’: Parallel simulation with multiple replicas

Return type:

List of valid method names

static GetFeatureSet()

Get supported features.

The native SSA solver now drives the same State.afterEvent machinery as the CTMC solver, so its capability set mirrors CTMC’s (plus PAS).

static Supports(model)

Check if model is supported.

Mirrors MATLAB SolverSSA.supports: gates the model’s used language features against getFeatureSet(). This previously checked only the station and class counts, so it accepted every model regardless of the features it used. Struct-like inputs without a feature registry fall back to a structural sanity check.

static DefaultOptions()

Get default solver options.

static default_options()

Get default solver options.

GetAvgChain()

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

GetAvgChainTable()

Get average metrics by chain as DataFrame.

GetAvgQLenChain()

Get average queue lengths aggregated by chain.

GetAvgUtilChain()

Get average utilizations aggregated by chain.

GetAvgRespTChain()

Get average response times aggregated by chain.

Uses alpha-weighted sum matching MATLAB: RN(:,c) = sum(RNclass(:,inchain).*alpha(:,inchain),2)

GetAvgResidTChain()

Get average residence times aggregated by chain.

GetAvgTputChain()

Get average throughputs aggregated by chain.

GetAvgArvRChain()

Get average arrival rates aggregated by chain.

GetAvgNode()

Get average metrics per node.

Unlike getAvg() which returns station-level metrics, this method returns node-level metrics including non-station nodes (e.g., Cache). For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

Return type:

Tuple[ndarray, ndarray, ndarray, ndarray, ndarray, ndarray]

GetAvgNodeTable()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

GetAvgNodeChain()

Get average metrics by node and chain.

GetAvgNodeChainTable()

Get average metrics by node and chain as DataFrame.

GetAvgNodeQLenChain()

Get average queue lengths by node aggregated by chain.

GetAvgNodeUtilChain()

Get average utilizations by node aggregated by chain.

GetAvgNodeRespTChain()

Get average response times by node aggregated by chain.

GetAvgNodeResidTChain()

Get average residence times by node aggregated by chain.

GetAvgNodeTputChain()

Get average throughputs by node aggregated by chain.

GetAvgNodeArvRChain()

Get average arrival rates by node aggregated by chain.

GetAvgSys()

Get system-level average metrics.

Returns:

Tuple of (R, T) where R is system response time and T is system throughput

Return type:

Tuple[ndarray, ndarray]

GetAvgSysTable()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

GetTranAvg()

Get transient average metrics from simulation.

SSA provides transient metrics from the simulation trajectory.

Returns:

Tuple of (Q, U, T) transient queue lengths, utilizations, and throughputs

Return type:

Tuple[ndarray, ndarray, ndarray]

SampleSysAggr(numEvents=None)

The system sample path with per-class job counts; see sampleSys.

aT()

Get performance metrics table.

aNT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

aCT()

Get average metrics by chain as DataFrame.

aNCT()

Get average metrics by node and chain as DataFrame.

aST()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avgT()

Get performance metrics table.

nodeAvgT()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

chainAvgT()

Get average metrics by chain as DataFrame.

nodeChainAvgT()

Get average metrics by node and chain as DataFrame.

sysAvgT()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_chain_table()

Get average metrics by chain as DataFrame.

avg_sys_table()

System-level metrics table (one row per chain).

Default implementation for solvers whose getAvgSys() returns per-class vectors: chains made of a single class map one-to-one onto the class metrics; for multi-class chains the chain throughput is the sum of the completing per-class throughputs and the chain response time is the throughput-weighted mean of the per-class values. Solvers with native chain-level getAvgSys() (e.g. NC) override this.

avg_node_table()

Get average metrics by node as DataFrame.

Returns node-based results (one row per node per class) including non-station nodes like Cache. For Cache nodes, hit/miss class throughputs are computed using actual hit/miss probabilities.

Returns:

Node, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

avg_node_chain_table()

Get average metrics by node and chain as DataFrame.

External Solver Wrappers

class SolverJMT(model, method_or_options=None, **kwargs)[source]

Bases: NetworkSolver

JMT solver integration.

This solver provides discrete-event simulation and analytical methods via command line is launched as an external process, exactly like MATLAB’s SolverJMT.

Supported methods:
  • ‘jsim’ / ‘default’: Discrete event simulation

  • ‘jmva’ / ‘jmva.mva’: Mean Value Analysis

  • ‘jmva.amva’: Approximate MVA

  • ‘jmva.recal’: RECALsimulation

  • ‘jmva.comom’: CoMoM algorithm

  • ‘jmva.chow’: Chow algorithm

  • ‘jmva.bs’: Bard-Schweitzer

  • ‘jmva.aql’: AQL algorithm

  • ‘jmva.lin’: Linearizer

  • ‘jmva.dmlin’: De Souza-Muntz Linearizer

Parameters:
  • model – Network model (Python wrapper or native structure)

  • method – Solution method (default: ‘jsim’)

  • **kwargs – Additional solver options (samples, seed, etc.)

Example

>>> solver = SolverJMT(model, samples=10000, seed=42)
>>> solver.runAnalyzer()
>>> table = solver.getAvgTable()
supportsTransientAnalysis()[source]

Transient averages are available (simulation restricted to options.timespan).

supports_transient_analysis()

Transient averages are available (simulation restricted to options.timespan).

runAnalyzer()[source]

Run the JMT analyzer.

Calls JMT via command line and stores the results.

Returns:

self for method chaining

Return type:

SolverJMT

getAvgTable()[source]

Get average performance metrics as a DataFrame.

Returns:

Station, Class, QLen, Util, RespT, Tput, ArvR

Return type:

DataFrame with columns

getAvgQLen()[source]

Get average queue lengths (M x K matrix).

getAvgUtil()[source]

Get average utilizations (M x K matrix).

getAvgRespT()[source]

Get average response times (M x K matrix).

getAvgTput()[source]

Get average throughputs (M x K matrix).

getAvgArvR()[source]

Get average arrival rates (M x K matrix).

getAvgFcr()[source]

The finite-capacity-region rows, ((nregions*6) x nclasses) or empty.

WHY A SEPARATE ACCESSOR. getAvg returns the STATION metrics alone, and a region is not a station: its rows live past the last one, which is where getAvgNodeTable reads them from to fill the FCR pseudo-node. A host bridging through getAvg (MATLAB lang=’python’) therefore saw no region at all and dropped the FCR row from its node table (fcr_mm1waitq[M2P], “row FCR1 missing”).

The six blocks are stacked in the order Q, U, R, W, A, T, each (nregions x nclasses), so one marshalled matrix carries all of them. Util and ArvR are NaN: JMT reports neither for a region.

getAvgChainTable()[source]

Get average performance metrics aggregated by chain.

Returns:

Chain, QLen, Util, RespT, Tput

Return type:

DataFrame with columns

getAvgSysTable()[source]

Get system-level average performance metrics.

Returns:

Chain, SysRespT, SysTput

Return type:

DataFrame with columns

getAvgSysRespT()[source]

Get system response times (1 x K).

getAvgSysTput()[source]

Get system throughputs (1 x K).

sampleSysAggr(num_events=None)[source]

Sample system-wide aggregated state trajectories via JMT logging.

Faithful port of MATLAB SolverJMT.sampleSysAggr: all non-Source stations are logged in a temporary model copy, simulated, and their per-class queue-length trajectories reconstructed and interpolated (previous/step) onto a common timeline (the union of all stations’ event times).

Parameters:

num_events (int | None) – Number of events to sample (default: uses options.samples)

Returns:

Dict with ‘t’ (common timeline), ‘state’ (list of per-station len(t) x nclasses matrices; Source stations = [[inf]]), ‘handle’, ‘event’ (chronological event list) and ‘isaggregate’=True.

Return type:

Dict[str, Any] | None

getProbSysAggr()[source]

Get system state probability via simulation sampling.

Uses JMT simulation with logging to estimate the probability of the system being in the current aggregated state (as set via setState).

The probability is computed as the fraction of time the system spends in the target state during simulation.

Note: This method requires Logger nodes to be present in the model for full functionality. If log files are not available, it attempts to estimate probabilities from the simulation’s average metrics.

Returns:

Estimated probability of the current system state.

Returns 0.0 if the state was not observed during simulation.

Return type:

float

getProbAggr(station)[source]

Get the aggregated state probability at a station.

Under lang=’cpp’ this is -s jmt -a prob: one logged JSIM run, dwell-weighted over the time the station holds its declared state. The native path here has no equivalent – JSIM reports means and not state occupancies to this wrapper – and returns an empty array, which is what it has always done.

getRuntime()[source]

Get solver runtime in seconds.

getMethod()[source]

Get the method used.

listValidMethods()[source]

List valid methods for this solver.

isStochasticMethod(method)[source]

Simulation-based methods (default, jsim, replication) return stochastic estimates. The analytical JMVA methods do not, except for the sampling-based variants (e.g. jmva.ls).

is_stochastic_method(method)

Simulation-based methods (default, jsim, replication) return stochastic estimates. The analytical JMVA methods do not, except for the sampling-based variants (e.g. jmva.ls).

static isAvailable()[source]

Check if JMT solver is available.

static getFeatureSet()[source]

Get the set of features supported by this solver.

static getJMVAFeatureSet()[source]

What the JMVA ANALYTICAL engine accepts, much less than JSIM.

Derived from the writer rather than guessed: write_jmva emits, per station, a <delaystation>, a <listation> or an <ldstation>, a per-chain <servicetime> and a per-chain <visit>, and at model level the closed populations, the open arrival rates and the reference station. NOTHING ELSE IN THE MODEL REACHES JMVA, so a construct whose whole effect is not carried by (station type, demand, visits, population) would be solved away silently.

Dropped from the JSIM set, and why:
  • Fork/Join and the fan-out names – no fork element exists, and a visit ratio cannot express the join synchronization.

  • Place/Transition and the Petri-net sections – no counterpart.

  • Region – JMVA has no finite capacity region.

  • Reneging/Balking – no impatience element; the abandonment would simply not happen.

  • SetupDelayOff, ServerParallelism, HeteroServers – each a server-side attribute the JMVA document has no slot for.

  • the non-BCMP disciplines – the writer emits NO discipline at all, so a priority, weighted, size-based or limited-sharing station would be solved as an ordinary load-independent one. Only the four BCMP station types survive the encoding, the same line SolverNC and SolverMVA draw.

  • the state-dependent routings (RROBIN, WRROBIN, JSQ, SQ) – the document carries mean visit counts, which is not what makes a join-the-shortest-queue model behave as it does.

The DISTRIBUTIONS are deliberately kept: JMVA consumes a mean service demand, so any renewal law with a finite mean is admissible, exactly as it is for SolverMVA and SolverNC. Cache is absent from this port’s JSIM set already and so does not appear here either.

static get_jmva_feature_set()

What the JMVA ANALYTICAL engine accepts, much less than JSIM.

Derived from the writer rather than guessed: write_jmva emits, per station, a <delaystation>, a <listation> or an <ldstation>, a per-chain <servicetime> and a per-chain <visit>, and at model level the closed populations, the open arrival rates and the reference station. NOTHING ELSE IN THE MODEL REACHES JMVA, so a construct whose whole effect is not carried by (station type, demand, visits, population) would be solved away silently.

Dropped from the JSIM set, and why:
  • Fork/Join and the fan-out names – no fork element exists, and a visit ratio cannot express the join synchronization.

  • Place/Transition and the Petri-net sections – no counterpart.

  • Region – JMVA has no finite capacity region.

  • Reneging/Balking – no impatience element; the abandonment would simply not happen.

  • SetupDelayOff, ServerParallelism, HeteroServers – each a server-side attribute the JMVA document has no slot for.

  • the non-BCMP disciplines – the writer emits NO discipline at all, so a priority, weighted, size-based or limited-sharing station would be solved as an ordinary load-independent one. Only the four BCMP station types survive the encoding, the same line SolverNC and SolverMVA draw.

  • the state-dependent routings (RROBIN, WRROBIN, JSQ, SQ) – the document carries mean visit counts, which is not what makes a join-the-shortest-queue model behave as it does.

The DISTRIBUTIONS are deliberately kept: JMVA consumes a mean service demand, so any renewal law with a finite mean is admissible, exactly as it is for SolverMVA and SolverNC. Cache is absent from this port’s JSIM set already and so does not appear here either.

getMethodFeatureSet(method)[source]

SolverJMT drives TWO ENGINES, and they accept different models.

‘default’, ‘jsim’ and ‘replication’ run the JSIM SIMULATOR, whose envelope is getFeatureSet. The ‘jmva.*’ names run the JMVA ANALYTICAL engine, which reads a document carrying only a station type, a per-chain demand, a per-chain visit count, the populations or arrival rates and a reference station – so declaring the JSIM envelope for jmva was a promise the writer could not keep.

Defining this is also what lets the base runAnalyzerChecks gate name the offending features (mirrors MATLAB SolverJMT.getMethodFeatureSet): without it the coarse supports(model) is used, which accepts every model. A non-Network model (e.g. a LayeredNetwork) keeps the coarse path and any structural checks that operate on such models.

get_method_feature_set(method)

SolverJMT drives TWO ENGINES, and they accept different models.

‘default’, ‘jsim’ and ‘replication’ run the JSIM SIMULATOR, whose envelope is getFeatureSet. The ‘jmva.*’ names run the JMVA ANALYTICAL engine, which reads a document carrying only a station type, a per-chain demand, a per-chain visit count, the populations or arrival rates and a reference station – so declaring the JSIM envelope for jmva was a promise the writer could not keep.

Defining this is also what lets the base runAnalyzerChecks gate name the offending features (mirrors MATLAB SolverJMT.getMethodFeatureSet): without it the coarse supports(model) is used, which accepts every model. A non-Network model (e.g. a LayeredNetwork) keeps the coarse path and any structural checks that operate on such models.

supportsModelMethod(method)[source]

Structural gate for what no registry name can state.

Three rules: the finite timespan the ‘replication’ arm integrates over, the single-server restriction of the closed-form JMVA algorithms (a server count is not a declared feature), and the one feature JMT admits in a RE-ENCODED form only. Limited load dependence has no representation of its own in either JMT document: the JSIM writer turns it into a server count and the JMVA writer into the matching <ldstation>, so alpha(n) = min(n,c) with an integer c is written exactly and any other scaling would be solved at a service rate JMT never saw. The first two come from jmt_method_refusal, which the analyzer asks as well.

supports_model_method(method)

Structural gate for what no registry name can state.

Three rules: the finite timespan the ‘replication’ arm integrates over, the single-server restriction of the closed-form JMVA algorithms (a server count is not a declared feature), and the one feature JMT admits in a RE-ENCODED form only. Limited load dependence has no representation of its own in either JMT document: the JSIM writer turns it into a server count and the JMVA writer into the matching <ldstation>, so alpha(n) = min(n,c) with an integer c is written exactly and any other scaling would be solved at a service rate JMT never saw. The first two come from jmt_method_refusal, which the analyzer asks as well.

static supports(model)[source]

Check if this solver supports the given model.

Mirrors MATLAB SolverJMT.supports. This previously returned True unconditionally, so it accepted models built on features the JSIM writer cannot represent (e.g. FCFSPR).

static defaultOptions()[source]

Get default solver options.

getFileName()[source]

Get the model file name, WITHOUT directory and WITHOUT extension.

Matches MATLAB getFileName.m, whose callers build the name as [fileName ‘.jsim’], and the JAR, which does fileName + “.jsim”. This previously returned ‘model.jsimg’, i.e. it included the extension, so it did not compose the way the other two codebases’ callers expect.

getFilePath()[source]

Get the directory holding the model file.

Returns the DIRECTORY, matching MATLAB getFilePath (out = self.filePath); the file name is getFileName() and the joined path is getJSIMTempPath().

static getJMTJarPath()[source]

Get path to JMT.jar.

getJMVATempPath()[source]

Get path to the temporary JMVA model file.

getJSIMTempPath()[source]

Get path to the temporary JSIM model file.

writeJMVA(outputFileName=None)[source]

Write model to JMVA format.

Parameters:

outputFileName (str) – Output file path. If None, writes to temp directory.

Returns:

Path to the written file.

Return type:

str

writeJSIM(outputFileName=None)[source]

Write model to JSIM XML format.

Parameters:

outputFileName (str) – Output file path. If None, writes to temp directory.

Returns:

Path to the written file.

Return type:

str

QN2JSIMG(outputFileName=None)[source]

Convert queueing network to JSIMG format. Wrapper for writeJSIM.

Parameters:

outputFileName (str) – Output file path.

Returns:

Path to the written file.

Return type:

str

getTranCdfRespT(R=None)[source]

Get transient CDF of response times.

The same logged pipeline as getCdfRespT WITHOUT the steady-state seed: the reference @SolverJMT/getTranCdfRespT.m starts the logged run from the model’s default initial state, so the collected samples cover the transient, where getCdfRespT preloads the rounded steady-state queue lengths to shorten the warmup.

getTranCdfPassT(R=None)[source]

Get transient CDF of passage times. Delegates to getTranCdfRespT, its own name in the reference’s sibling file.

getTranProbAggr(node=None)[source]

Get transient aggregated state probabilities from simulation.

Runs simulation and computes time-windowed probability from trajectory.

Parameters:

node – Node index or node object. If None, returns for all nodes.

Returns:

Dict with ‘t’ (time vector) and ‘prob’ (probability trajectory).

getTranAvg()[source]

Get transient average metrics from simulation.

Runs simulation with logging and extracts time series.

Returns:

Tuple of (QNt, UNt, TNt) time series dicts, or None if unavailable.

getProb(node=None, state=None)[source]

Get state probability from simulation trajectory.

Parameters:
  • node – Node index or node object.

  • state – Target state vector. If None, returns probability of current state.

Returns:

Float probability value, or dict of probabilities.

getProbSys()[source]

Get joint system state probability.

Returns:

Float probability of the current system state.

getProbMarg(node=None, jobclass=None)[source]

Get marginal state probability for a specific class at a node.

Parameters:
  • node – Node index or object.

  • jobclass – Class index.

Returns:

Dict mapping state values to probabilities.

getProbNormConstAggr()[source]

Log normalizing constant, from the JMVA engine only.

A simulation computes no normalizing constant, but the analytical JMVA algorithms report one in the result file’s <normconst logValue>, which MATLAB stores as result.Prob.logNormConstAggr. It is returned here for the jmva* methods and refused for the simulation ones rather than handing back the NaN placeholder.

Raises:

NotImplementedError – on the simulation methods.

sample(node, numEvents=1000)[source]

Sample the aggregated state trajectory at a node (JMT logs only carry per-class counts, so this is equivalent to sampleAggr()).

Parameters:
  • node – Node index or node object.

  • numEvents (int) – Number of events to sample.

Returns:

Dict with ‘t’ (time vector) and ‘state’ (per-class counts).

sampleAggr(node, numEvents=1000)[source]

Sample the aggregated (per-class count) state trajectory at a node.

Faithful port of MATLAB SolverJMT.sampleAggr: a temporary logged copy of the model is simulated and its arrival/departure logs are reconstructed into a piecewise-constant per-class queue-length trajectory.

Parameters:
  • node – Node index (0-based) or node object.

  • numEvents (int) – Desired number of sampled events at the node.

Returns:

Dict with keys ‘handle’, ‘t’ (event-boundary times), ‘state’ (len x nclasses per-class counts), ‘event’ (chronological event list) and ‘isaggregate’=True.

sampleSys(numEvents=1000)[source]

Sample system-wide state trajectory.

Parameters:

numEvents (int) – Number of events to sample.

Returns:

Dict with ‘t’ (time vector) and ‘states’ (list of per-node state matrices).

getAvgResidT()[source]

Get average residence times (M x K).

getAvgWaitT()[source]

Get average waiting times (M x K). W = R - S.

getAvg()[source]

Get all average metrics at once.

Returns:

Tuple of (Q, U, R, T, A, W)

getAvgSys()[source]

Get system-level average metrics.

Returns:

Tuple of (CN, XN) - system response times and throughputs

getAvgNode()[source]

Get average metrics per node.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

getCdfRespT(R=None)[source]

Get response time CDF via transient simulation with logging.

This method runs JMT twice: 1. First run: Get steady-state queue lengths to initialize state 2. Second run: Run with logging enabled to collect response time samples

Ported from MATLAB’s SolverJMT.getCdfRespT.

Parameters:

R – Optional response time handles (uses defaults if None)

Returns:

List of lists where RD[station][class] is a 2D array [cdf, time]

getPerctRespT(percentiles=None)[source]

Get response time percentiles.

Parameters:

percentiles – Array of percentile values (default: [90, 95, 99])

Returns:

Tuple of (PercRT, PercTable) where PercRT is list of dicts and PercTable is a pandas DataFrame

avg_qlen()

Get average queue lengths (M x K matrix).

prob_sys_aggr()

Get system state probability via simulation sampling.

Uses JMT simulation with logging to estimate the probability of the system being in the current aggregated state (as set via setState).

The probability is computed as the fraction of time the system spends in the target state during simulation.

Note: This method requires Logger nodes to be present in the model for full functionality. If log files are not available, it attempts to estimate probabilities from the simulation’s average metrics.

Returns:

Estimated probability of the current system state.

Returns 0.0 if the state was not observed during simulation.

Return type:

float

prob_aggr(station)

Get the aggregated state probability at a station.

Under lang=’cpp’ this is -s jmt -a prob: one logged JSIM run, dwell-weighted over the time the station holds its declared state. The native path here has no equivalent – JSIM reports means and not state occupancies to this wrapper – and returns an empty array, which is what it has always done.

avg_util()

Get average utilizations (M x K matrix).

avg_respt()

Get average response times (M x K matrix).

get_avg_respt()

Get average response times (M x K matrix).

avg_residt()

Get average residence times (M x K).

avg_waitt()

Get average waiting times (M x K). W = R - S.

avg_tput()

Get average throughputs (M x K matrix).

avg_arv_r()

Get average arrival rates (M x K matrix).

avg_fcr()

The finite-capacity-region rows, ((nregions*6) x nclasses) or empty.

WHY A SEPARATE ACCESSOR. getAvg returns the STATION metrics alone, and a region is not a station: its rows live past the last one, which is where getAvgNodeTable reads them from to fill the FCR pseudo-node. A host bridging through getAvg (MATLAB lang=’python’) therefore saw no region at all and dropped the FCR row from its node table (fcr_mm1waitq[M2P], “row FCR1 missing”).

The six blocks are stacked in the order Q, U, R, W, A, T, each (nregions x nclasses), so one marshalled matrix carries all of them. Util and ArvR are NaN: JMT reports neither for a region.

avg_chain_table()

Get average performance metrics aggregated by chain.

Returns:

Chain, QLen, Util, RespT, Tput

Return type:

DataFrame with columns

avg_sys_table()

Get system-level average performance metrics.

Returns:

Chain, SysRespT, SysTput

Return type:

DataFrame with columns

avg_sys_resp_t()

Get system response times (1 x K).

avg_sys_tput()

Get system throughputs (1 x K).

run_analyzer()

Run the JMT analyzer.

Calls JMT via command line and stores the results.

Returns:

self for method chaining

Return type:

SolverJMT

get_runtime()

Get solver runtime in seconds.

get_method()

Get the method used.

list_valid_methods()

List valid methods for this solver.

static is_available()

Check if JMT solver is available.

static get_feature_set()

Get the set of features supported by this solver.

static default_options()

Get default solver options.

cdf_resp_t(R=None)

Get response time CDF via transient simulation with logging.

This method runs JMT twice: 1. First run: Get steady-state queue lengths to initialize state 2. Second run: Run with logging enabled to collect response time samples

Ported from MATLAB’s SolverJMT.getCdfRespT.

Parameters:

R – Optional response time handles (uses defaults if None)

Returns:

List of lists where RD[station][class] is a 2D array [cdf, time]

cdf_respt(R=None)

Get response time CDF via transient simulation with logging.

This method runs JMT twice: 1. First run: Get steady-state queue lengths to initialize state 2. Second run: Run with logging enabled to collect response time samples

Ported from MATLAB’s SolverJMT.getCdfRespT.

Parameters:

R – Optional response time handles (uses defaults if None)

Returns:

List of lists where RD[station][class] is a 2D array [cdf, time]

get_cdf_resp_t(R=None)

Get response time CDF via transient simulation with logging.

This method runs JMT twice: 1. First run: Get steady-state queue lengths to initialize state 2. Second run: Run with logging enabled to collect response time samples

Ported from MATLAB’s SolverJMT.getCdfRespT.

Parameters:

R – Optional response time handles (uses defaults if None)

Returns:

List of lists where RD[station][class] is a 2D array [cdf, time]

get_tran_cdf_respt(R=None)

Get transient CDF of response times.

The same logged pipeline as getCdfRespT WITHOUT the steady-state seed: the reference @SolverJMT/getTranCdfRespT.m starts the logged run from the model’s default initial state, so the collected samples cover the transient, where getCdfRespT preloads the rounded steady-state queue lengths to shorten the warmup.

get_tran_cdf_resp_t(R=None)

Get transient CDF of response times.

The same logged pipeline as getCdfRespT WITHOUT the steady-state seed: the reference @SolverJMT/getTranCdfRespT.m starts the logged run from the model’s default initial state, so the collected samples cover the transient, where getCdfRespT preloads the rounded steady-state queue lengths to shorten the warmup.

get_tran_cdf_pass_t(R=None)

Get transient CDF of passage times. Delegates to getTranCdfRespT, its own name in the reference’s sibling file.

perct_resp_t(percentiles=None)

Get response time percentiles.

Parameters:

percentiles – Array of percentile values (default: [90, 95, 99])

Returns:

Tuple of (PercRT, PercTable) where PercRT is list of dicts and PercTable is a pandas DataFrame

perct_respt(percentiles=None)

Get response time percentiles.

Parameters:

percentiles – Array of percentile values (default: [90, 95, 99])

Returns:

Tuple of (PercRT, PercTable) where PercRT is list of dicts and PercTable is a pandas DataFrame

getAvgNodeTable()[source]

Per-node average performance metrics. Mirrors MATLAB and JAR output by including non-station nodes (Source, Sink, Router, ClassSwitch) alongside station rows. Per-node arrival rates are computed via sn_get_node_arvr_from_tput; passthrough nodes get Tput == ArvR; Sink Tput is 0; queue/delay rows reuse the station-level metrics.

avg_node_table()

Per-node average performance metrics. Mirrors MATLAB and JAR output by including non-station nodes (Source, Sink, Router, ClassSwitch) alongside station rows. Per-node arrival rates are computed via sn_get_node_arvr_from_tput; passthrough nodes get Tput == ArvR; Sink Tput is 0; queue/delay rows reuse the station-level metrics.

get_avg_node_table()

Per-node average performance metrics. Mirrors MATLAB and JAR output by including non-station nodes (Source, Sink, Router, ClassSwitch) alongside station rows. Per-node arrival rates are computed via sn_get_node_arvr_from_tput; passthrough nodes get Tput == ArvR; Sink Tput is 0; queue/delay rows reuse the station-level metrics.

get_file_name()

Get the model file name, WITHOUT directory and WITHOUT extension.

Matches MATLAB getFileName.m, whose callers build the name as [fileName ‘.jsim’], and the JAR, which does fileName + “.jsim”. This previously returned ‘model.jsimg’, i.e. it included the extension, so it did not compose the way the other two codebases’ callers expect.

get_file_path()

Get the directory holding the model file.

Returns the DIRECTORY, matching MATLAB getFilePath (out = self.filePath); the file name is getFileName() and the joined path is getJSIMTempPath().

static get_jmt_jar_path()

Get path to JMT.jar.

write_jmva(outputFileName=None)

Write model to JMVA format.

Parameters:

outputFileName (str) – Output file path. If None, writes to temp directory.

Returns:

Path to the written file.

Return type:

str

write_jsim(outputFileName=None)

Write model to JSIM XML format.

Parameters:

outputFileName (str) – Output file path. If None, writes to temp directory.

Returns:

Path to the written file.

Return type:

str

class SolverQNS(model_or_sn, options=None, **kwargs)[source]

Bases: NetworkSolver

Native Python QNS solver using external qnsolver tool.

This solver wraps the qnsolver command-line tool from the LQNS toolkit to analyze queueing networks using various multiserver approximation methods.

Supported methods: - default: Uses Conway approximation - conway: Conway’s approximation - rolia: Rolia’s method - zhou: Zhou’s approximation - suri: Suri’s approximation - reiser: Reiser’s method - schmidt: Schmidt’s method

Requirements:

The ‘qnsolver’ command must be available in the system PATH. Install from: http://www.sce.carleton.ca/rads/lqns/

Example

>>> solver = SolverQNS(sn, QNSOptions(method='conway'))
>>> result = solver.runAnalyzer()
>>> print(result.QN)  # Queue lengths

Initialize the QNS solver.

Parameters:
  • model_or_sn – Network model or NetworkStruct containing the queueing network

  • options (QNSOptions | None) – Optional QNSOptions configuration

  • **kwargs – Additional options (method, multiserver, samples, verbose, keep)

__init__(model_or_sn, options=None, **kwargs)[source]

Initialize the QNS solver.

Parameters:
  • model_or_sn – Network model or NetworkStruct containing the queueing network

  • options (QNSOptions | None) – Optional QNSOptions configuration

  • **kwargs – Additional options (method, multiserver, samples, verbose, keep)

static isAvailable()[source]

Check if qnsolver can be run: a native binary is on the PATH.

qnsolver ships with LQNS, whose licence forbids redistribution, so LINE never runs it from a container image; run-tests.sh –lqns-docker puts a shim on the PATH when a containerised build is what should be exercised.

Returns:

True if a native qnsolver binary is available, False otherwise

Return type:

bool

static listValidMethods()[source]

List valid methods for the QNS solver.

runAnalyzer()[source]

Run the QNS analysis.

Returns:

QNSResult containing performance metrics

Raises:

RuntimeError – If qnsolver is not available or fails

Return type:

QNSResult

getAvgTable()[source]

Get comprehensive average performance metrics table.

Returns:

Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput

Return type:

pandas.DataFrame with columns

getAvg()[source]

Get all average metrics at once.

Returns:

Tuple of (Q, U, R, T, A, W)

getAvgQLen()[source]

Get average queue lengths (M x K).

getAvgUtil()[source]

Get average utilizations (M x K).

getAvgRespT()[source]

Get average response times (M x K).

getAvgResidT()[source]

Get average residence times (M x K).

getAvgWaitT()[source]

Get average waiting times (M x K).

getAvgTput()[source]

Get average throughputs (M x K).

getAvgArvR()[source]

Get average arrival rates (M x K).

getAvgSysRespT()[source]

Get system response times (1 x C).

getAvgSysTput()[source]

Get system throughputs (1 x C).

getAvgSys()[source]

Get system-level average metrics.

Returns:

Tuple of (CN, XN) - system response times and throughputs

getAvgSysTable()[source]

Get system-level metrics as DataFrame (chain-level shared layout).

getAvgQLenChain()[source]

Get average queue lengths aggregated by chain.

getAvgUtilChain()[source]

Get average utilizations aggregated by chain.

getAvgRespTChain()[source]

Get average response times aggregated by chain.

getAvgResidTChain()[source]

Get average residence times aggregated by chain.

getAvgTputChain()[source]

Get average throughputs aggregated by chain.

getAvgArvRChain()[source]

Get average arrival rates aggregated by chain.

getAvgChain()[source]

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

getAvgChainTable()[source]

Get average metrics by chain as DataFrame.

getAvgNode()[source]

Get average metrics per node.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

getAvgNodeTable()[source]

Get average metrics by node as DataFrame.

getAvgNodeChain()[source]

Get average metrics by node and chain.

getAvgNodeChainTable()[source]

Get average metrics by node and chain as DataFrame.

getAvgNodeQLenChain()[source]

Get average queue lengths by node aggregated by chain.

getAvgNodeUtilChain()[source]

Get average utilizations by node aggregated by chain.

getAvgNodeRespTChain()[source]

Get average response times by node aggregated by chain.

getAvgNodeResidTChain()[source]

Get average residence times by node aggregated by chain.

getAvgNodeTputChain()[source]

Get average throughputs by node aggregated by chain.

getAvgNodeArvRChain()[source]

Get average arrival rates by node aggregated by chain.

static getFeatureSet()[source]

Get supported features.

Returns the canonical feature names (mirrors MATLAB SolverQNS.getFeatureSet and the JAR SolverQNS; all rows wrap the same external qnsolver tool).

static supports(model)[source]

Check if model is supported.

Mirrors MATLAB SolverQNS.supports. This previously checked only the station and class counts, so it accepted every model regardless of the features it used.

getMethodFeatureSet(method)[source]

The QNS feature envelope, per method (it does not vary by method).

Defining this is what lets NetworkSolver.supportsModelMethod NAME the offending features. With no method feature set the base falls back to the coarse supports(model), which returns an empty reason, so the gate could only answer False with nothing said: a caller asking why QNS refused a capped model got ‘’. Mirrors @SolverQNS/getMethodFeatureSet in MATLAB, which exists for exactly this reason.

A non-Network model (a LayeredNetwork, say) has no used-feature record, so it keeps the coarse path and any structural checks that operate on such models.

supportsModelMethod(method)[source]

Structural finite-capacity gate.

NOTHING under the QNS tree reads sn.cap or sn.classcap – the model is written out for qnsolver, whose MVA-family algorithms have no representation of a finite buffer – so a capped station was solved as an unbounded one and the table reported the unconstrained answer under this solver’s name. There is no registry feature name for plain capacity, hence the structural test; SolverMVA, SolverNC, SolverAG and SolverFLD gate the same way through the same helper.

Without it SolverAUTO.listValidMethods offered all eight ‘qns’ method names on the BAS-blocking model of cqn_bas_blocking. Mirrors @SolverQNS/supportsModelMethod in MATLAB and the JAR.

supports_model_method(method)

Structural finite-capacity gate.

NOTHING under the QNS tree reads sn.cap or sn.classcap – the model is written out for qnsolver, whose MVA-family algorithms have no representation of a finite buffer – so a capped station was solved as an unbounded one and the table reported the unconstrained answer under this solver’s name. There is no registry feature name for plain capacity, hence the structural test; SolverMVA, SolverNC, SolverAG and SolverFLD gate the same way through the same helper.

Without it SolverAUTO.listValidMethods offered all eight ‘qns’ method names on the BAS-blocking model of cqn_bas_blocking. Mirrors @SolverQNS/supportsModelMethod in MATLAB and the JAR.

static defaultOptions()[source]

Get default solver options.

run_analyzer()

Run the QNS analysis.

Returns:

QNSResult containing performance metrics

Raises:

RuntimeError – If qnsolver is not available or fails

Return type:

QNSResult

static is_available()

Check if qnsolver can be run: a native binary is on the PATH.

qnsolver ships with LQNS, whose licence forbids redistribution, so LINE never runs it from a container image; run-tests.sh –lqns-docker puts a shim on the PATH when a containerised build is what should be exercised.

Returns:

True if a native qnsolver binary is available, False otherwise

Return type:

bool

static list_valid_methods()

List valid methods for the QNS solver.

get_avg()

Get all average metrics at once.

Returns:

Tuple of (Q, U, R, T, A, W)

get_avg_qlen()

Get average queue lengths (M x K).

get_avg_util()

Get average utilizations (M x K).

get_avg_respt()

Get average response times (M x K).

get_avg_residt()

Get average residence times (M x K).

get_avg_waitt()

Get average waiting times (M x K).

get_avg_tput()

Get average throughputs (M x K).

get_avg_arvr()

Get average arrival rates (M x K).

get_avg_sys_respt()

Get system response times (1 x C).

get_avg_sys_tput()

Get system throughputs (1 x C).

get_avg_sys()

Get system-level average metrics.

Returns:

Tuple of (CN, XN) - system response times and throughputs

get_avg_sys_table()

Get system-level metrics as DataFrame (chain-level shared layout).

get_avg_chain()

Get all average metrics aggregated by chain.

Returns:

Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain

get_avg_chain_table()

Get average metrics by chain as DataFrame.

get_avg_node()

Get average metrics per node.

Returns:

Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics

get_avg_node_table()

Get average metrics by node as DataFrame.

class SolverLQNS(model, options=None, **kwargs)[source]

Bases: Solver

Native Python LQNS solver using external lqns/lqsim tools.

This solver wraps the LQNS (Layered Queueing Network Solver) command-line tools to analyze layered queueing networks. The CLI is called directly without going through SolverLQNS.

Supported methods: - default/lqns: Standard LQNS analytical solver - srvn: SRVN layering - exactmva: Exact MVA algorithm - srvn.exactmva: SRVN with exact MVA - sim/lqsim: Simulation-based solver

Requirements:

The ‘lqns’ and ‘lqsim’ commands must be available in the system PATH. Install from: http://www.sce.carleton.ca/rads/lqns/

LINE ships no LQNS binary and runs none from a container image: LQNS is distributed under an evaluation agreement that forbids redistribution.

Alternatively, point LINE at a host that already runs LQNS:

options.config.remote = True; options.config.remote_url = ‘http://localhost:8080

Note

Model serialization to LQNX format method. The native aspect is in the CLI execution and result parsing.

Example

>>> lqn = LayeredNetwork('model')
>>> # ... build model ...
>>> solver = SolverLQNS(lqn, LQNSOptions(method='lqns'))
>>> result = solver.runAnalyzer()

Initialize the LQNS solver.

Parameters:
  • model – LayeredNetwork model

  • options (LQNSOptions | None) – Optional LQNSOptions configuration

  • **kwargs – Additional parameters (keep, etc.) for compatibility

__init__(model, options=None, **kwargs)[source]

Initialize the LQNS solver.

Parameters:
  • model – LayeredNetwork model

  • options (LQNSOptions | None) – Optional LQNSOptions configuration

  • **kwargs – Additional parameters (keep, etc.) for compatibility

static isAvailable()[source]

Check if lqns can be run: a native binary is on the PATH.

LINE never runs LQNS from a container image, because its licence forbids redistribution. To exercise a containerised build, put a shim on the PATH with run-tests.sh –lqns-docker.

Returns:

True if a native lqns binary is available, False otherwise

Return type:

bool

static listValidMethods()[source]

List valid methods for the LQNS solver.

isStochasticMethod(method)[source]

The lqsim simulator is stochastic; the analytical lqns/srvn methods are deterministic.

is_stochastic_method(method)

The lqsim simulator is stochastic; the analytical lqns/srvn methods are deterministic.

runAnalyzer()[source]

Run the LQNS analysis.

Returns:

LQNSResult containing performance metrics

Raises:

RuntimeError – If lqns is not available or fails

Return type:

LQNSResult

getAvg()[source]

Get average performance metrics.

getAvgTable()[source]

Get average performance metrics table.

Returns:

pandas.DataFrame with layered network performance metrics

Return type:

DataFrame

avg_table()[source]

Alias for getAvgTable() for API consistency.

get_avg_table()

Alias for getAvgTable() for API consistency.

getRawAvgTables()[source]

Get raw average tables including call metrics.

Returns:

Tuple of (avg_table, call_avg_table) DataFrames

Return type:

Tuple[DataFrame, DataFrame]

get_raw_avg_tables()

Get raw average tables including call metrics.

Returns:

Tuple of (avg_table, call_avg_table) DataFrames

Return type:

Tuple[DataFrame, DataFrame]

static getFeatureSet()[source]

Get set of features supported per layer by the LQNS solver.

Returns the canonical feature names (mirrors MATLAB SolverLQNS.supports and the JAR SolverLQNS.getFeatureSet).

static supports(model)[source]

Check if model is supported.

Mirrors MATLAB SolverLQNS.supports. No supports() existed anywhere in the MRO, so calling it raised AttributeError and the solver had no gate.

static defaultOptions()[source]

Get default solver options.

Returns:

LQNSOptions with default configuration

Return type:

LQNSOptions

run_analyzer()

Run the LQNS analysis.

Returns:

LQNSResult containing performance metrics

Raises:

RuntimeError – If lqns is not available or fails

Return type:

LQNSResult

get_avg()

Get average performance metrics.

static is_available()

Check if lqns can be run: a native binary is on the PATH.

LINE never runs LQNS from a container image, because its licence forbids redistribution. To exercise a containerised build, put a shim on the PATH with run-tests.sh –lqns-docker.

Returns:

True if a native lqns binary is available, False otherwise

Return type:

bool

static list_valid_methods()

List valid methods for the LQNS solver.

static default_options()

Get default solver options.

Returns:

LQNSOptions with default configuration

Return type:

LQNSOptions

class SolverLN(model, solver_factory_or_options=None, options=None, **kwargs)[source]

Bases: EnsembleSolver

Native Python Layered Network (LN) solver.

This implementation matches MATLAB’s SolverLN at 100% parity: - Uses the same layer decomposition algorithm (buildLayersRecursive) - Creates Network objects for each layer with proper classes and routing - Uses MVA solvers for each layer - Implements the same fixed-point iteration with convergence testing

The algorithm:

1. Build layer submodels: one per processor (host layer) and per task
2. Initialize service demands and think times from LQN structure
3. Iterate until convergence:
   a. Solve each layer using MVA
   b. Update service times based on lower-layer response times
   c. Update think times based on caller waiting times
   d. Update routing probabilities based on throughputs
   e. Check convergence
4. Aggregate results from all layers

options.method='nlp' takes none of those steps. It states the whole model as ONE nonlinear program over the QD-AMVA laws and roots it, so no submodel is built and no fixed point is driven between them; see solver_ln_nlp_analyzer. It is served natively and refuses a delegated options.lang.

options.lang delegates the whole layered solve instead: 'java' to jline.jar over JSON, 'cpp' to the C++ line-cli over the .lqnx interchange (steady state only, and it refuses what that interchange cannot carry – see solvers/cpp_dispatch.py). Either way the native fixed point never runs and options.arith selects the C++ arithmetic backend.

Parameters:
  • model – LayeredNetwork model

  • solver_factory – Optional factory function to create layer solvers

  • options – Solver options

  • **kwargs – Additional options

listValidMethods()[source]

Valid methods for this solver, SolverLN.m verbatim.

Each name states the LAYERING and the ENCODING; ln_requested_method normalises the alias spellings (‘ph’, ‘cs’, ‘srvncs’, ‘flatcs’, ‘squashed’, ‘squashed.ph’) onto these, and they are left out here to keep the list unambiguous, exactly as the reference does.

list_valid_methods()

Valid methods for this solver, SolverLN.m verbatim.

Each name states the LAYERING and the ENCODING; ln_requested_method normalises the alias spellings (‘ph’, ‘cs’, ‘srvncs’, ‘flatcs’, ‘squashed’, ‘squashed.ph’) onto these, and they are left out here to keep the list unambiguous, exactly as the reference does.

supportsModelMethod(method)[source]

The encoding rules the layer builders enforce at solve time, stated here so a CALLER can see them before running.

‘srvn.ph’ and ‘flat.ph’ compose each entry into ONE phase-type law, and several constructs have nowhere to go in that law: a forwarding call whose target is not in the caller’s activity graph, a routed call group whose dispatch order the composition folds away, a cache task, an admission constraint, a queue-dependent rate on a station the composition replaces. ‘flat.ph’ additionally squashes every layer into one network, which per-layer state (a replica, a powered-down setup thread) cannot survive.

None of these is a feature name, so none can be a feature-set delta: they are properties of what the METHOD does to the model. Left only in the builders they were invisible to every gate above them, and listValidMethods returns the same eight names for every model, so a report offered every encoding on every layered model.

Phase 2 is deliberately NOT tested: that refusal reads self.hasPhase2, which is built during layering rather than being a property of the model, so a gate cannot ask it without doing the layering it precedes. Mirrors MATLAB ln_method_refusal.

supports_model_method(method)

The encoding rules the layer builders enforce at solve time, stated here so a CALLER can see them before running.

‘srvn.ph’ and ‘flat.ph’ compose each entry into ONE phase-type law, and several constructs have nowhere to go in that law: a forwarding call whose target is not in the caller’s activity graph, a routed call group whose dispatch order the composition folds away, a cache task, an admission constraint, a queue-dependent rate on a station the composition replaces. ‘flat.ph’ additionally squashes every layer into one network, which per-layer state (a replica, a powered-down setup thread) cannot survive.

None of these is a feature name, so none can be a feature-set delta: they are properties of what the METHOD does to the model. Left only in the builders they were invisible to every gate above them, and listValidMethods returns the same eight names for every model, so a report offered every encoding on every layered model.

Phase 2 is deliberately NOT tested: that refusal reads self.hasPhase2, which is built during layering rather than being a property of the model, so a gate cannot ask it without doing the layering it precedes. Mirrors MATLAB ln_method_refusal.

supports(model)[source]

Check if the layered model is supported.

Mirrors MATLAB SolverLN.supports: an LQN is solved layer by layer, so the gate is the conjunction of the per-layer solvers’ own gates against their own layer, not a feature set of SolverLN’s own. This cannot be a static method, since it needs self.solvers[e].

No supports() existed anywhere in the MRO, so calling it raised AttributeError and the solver had no gate at all.

probe_layer_solver_name()[source]

Name the layer solver this ensemble runs, WITHOUT building the layers.

A delegated solve (lang=’java’/’cpp’) never enters iterate(), so self.solvers is still empty when the dispatcher has to name the layer engine, and reading it off that list reports the NATIVE DEFAULT (MVA) however the caller built the solver. A lambda factory hides the class from _layer_solver_cls too, so LN(model, lambda m: NC(m, opts)) was delegated as an MVA-layered ensemble – a different fixed point, not a different spelling of the same one (lcq_threehosts: cache hit 0.5 under MVA layers against 0.48331 under NC ones).

THE FACTORY IS THE DECLARATION, so it is applied to a layer and the product named. CTMC and MAM are skipped because they are the automatic per-layer substitutions (a finite capacity region, a SetupTask’s setup times), not a choice the caller made; if every layer resolves to one of those the answer is None and the caller keeps its own default.

init()[source]

Initialize before starting iterations (matches MATLAB init).

pre(it)[source]

Operations before each iteration (matches MATLAB pre).

Seed control for stochastic layer solvers.

analyze(it, e)[source]

Analyze a layer (matches MATLAB analyze).

Returns:

Tuple of (result dict, runtime)

Return type:

Tuple[Dict, float]

post(it)[source]

Operations after each iteration (matches MATLAB post).

update_metrics(it)[source]

Update metrics (matches MATLAB updateMetrics).

update_think_times(it)[source]

Update think times (matches MATLAB updateThinkTimes).

update_populations(it)[source]

Apply the interlock correction to call residence times.

The path tables built by _init_interlock are combined with the current iterate to obtain, for each (client, server) pair, the interlocked flow of Eq. (4.3) of Franks (1999), and from it the interlock probability, the share of that flow that the layer decomposition would otherwise count twice. Eq. (4.7) removes one source in n_s from the queue length inside MVA; the equivalent correction is applied here to the residence times returned by the layer:

R_adj = S + (1 - prIL) * W,   W = R - S,

which leaves service and utilization untouched and removes only the interlocked share of the waiting time.

Called after update_metrics, which produces the raw callresidt from the layer solutions, and before update_think_times.

update_layers(it)[source]

Update layer parameters (matches MATLAB updateLayers).

update_routing_probabilities(it)[source]

Update routing probabilities (matches MATLAB updateRoutingProbabilities).

converged(it)[source]

Check convergence (matches MATLAB converged).

converged_stoch(it)[source]

Convergence controller for stochastic layer solvers (Robbins-Monro mode).

When one or more layer solvers return noisy estimates (simulation, e.g. JMT/SSA/LDES, or Monte Carlo integration, e.g. NC with mci/imci/ls), the deterministic Picard iteration in converged() cannot terminate: the successive-difference error is bounded below by the standard error of the layer estimates, and the layer-reset confirmation step merely resamples the noise. This routine implements a stochastic approximation iteration instead:

  1. Burn-in: for the first stochiter_burnin iterations the plain Picard iteration runs with the relaxation factor configured at init.

  2. Robbins-Monro step: afterwards the relaxation factor applied by update_metrics to the fed-forward iterate (servt, residt, tput, callservt) decays as omega_k = a0/k**alpha with alpha in (0.5,1]. Under the contraction assumption already made by the deterministic iteration, and zero-mean noise with bounded variance, the iterate converges almost surely to the true fixed point (Robbins and Monro, 1951). Layer seeds are rotated per iteration in pre() so successive evaluations observe independent noise.

  3. Polyak-Ruppert averaging: running averages of the layer results and of the reported iterates are maintained and installed as the final solution in finish(), giving the optimal O(1/sqrt(k)) rate and robustness to the choice of a0 (Polyak and Juditsky, 1992).

  4. Stopping: iteration stops when the drift of the averaged results stays below iter_tol for stochiter_conseq consecutive iterations. The drift of a running average decays like 1/k even under persistent noise, so the test terminates, and it self-calibrates: larger noise keeps the drift above tolerance longer, forcing more averaging.

finish()[source]

Operations after iterations complete (matches MATLAB finish).

iterate()[source]

Run iteration (matches MATLAB EnsembleSolver iterate).

get_ensemble_avg()[source]

Get ensemble average (matches MATLAB getEnsembleAvg).

get_avg()[source]

Get average metrics (alias for get_ensemble_avg).

getCdfRespT()[source]

Response time distribution of every entry of the layered network.

Mirrors MATLAB @SolverLN/getCdfRespT.m. The distribution is formed by the moment3 pass alone – the mean-based update builds no law at all – so a solver constructed with any other method re-runs the ensemble under moment3 here and restores the caller’s method afterwards. The routing layers already built serve moment3 unchanged, so only the update pass changes.

Returns:

A list of nentries items, one per entry in the entry-local index space (lqn.eshift + i). Each is an (n, 2) array whose columns are [F(t), t], the column order every CDF getter in LINE uses, or None for an entry the pass fitted no law to.

Raises:

ValueError – if the layers were built for a phase-type encoding, which carries no activity-graph routing to re-run over.

Return type:

List[ndarray | None]

get_cdf_resp_t()

Response time distribution of every entry of the layered network.

Mirrors MATLAB @SolverLN/getCdfRespT.m. The distribution is formed by the moment3 pass alone – the mean-based update builds no law at all – so a solver constructed with any other method re-runs the ensemble under moment3 here and restores the caller’s method afterwards. The routing layers already built serve moment3 unchanged, so only the update pass changes.

Returns:

A list of nentries items, one per entry in the entry-local index space (lqn.eshift + i). Each is an (n, 2) array whose columns are [F(t), t], the column order every CDF getter in LINE uses, or None for an entry the pass fitted no law to.

Raises:

ValueError – if the layers were built for a phase-type encoding, which carries no activity-graph routing to re-run over.

Return type:

List[ndarray | None]

getTranAvg(*args)[source]

Transient average station metrics of the layered network.

options.config['ln_transient'] selects the inter-layer coupling of the transient:

  • 'decoupled': freeze inter-layer demands at the converged fixed point (get_ensemble_avg) and run each layer’s transient in isolation.

  • 'coupled' (default): reconcile the per-layer transients by waveform relaxation, so layer populations and inter-layer demands co-evolve in model time (getTranAvgCoupled).

Both modes return the SAME block-diagonal layout; iteration 0 of the coupled relaxation is exactly the decoupled result. Mirrors MATLAB SolverLN.getTranAvg.

getTranAvgDecoupled(*args)[source]

Decoupled (frozen-demand) transient average station metrics.

Mirrors MATLAB SolverLN.getTranAvgDecoupled: runs the ensemble fixed-point solve, then delegates the transient analysis to each layer solver and assembles the per-layer station x class traces block-diagonally (layer e in a disjoint row/column block). Off-block cells are left None.

Transient traces are only produced by transient-capable layer solvers (Fluid, CTMC, SSA); with steady-state-only layers (MVA, NC) the delegated getTranAvg raises, matching the MATLAB behaviour.

Returns:

each a block-diagonal nested list [rows][cols] of TranResult (or None off-block), where layer e occupies a disjoint block of rows (its stations) and columns (its classes).

Return type:

(QNlqn_t, UNlqn_t, TNlqn_t)

getBlockAvg()[source]

Block-diagonal aggregate STEADY tables, in the layout getTranAvg returns.

Three (M x K) arrays over the SAME aggregate station x class space as getTranAvg and LayeredNetwork.get_tran_handles: layer e occupies a disjoint block of rows (its stations) and columns (its classes), and every off-block cell is 0, because no station of one layer carries a class of another.

THIS IS NOT getAvg, AND THE DIFFERENCE IS AN INDEX SPACE, not a layout. SolverLN.getAvg is getEnsembleAvg: ONE ROW over the LQN’s own nodes – hosts, tasks, entries, activities – which carries no station or class meaning whatsoever. Copying its leading block into an (M, K) aggregate therefore reinterprets LQN node k as CLASS k, silently, and the value lands on a cell no layer owns. SolverENV’s exit-metric seed did exactly that for a layered stage until 2026-09-14: the stray cell sat off-block where the transient never overwrites anything, survived the probEnv blend, and inflated the aggregate queue length and throughput past the closed population the stages conserve.

Reads the layer solvers’ own cached averages and does NOT re-run the layered fixed point: the coupling that wants this is mid-iteration and is still reading the results that solve produced.

Returns:

each an (M x K) numpy array, off-block cells 0.

Return type:

(QN, UN, TN)

get_tran_avg(*args)

Transient average station metrics of the layered network.

options.config['ln_transient'] selects the inter-layer coupling of the transient:

  • 'decoupled': freeze inter-layer demands at the converged fixed point (get_ensemble_avg) and run each layer’s transient in isolation.

  • 'coupled' (default): reconcile the per-layer transients by waveform relaxation, so layer populations and inter-layer demands co-evolve in model time (getTranAvgCoupled).

Both modes return the SAME block-diagonal layout; iteration 0 of the coupled relaxation is exactly the decoupled result. Mirrors MATLAB SolverLN.getTranAvg.

get_tran_avg_decoupled(*args)

Decoupled (frozen-demand) transient average station metrics.

Mirrors MATLAB SolverLN.getTranAvgDecoupled: runs the ensemble fixed-point solve, then delegates the transient analysis to each layer solver and assembles the per-layer station x class traces block-diagonally (layer e in a disjoint row/column block). Off-block cells are left None.

Transient traces are only produced by transient-capable layer solvers (Fluid, CTMC, SSA); with steady-state-only layers (MVA, NC) the delegated getTranAvg raises, matching the MATLAB behaviour.

Returns:

each a block-diagonal nested list [rows][cols] of TranResult (or None off-block), where layer e occupies a disjoint block of rows (its stations) and columns (its classes).

Return type:

(QNlqn_t, UNlqn_t, TNlqn_t)

get_block_avg()

Block-diagonal aggregate STEADY tables, in the layout getTranAvg returns.

Three (M x K) arrays over the SAME aggregate station x class space as getTranAvg and LayeredNetwork.get_tran_handles: layer e occupies a disjoint block of rows (its stations) and columns (its classes), and every off-block cell is 0, because no station of one layer carries a class of another.

THIS IS NOT getAvg, AND THE DIFFERENCE IS AN INDEX SPACE, not a layout. SolverLN.getAvg is getEnsembleAvg: ONE ROW over the LQN’s own nodes – hosts, tasks, entries, activities – which carries no station or class meaning whatsoever. Copying its leading block into an (M, K) aggregate therefore reinterprets LQN node k as CLASS k, silently, and the value lands on a cell no layer owns. SolverENV’s exit-metric seed did exactly that for a layered stage until 2026-09-14: the stray cell sat off-block where the transient never overwrites anything, survived the probEnv blend, and inflated the aggregate queue length and throughput past the closed population the stages conserve.

Reads the layer solvers’ own cached averages and does NOT re-run the layered fixed point: the coupling that wants this is mid-iteration and is still reading the results that solve produced.

Returns:

each an (M x K) numpy array, off-block cells 0.

Return type:

(QN, UN, TN)

getTranAvgCoupled(*args)[source]

Coupled layered transient by waveform relaxation over the LQN ensemble.

Port of MATLAB @SolverLN/getTranAvgCoupled.m. Unlike getTranAvgDecoupled, which freezes inter-layer demands at the converged fixed point, this reconciles the per-layer transients iteratively: each layer’s transient is driven by TIME-VARYING inter-layer demand trajectories taken from the other layers’ latest transients, and the loop repeats until the trajectories stop changing (sup-norm gap over time). The time-varying demands are injected into each layer solver through the per-(station,class) rate schedule (options.config['rate_sched']), honoured by the fluid rate multiplier and by the CTMC time-varying transient.

Iteration 0 uses the frozen equilibrium demands, so it reproduces getTranAvgDecoupled exactly; at convergence every layer relaxes to its fixed point, so the endpoint equals get_ensemble_avg. The return layout is the same block-diagonal (station x class per layer) as getTranAvgDecoupled.

Coupled channels: task think times (client delay) and synchronous-call service demands (caller client station). Both are the dominant inter-layer couplings; intra-layer host service stays at its equilibrium value.

get_tran_avg_coupled(*args)

Coupled layered transient by waveform relaxation over the LQN ensemble.

Port of MATLAB @SolverLN/getTranAvgCoupled.m. Unlike getTranAvgDecoupled, which freezes inter-layer demands at the converged fixed point, this reconciles the per-layer transients iteratively: each layer’s transient is driven by TIME-VARYING inter-layer demand trajectories taken from the other layers’ latest transients, and the loop repeats until the trajectories stop changing (sup-norm gap over time). The time-varying demands are injected into each layer solver through the per-(station,class) rate schedule (options.config['rate_sched']), honoured by the fluid rate multiplier and by the CTMC time-varying transient.

Iteration 0 uses the frozen equilibrium demands, so it reproduces getTranAvgDecoupled exactly; at convergence every layer relaxes to its fixed point, so the endpoint equals get_ensemble_avg. The return layout is the same block-diagonal (station x class per layer) as getTranAvgDecoupled.

Coupled channels: task think times (client delay) and synchronous-call service demands (caller client station). Both are the dominant inter-layer couplings; intra-layer host service stays at its equilibrium value.

get_avg_table()[source]

Get average metrics as a table (matches MATLAB getAvgTable).

reset()[source]

Reset solver state.

set_state(state)[source]

Import a previously exported state (see get_state) for continuation.

update_solver(solver_factory)[source]

Replace all per-layer solvers with ones built by solver_factory, preserving the current solution state for refinement.

updateSolver(solver_factory)[source]
static defaultOptions()[source]

Get default LN solver options.

static default_options()[source]

Get default options (Python convention).

getSensitivityTable(method='auto', step=None, scheme='forward')[source]

Layer-wise performance sensitivities of a layered network.

Solves the layered model and then delegates to each layer solver, returning the concatenation of the layer tables with a leading Layer column. Every row is a (Layer, Station, JobClass) triple carrying the derivative of that row’s mean measures with respect to that station-class service RATE: dTput_dRate, dRespT_dRate, dQLen_dRate, dUtil_dRate.

The options are passed through to the layer solvers unchanged, with the same meaning as in NetworkSolver.getSensitivityTable: each layer independently takes the analytic branch where its own solver supports it and the model is in scope, and finite differences otherwise. In practice a layer submodel is chain-based (the callers switch class), which puts it out of scope of the analytic branch, so the layers normally finite-difference their own solver.

IMPORTANT, on what these derivatives mean. Each entry is a derivative WITHIN ITS LAYER, taken with the layer parameters that the fixed point produced held fixed. It is a partial derivative of the layer submodel, not the total derivative of the layered model: perturbing a host demand in one layer moves the think times, populations and service rates of the other layers through the fixed-point map, and that indirect term is not included here. The layer table is the right object for attributing a bottleneck inside a layer, and the wrong one for predicting the effect of a parameter change on the solved layered model.

Returns the table; the per-layer second outputs are on DataFrame.attrs['sens'] and the per-layer branch labels on DataFrame.attrs['layer_methods'], with attrs['method'] the summary (‘exact’, ‘fd’, or ‘mixed’).

get_sensitivity_table(method='auto', step=None, scheme='forward')

Layer-wise performance sensitivities of a layered network.

Solves the layered model and then delegates to each layer solver, returning the concatenation of the layer tables with a leading Layer column. Every row is a (Layer, Station, JobClass) triple carrying the derivative of that row’s mean measures with respect to that station-class service RATE: dTput_dRate, dRespT_dRate, dQLen_dRate, dUtil_dRate.

The options are passed through to the layer solvers unchanged, with the same meaning as in NetworkSolver.getSensitivityTable: each layer independently takes the analytic branch where its own solver supports it and the model is in scope, and finite differences otherwise. In practice a layer submodel is chain-based (the callers switch class), which puts it out of scope of the analytic branch, so the layers normally finite-difference their own solver.

IMPORTANT, on what these derivatives mean. Each entry is a derivative WITHIN ITS LAYER, taken with the layer parameters that the fixed point produced held fixed. It is a partial derivative of the layer submodel, not the total derivative of the layered model: perturbing a host demand in one layer moves the think times, populations and service rates of the other layers through the fixed-point map, and that indirect term is not included here. The layer table is the right object for attributing a bottleneck inside a layer, and the wrong one for predicting the effect of a parameter change on the solved layered model.

Returns the table; the per-layer second outputs are on DataFrame.attrs['sens'] and the per-layer branch labels on DataFrame.attrs['layer_methods'], with attrs['method'] the summary (‘exact’, ‘fd’, or ‘mixed’).

avg_table()

Get average metrics as a table (matches MATLAB getAvgTable).

getAvgTable()

Get average metrics as a table (matches MATLAB getAvgTable).

avgTable()

Get average metrics as a table (matches MATLAB getAvgTable).

avgT()

Get average metrics as a table (matches MATLAB getAvgTable).

aT()

Get average metrics as a table (matches MATLAB getAvgTable).

Specialized Solvers

SolverAuto

alias of SolverAUTO

LINE

alias of SolverAUTO

Result Classes

class SampleResult(handle='', t=None, state=None, event=None, isaggregate=False, nodeIndex=None, numEvents=0)[source]

Bases: object

Container for sample-based simulation results.

handle: str = ''
t: ndarray = None
state: ndarray = None
event: List[EventInfo] = None
isaggregate: bool = False
numEvents: int = 0
class EventInfo(node=0, jobclass=0, t=0.0, event=None)[source]

Bases: object

Information about a single event in a simulation trace.

node: int = 0
jobclass: int = 0
t: float = 0.0
event: str = None

Solver Configuration

class SolverOptions(solver_type=None)

Bases: object

Options for solvers.

Distributions (line_solver.distributions)

The distributions module provides probability distributions for service and arrival processes.

Base Distribution Classes

class Distribution[source]

Bases: ABC

Base class for all probability distributions.

This class provides the common interface for all probability distributions in LINE, including service time distributions and inter-arrival time distributions.

Initialize a new distribution.

__init__()[source]

Initialize a new distribution.

property name: str

Get the distribution name.

get_feature_name()[source]

The SolverFeatureSet entry this distribution is marked under.

Separate from name because that one also selects the ProcessType and the JSON wire type: a subclass whose registry name is more specific than its process type (a Trace, or a two-phase Coxian) says so here without moving to a different process type. Defaults to name, so a distribution needing no distinction is unaffected.

getFeatureName()

The SolverFeatureSet entry this distribution is marked under.

Separate from name because that one also selects the ProcessType and the JSON wire type: a subclass whose registry name is more specific than its process type (a Trace, or a two-phase Coxian) says so here without moving to a different process type. Defaults to name, so a distribution needing no distinction is unaffected.

abstract getMean()[source]

Get the mean (expected value) of the distribution.

abstract getVar()[source]

Get the variance of the distribution.

get_mean()[source]

Get the mean (expected value) of the distribution.

get_var()[source]

Get the variance of the distribution.

eval_pmf(x)[source]

Evaluate the probability mass function at point x (snake_case alias for evalPMF; dispatches to the concrete distribution’s override).

getSCV()[source]

Get the squared coefficient of variation (SCV).

SCV = Var[X] / E[X]^2

getRate()[source]

Get the rate parameter (1/mean).

For immediate (zero-mean) distributions, returns GlobalConstants.Immediate (a large but finite value) to avoid numerical issues with infinite rates. This matches MATLAB’s behavior.

getSkew()[source]

Get the skewness of the distribution.

getSupport()[source]

Get the support range [min, max] of the distribution.

isContinuous()[source]

Check if this distribution is continuous.

isDiscrete()[source]

Check if this distribution is discrete.

isDisabled()[source]

Check if this distribution is equivalent to a Disabled distribution.

Mirrors MATLAB Distribution.isDisabled (isnan(getMean(self))): a distribution whose mean is not a number carries no usable process representation, so the struct records it as disabled rather than mislabelling it. Returning a hardcoded False, as this did, made refreshStruct treat such a distribution as an ordinary one.

isImmediate()[source]

Check if this distribution represents immediate service.

evalCDF(x)[source]

Evaluate the cumulative distribution function at point x.

evalPDF(x)[source]

Evaluate the probability density function at point x.

sample(n=1, rng=None)[source]

Generate random samples from this distribution.

Parameters:
  • n (int) – Number of samples to generate.

  • rng (Generator | None) – Optional random number generator.

Returns:

Array of n random samples.

Return type:

ndarray

get_scv()

Get the squared coefficient of variation (SCV).

SCV = Var[X] / E[X]^2

get_rate()

Get the rate parameter (1/mean).

For immediate (zero-mean) distributions, returns GlobalConstants.Immediate (a large but finite value) to avoid numerical issues with infinite rates. This matches MATLAB’s behavior.

get_skew()

Get the skewness of the distribution.

get_support()

Get the support range [min, max] of the distribution.

class ContinuousDistribution[source]

Bases: Distribution

Base class for continuous probability distributions.

Initialize a new distribution.

isContinuous()[source]

Check if this distribution is continuous.

isDiscrete()[source]

Check if this distribution is discrete.

evalLST(s)[source]

Laplace-Stieltjes transform E[e^{-sX}] evaluated at s.

Generic numerical fallback: rectangle-rule integration of evalPDF over [0, 20*mean] with 1000 points. Subclasses with a closed form or a MATLAB-matching numerical scheme (Pareto/Lognormal/Weibull) override this. Used by the G/M/1 MVA solver’s sigma-root for non-PH arrivals.

evalLaplaceTransform(s)

Laplace-Stieltjes transform E[e^{-sX}] evaluated at s.

Generic numerical fallback: rectangle-rule integration of evalPDF over [0, 20*mean] with 1000 points. Subclasses with a closed form or a MATLAB-matching numerical scheme (Pareto/Lognormal/Weibull) override this. Used by the G/M/1 MVA solver’s sigma-root for non-PH arrivals.

class DiscreteDistribution[source]

Bases: Distribution

Base class for discrete probability distributions.

Initialize a new distribution.

isContinuous()[source]

Check if this distribution is continuous.

isDiscrete()[source]

Check if this distribution is discrete.

evalPMF(x)[source]

Evaluate the probability mass function at point x.

class Markovian[source]

Bases: Distribution

Base class for Markovian (phase-type) distributions.

Markovian distributions can be represented in terms of initial probability vectors and transition rate matrices.

Initialize a new distribution.

getD0()[source]

Get the D0 matrix (for MAP representations).

getD1()[source]

Get the D1 matrix (for MAP representations).

getMu()[source]

Get the service rates in each phase.

getPhi()[source]

Get the completion probabilities from each phase.

getInitProb()[source]

Get the initial probability vector.

getNumberOfPhases()[source]

Get the number of phases.

getRepresentation()[source]

Return the (D0, D1) matrix representation used in the theory of Markovian arrival processes, as a list [D0, D1] (element k corresponds to matrix D_k).

get_representation()[source]

snake_case alias for getRepresentation().

evalLST(s)[source]

Laplace-Stieltjes transform, alpha (sI - D0)^-1 (-D0 e), as in MATLAB.

Distribution.evalLST is a rectangle rule over evalPDF: it carries percent-level error on a phase-type law (Exp(1) at s = 0.5 read 0.65672 against 2/3) and returns ZERO for a COMPLEX argument, since it evaluates math.exp. Every law reaching here is Markovian, so the closed form applies, and being analytic it also serves the complex arguments that transform inversion and root location need.

getPH()[source]

Return the phase-type representation as a dict {0: D0, 1: D1}, matching the JAR Map<Integer, Matrix> shape.

get_ph()

Return the phase-type representation as a dict {0: D0, 1: D1}, matching the JAR Map<Integer, Matrix> shape.

get_d0()

Get the D0 matrix (for MAP representations).

get_d1()

Get the D1 matrix (for MAP representations).

get_mu()

Get the service rates in each phase.

get_phi()

Get the completion probabilities from each phase.

get_init_prob()

Get the initial probability vector.

get_number_of_phases()

Get the number of phases.

Continuous Distributions

class Exp(rate)[source]

Bases: ContinuousDistribution, Markovian

Exponential distribution.

The exponential distribution is the simplest continuous distribution for modeling service times in queueing systems. It has the memoryless property and SCV = 1.

Parameters:

rate (float) – The rate parameter (lambda = 1/mean).

Initialize a new distribution.

classmethod fit(mean, scv=1.0, skew=None)[source]

Fit an exponential to the given moments (MATLAB Exp.fit).

The exponential has SCV = 1 and skewness 2, so only the mean is used; the other moments are accepted for signature compatibility.

classmethod fit_mean_and_scv(mean, scv=1.0)[source]

Fit an exponential to a mean and SCV (MATLAB Exp.fitMeanAndSCV).

An exponential cannot represent SCV != 1; MATLAB warns and uses SCV = 1, which is what happens here.

classmethod fitMeanAndSCV(mean, scv=1.0)[source]

camelCase alias of fit_mean_and_scv (MATLAB/JAR spelling).

classmethod fit_mean(mean)[source]

Create an exponential distribution with the given mean.

THE RATE IS CLAMPED to [GlobalConstants.Zero, GlobalConstants.Immediate], which is what MATLAB Exp.fitMean and the JAR twin both do (min(Immediate, max(Zero, 1/MEAN))) and what fitRate below already did here. Without it a mean BELOW FineTol (1e-8) built a different model in each codebase from the same script: Exp.fit_mean(5e-10) gave a rate of 2e9 where MATLAB gave 1e8, so lqn_sockshop, whose bookkeeping activities are written Exp.fitMean(0.0000000005), round-tripped Python -> MATLAB into a model with 20x smaller demands at those activities. It disagreed only where the answer is near zero, which is exactly where a RELATIVE comparison is most severe: the JSON parity row reported maxrel=1 on QLen.

Parameters:

mean (float) – Target mean.

Returns:

Exp distribution with the clamped rate.

Return type:

Exp

classmethod fitMean(mean)

Create an exponential distribution with the given mean.

THE RATE IS CLAMPED to [GlobalConstants.Zero, GlobalConstants.Immediate], which is what MATLAB Exp.fitMean and the JAR twin both do (min(Immediate, max(Zero, 1/MEAN))) and what fitRate below already did here. Without it a mean BELOW FineTol (1e-8) built a different model in each codebase from the same script: Exp.fit_mean(5e-10) gave a rate of 2e9 where MATLAB gave 1e8, so lqn_sockshop, whose bookkeeping activities are written Exp.fitMean(0.0000000005), round-tripped Python -> MATLAB into a model with 20x smaller demands at those activities. It disagreed only where the answer is near zero, which is exactly where a RELATIVE comparison is most severe: the JSON parity row reported maxrel=1 on QLen.

Parameters:

mean (float) – Target mean.

Returns:

Exp distribution with the clamped rate.

Return type:

Exp

property rate: float

Get the rate parameter.

getMean()[source]

Get the mean (1/rate).

getVar()[source]

Get the variance (1/rate^2).

getSCV()[source]

Get the squared coefficient of variation (always 1 for exponential).

getSkew()[source]

Get the skewness (always 2 for exponential).

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

getNumberOfPhases()[source]

Get the number of phases (1 for exponential).

getD0()[source]

Get the D0 matrix for MAP representation.

getD1()[source]

Get the D1 matrix for MAP representation.

getMu()[source]

Get the service rates in each phase.

getPhi()[source]

Get the completion probabilities from each phase.

getInitProb()[source]

Get the initial probability vector.

classmethod fitRate(rate)[source]

Create an exponential distribution with the given rate.

THE RATE IS CLAMPED TO [GlobalConstants.Zero, GlobalConstants.Immediate], exactly as MATLAB Exp.fitRate, the JAR twin and the C++ exp_rate do. A fitter is fed a COMPUTED rate – an iterate of SolverLN, a refreshed arrival rate – and a rate of zero is a starved element rather than a malformed model: raising here aborted the whole layered solve of lqn_ofbiz on an activity whose throughput was still zero. A rate the caller writes itself still goes through the constructor, which refuses a non-positive one.

Parameters:

rate (float) – The rate parameter (lambda).

Returns:

Exp distribution with the clamped rate.

Return type:

Exp

classmethod fit_rate(rate)

Create an exponential distribution with the given rate.

THE RATE IS CLAMPED TO [GlobalConstants.Zero, GlobalConstants.Immediate], exactly as MATLAB Exp.fitRate, the JAR twin and the C++ exp_rate do. A fitter is fed a COMPUTED rate – an iterate of SolverLN, a refreshed arrival rate – and a rate of zero is a starved element rather than a malformed model: raising here aborted the whole layered solve of lqn_ofbiz on an activity whose throughput was still zero. A rate the caller writes itself still goes through the constructor, which refuses a non-positive one.

Parameters:

rate (float) – The rate parameter (lambda).

Returns:

Exp distribution with the clamped rate.

Return type:

Exp

get_rate()

Get the rate parameter (1/mean).

For immediate (zero-mean) distributions, returns GlobalConstants.Immediate (a large but finite value) to avoid numerical issues with infinite rates. This matches MATLAB’s behavior.

class Det(value)[source]

Bases: ContinuousDistribution

Deterministic (constant) distribution.

All service times are exactly equal to the specified value. Has SCV = 0 (no variability).

Parameters:

value (float) – The constant service time value.

Initialize a new distribution.

property value: float

Get the constant value.

getMean()[source]

Get the mean (equals the constant value).

getVar()[source]

Get the variance (always 0).

getSCV()[source]

Get the SCV (always 0).

getSkew()[source]

Get the skewness (undefined, return 0).

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x (delta function, return inf at value).

evalLST(s)[source]

LST of a deterministic time: exp(-s*t). Matches MATLAB Det.evalLST. numpy rather than math, so a COMPLEX argument is admissible: transform inversion and root location both need one.

sample(n=1, rng=None)[source]

Generate random samples (all equal to value).

isImmediate()[source]

Check if this is an immediate (zero) service.

classmethod fit_mean(mean)[source]

Create a deterministic distribution with the given mean.

Since Det has zero variance, the mean equals the constant value.

Parameters:

mean (float) – The mean (and constant value) of the distribution.

Returns:

Det distribution with the specified mean.

Return type:

Det

classmethod fitMean(mean)

Create a deterministic distribution with the given mean.

Since Det has zero variance, the mean equals the constant value.

Parameters:

mean (float) – The mean (and constant value) of the distribution.

Returns:

Det distribution with the specified mean.

Return type:

Det

class Erlang(phase_rate, nphases)[source]

Bases: ContinuousDistribution, Markovian

Erlang distribution (sum of k exponentials).

The Erlang distribution is the distribution of the sum of k independent exponential random variables with the same rate. It has SCV = 1/k.

Parameters:
  • phase_rate (float) – Rate parameter for each exponential phase (alpha).

  • nphases (int) – Number of sequential exponential phases (r).

Mean = nphases / phase_rate = r / alpha

Initialize a new distribution.

classmethod fit(mean, scv, skew=None)[source]

Fit an Erlang to the given moments (MATLAB Erlang.fit).

The Erlang has one shape degree of freedom, so the skewness cannot be set independently and is ignored, as in MATLAB.

classmethod fit_mean_and_scv(mean, scv)[source]

Create an Erlang distribution from mean and SCV.

For Erlang, SCV = 1/k where k is the number of phases, so the order is k = ceil(1/SCV): the achievable SCVs are 1, 1/2, 1/3, … and the fit takes the first one AT OR BELOW the request. Rounding instead would return a different law – at SCV=0.4, ceil gives 3 phases and round gives 2 – and every solver downstream would answer a different model with no error raised. MATLAB Erlang.fitMeanAndSCV, the JAR and the C++ port all use ceil.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation, which must be <= 1.

Returns:

Erlang distribution with the given mean and the closest achievable SCV at or below the requested one.

Return type:

Erlang

classmethod fit_mean_and_order(mean, phases)[source]

Create an Erlang distribution from mean and number of phases.

Parameters:
  • mean (float) – Target mean.

  • phases (int) – Number of phases (order).

Returns:

Erlang distribution with given mean and phases.

Return type:

Erlang

classmethod fitMeanAndScv(mean, scv)

Create an Erlang distribution from mean and SCV.

For Erlang, SCV = 1/k where k is the number of phases, so the order is k = ceil(1/SCV): the achievable SCVs are 1, 1/2, 1/3, … and the fit takes the first one AT OR BELOW the request. Rounding instead would return a different law – at SCV=0.4, ceil gives 3 phases and round gives 2 – and every solver downstream would answer a different model with no error raised. MATLAB Erlang.fitMeanAndSCV, the JAR and the C++ port all use ceil.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation, which must be <= 1.

Returns:

Erlang distribution with the given mean and the closest achievable SCV at or below the requested one.

Return type:

Erlang

classmethod fitMeanAndSCV(mean, scv)

Create an Erlang distribution from mean and SCV.

For Erlang, SCV = 1/k where k is the number of phases, so the order is k = ceil(1/SCV): the achievable SCVs are 1, 1/2, 1/3, … and the fit takes the first one AT OR BELOW the request. Rounding instead would return a different law – at SCV=0.4, ceil gives 3 phases and round gives 2 – and every solver downstream would answer a different model with no error raised. MATLAB Erlang.fitMeanAndSCV, the JAR and the C++ port all use ceil.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation, which must be <= 1.

Returns:

Erlang distribution with the given mean and the closest achievable SCV at or below the requested one.

Return type:

Erlang

classmethod fitMeanAndOrder(mean, phases)

Create an Erlang distribution from mean and number of phases.

Parameters:
  • mean (float) – Target mean.

  • phases (int) – Number of phases (order).

Returns:

Erlang distribution with given mean and phases.

Return type:

Erlang

property phases: int

Get the number of phases.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSCV()[source]

Get the SCV (1/phases).

getSkew()[source]

Get the skewness.

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

getNumberOfPhases()[source]

Get the number of phases.

getD0()[source]

Get the D0 matrix for MAP representation.

getD1()[source]

Get the D1 matrix for MAP representation.

getMu()[source]

Get the service rates in each phase.

getPhi()[source]

Get the completion probabilities from each phase.

getInitProb()[source]

Get the initial probability vector.

class Gamma(shape, scale)[source]

Bases: ContinuousDistribution

Gamma distribution.

The gamma distribution is a two-parameter continuous distribution that generalizes the exponential and Erlang distributions.

Parameters:
  • shape (float) – Shape parameter (k or alpha).

  • scale (float) – Scale parameter (theta).

Initialize a new distribution.

property shape: float

Get the shape parameter.

property scale: float

Get the scale parameter.

getMean()[source]

Get the mean (shape * scale).

getVar()[source]

Get the variance (shape * scale^2).

getSCV()[source]

Get the SCV (1/shape).

getSkew()[source]

Get the skewness.

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

evalLST(s)[source]

LST of the Gamma law, (beta/(s+beta))^shape with beta = 1/scale.

MATLAB, the JAR and the cpp port all carry this closed form; without it the base rectangle rule answered here, which is both approximate (3.3e-4 relative at s = 0.5+1i) and real-only. Being analytic it also serves the complex arguments transform inversion needs.

sample(n=1, rng=None)[source]

Generate random samples.

classmethod fit_mean_and_scv(mean, scv)[source]

Create a Gamma distribution from mean and SCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Gamma distribution with given mean and SCV.

Return type:

Gamma

classmethod fitMeanAndSCV(mean, scv)

Create a Gamma distribution from mean and SCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Gamma distribution with given mean and SCV.

Return type:

Gamma

classmethod fitMeanAndScv(mean, scv)

Create a Gamma distribution from mean and SCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Gamma distribution with given mean and SCV.

Return type:

Gamma

class HyperExp(p_or_probs, rate1_or_rates, rate2=None)[source]

Bases: ContinuousDistribution, Markovian

Hyperexponential distribution (mixture of exponentials).

The hyperexponential distribution is a mixture of exponential distributions. It has SCV >= 1.

Supports two calling conventions (matching MATLAB API):
  • HyperExp(p, rate1, rate2): 2-phase with probability p of rate1, probability (1-p) of rate2

  • HyperExp(probs, rates): n-phase with vectors of probabilities and rates

Parameters:
  • p_or_probs (float | list | ndarray) – For 2-phase: probability of first component (scalar). For n-phase: list/array of probabilities.

  • rate1_or_rates (float | list | ndarray) – For 2-phase: rate of first component (scalar). For n-phase: list/array of rates.

  • rate2 (float | None) – For 2-phase only: rate of second component.

Initialize a new distribution.

classmethod fit(mean, scv=None, skew=None, **kwargs)[source]

Fit a two-phase hyperexponential to three moments (MATLAB HyperExp.fit).

MATLAB tries a Prony fit of the moment triple first and falls back to the two-moment fit when it is infeasible. The fallback is used here, which is what MATLAB itself returns whenever the triple is not hyperexponential-feasible.

HyperExp.fit(dist, method='feldmannwhitt', ...) instead fits the ccdf of the distribution dist ITSELF at points spread over decades of time scale, rather than matching moments (hyperexp_fit_longtail, Feldmann and Whitt 1998). That is the only form available for a long-tail law: a Pareto with tail index below 2 has no finite variance, so the moment fit above does not exist at all, and even where the moments are finite they say nothing about the orders of magnitude over which such a law acts. Any further keyword arguments (k, c1, b, decade, points) are passed through.

classmethod fit_mean(mean)[source]

Two-phase hyperexponential with both rates 1/mean (MATLAB HyperExp.fitMean). Both phases share the rate, so the mixing probability is immaterial and is taken as 0.5.

classmethod fit_rate(rate)[source]

Two-phase hyperexponential with both rates equal to rate (MATLAB HyperExp.fitRate).

classmethod fitMean(mean)[source]

camelCase alias of fit_mean (MATLAB/JAR spelling).

classmethod fitRate(rate)[source]

camelCase alias of fit_rate (MATLAB/JAR spelling).

classmethod fit_mean_and_scv(mean, scv, p=0.99)[source]

Create a 2-phase hyperexponential distribution from mean and SCV.

Uses the same algorithm as MATLAB’s map_hyperexp function.

Parameters:
  • mean (float) – Target mean (MEAN).

  • scv (float) – Target squared coefficient of variation (must be >= 1).

  • p (float) – Probability of being served in phase 1 (default: 0.99).

Returns:

HyperExp distribution with given mean and SCV.

Return type:

HyperExp

classmethod fit_mean_and_scv_balanced(mean, scv)[source]

Create a 2-phase hyperexponential distribution with balanced means.

Uses balanced means representation where p/mu1 = (1-p)/mu2.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation (must be >= 1).

Returns:

HyperExp distribution with given mean and SCV.

Return type:

HyperExp

classmethod fitMeanAndScvBalanced(mean, scv)

Create a 2-phase hyperexponential distribution with balanced means.

Uses balanced means representation where p/mu1 = (1-p)/mu2.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation (must be >= 1).

Returns:

HyperExp distribution with given mean and SCV.

Return type:

HyperExp

classmethod fitMeanAndSCVBalanced(mean, scv)

Create a 2-phase hyperexponential distribution with balanced means.

Uses balanced means representation where p/mu1 = (1-p)/mu2.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation (must be >= 1).

Returns:

HyperExp distribution with given mean and SCV.

Return type:

HyperExp

classmethod fitMeanAndScv(mean, scv, p=0.99)

Create a 2-phase hyperexponential distribution from mean and SCV.

Uses the same algorithm as MATLAB’s map_hyperexp function.

Parameters:
  • mean (float) – Target mean (MEAN).

  • scv (float) – Target squared coefficient of variation (must be >= 1).

  • p (float) – Probability of being served in phase 1 (default: 0.99).

Returns:

HyperExp distribution with given mean and SCV.

Return type:

HyperExp

classmethod fitMeanAndSCV(mean, scv, p=0.99)

Create a 2-phase hyperexponential distribution from mean and SCV.

Uses the same algorithm as MATLAB’s map_hyperexp function.

Parameters:
  • mean (float) – Target mean (MEAN).

  • scv (float) – Target squared coefficient of variation (must be >= 1).

  • p (float) – Probability of being served in phase 1 (default: 0.99).

Returns:

HyperExp distribution with given mean and SCV.

Return type:

HyperExp

property means: ndarray

Get the means of each component.

property probs: ndarray

Get the probabilities of each component.

property rates: ndarray

Get the rates of each component.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSkew()[source]

Get the skewness.

A phase-type mixture has raw moments E[S^k] = k! * sum_i p_i * m_i^k, so the third central moment follows in closed form. Without this the base-class default returned 0, i.e. a symmetric law, which silently understates E[S^3] for every consumer that reconstructs it from the skewness (the ForkTail branch variance among them: it read E[S^3] = 13 instead of 141.55 for the SCV = 4 fit).

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

getNumberOfPhases()[source]

Get the number of phases.

getD0()[source]

Get the D0 matrix for MAP representation.

getD1()[source]

Get the D1 matrix for MAP representation.

getMu()[source]

Get the service rates in each phase.

getPhi()[source]

Get the completion probabilities from each phase.

getInitProb()[source]

Get the initial probability vector.

class Lognormal(mu, sigma)[source]

Bases: ContinuousDistribution

Lognormal distribution.

A random variable X has a lognormal distribution if log(X) is normally distributed.

Parameters:
  • mu (float) – Mean of the underlying normal distribution.

  • sigma (float) – Standard deviation of the underlying normal distribution.

Initialize a new distribution.

property mu: float

Get the mu parameter.

property sigma: float

Get the sigma parameter.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSkew()[source]

Get the skewness.

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

evalLST(s)[source]

Numerical LST (rectangle rule, n=1000) matching MATLAB Lognormal.evalLST.

numpy rather than math for the kernel, so a COMPLEX argument is admissible: transform inversion and root location both need one, and MATLAB’s own quadrature extends to the complex plane unchanged.

sample(n=1, rng=None)[source]

Generate random samples.

classmethod fit_mean_and_scv(mean, scv)[source]

Construct a Lognormal from a target mean and squared coefficient of variation (SCV = variance/mean^2), converting to log-space (mu, sigma).

Port of MATLAB Lognormal.fitMeanAndSCV.

classmethod fitMeanAndSCV(mean, scv)

Construct a Lognormal from a target mean and squared coefficient of variation (SCV = variance/mean^2), converting to log-space (mu, sigma).

Port of MATLAB Lognormal.fitMeanAndSCV.

classmethod fitMeanAndScv(mean, scv)

Construct a Lognormal from a target mean and squared coefficient of variation (SCV = variance/mean^2), converting to log-space (mu, sigma).

Port of MATLAB Lognormal.fitMeanAndSCV.

class Pareto(alpha, scale)[source]

Bases: ContinuousDistribution

Pareto distribution.

The Pareto distribution is a power-law distribution often used to model heavy-tailed phenomena.

Parameters:
  • alpha (float) – Shape parameter (tail index).

  • scale (float) – Scale parameter (minimum value).

Initialize a new distribution.

property alpha: float

Get the alpha parameter.

property scale: float

Get the scale parameter.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSkew()[source]

Get the skewness.

For a Pareto law with shape alpha the third moment exists only when alpha > 3, and the skewness is 2*(1+alpha)/(alpha-3)*sqrt((alpha-2)/alpha). Without this the base-class default returned 0, i.e. a symmetric law, which silently understates E[S^3] for every consumer that reconstructs it from the skewness.

getSupport()[source]

Get the support [scale, inf).

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

evalLST(s)[source]

Laplace-Stieltjes transform E[e^{-sX}] of the Pareto distribution.

A*(s) = int_k^inf e^{-sx} alpha k^alpha x^{-(alpha+1)} dx. Substituting x = k/u maps the infinite tail onto a unit interval and cancels the scale exactly:

A*(s) = alpha * int_0^1 u^(alpha-1) exp(-s*k/u) du

This is the same transform as the closed form of Nadarajah & Kotz, A*(s) = alpha*(s*k)^alpha*Gamma(-alpha, s*k) = alpha*E_{alpha+1}(s*k) (Queueing Syst (2006) 54:243-244, DOI 10.1007/s11134-006-0299-1), but in a form that stays accurate as s -> 0, where the incomplete-gamma product underflows to 0/inf. Here s = 0 gives alpha*int_0^1 u^(alpha-1) du = 1 exactly, and the integrand is bounded and C^inf on a FINITE interval for alpha >= 2 (the shape floor the constructor enforces).

Accuracy: adaptive Gauss-Kronrod at 1e-12 relative, matching the MATLAB and JAR implementations, verified against mpmath to 1e-15. The previous implementation was a 1000-point right-endpoint rectangle sum truncated at k*1000**(1/alpha); it lost the mass beyond the truncation point and biased the transform low by ~3.1% at alpha=2.0078 (it returned A*(0)=0.96914, not 1).

sample(n=1, rng=None)[source]

Generate random samples.

classmethod fit_mean_and_scv(mean, scv)[source]

Create a Pareto distribution from mean and SCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Pareto distribution with given mean and SCV.

Return type:

Pareto

Note

For Pareto with alpha > 2: mean = alpha * scale / (alpha - 1) var = scale^2 * alpha / ((alpha - 1)^2 * (alpha - 2)) scv = var / mean^2 = 1 / (alpha * (alpha - 2))

Solving for alpha: alpha = (1 + sqrt(1 + 4*scv)) / (2*scv) Then: scale = mean * (alpha - 1) / alpha

classmethod fitMeanAndSCV(mean, scv)

Create a Pareto distribution from mean and SCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Pareto distribution with given mean and SCV.

Return type:

Pareto

Note

For Pareto with alpha > 2: mean = alpha * scale / (alpha - 1) var = scale^2 * alpha / ((alpha - 1)^2 * (alpha - 2)) scv = var / mean^2 = 1 / (alpha * (alpha - 2))

Solving for alpha: alpha = (1 + sqrt(1 + 4*scv)) / (2*scv) Then: scale = mean * (alpha - 1) / alpha

classmethod fitMeanAndScv(mean, scv)

Create a Pareto distribution from mean and SCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Pareto distribution with given mean and SCV.

Return type:

Pareto

Note

For Pareto with alpha > 2: mean = alpha * scale / (alpha - 1) var = scale^2 * alpha / ((alpha - 1)^2 * (alpha - 2)) scv = var / mean^2 = 1 / (alpha * (alpha - 2))

Solving for alpha: alpha = (1 + sqrt(1 + 4*scv)) / (2*scv) Then: scale = mean * (alpha - 1) / alpha

class Uniform(min_val, max_val)[source]

Bases: ContinuousDistribution

Uniform distribution on [min, max].

Parameters:
  • min_val (float) – Minimum value.

  • max_val (float) – Maximum value.

Initialize a new distribution.

property min_val: float

Get the minimum value.

property max_val: float

Get the maximum value.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSkew()[source]

Get the skewness (always 0 for uniform).

getSupport()[source]

Get the support [min, max].

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

evalLST(s)[source]

LST of Uniform[min,max]: (e^{-s*min}-e^{-s*max})/(s*(max-min)). Matches MATLAB. numpy rather than math, so a COMPLEX argument is admissible.

sample(n=1, rng=None)[source]

Generate random samples.

class Weibull(shape, scale)[source]

Bases: ContinuousDistribution

Weibull distribution.

The Weibull distribution is commonly used in reliability engineering to model time to failure.

Parameters:
  • shape (float) – Shape parameter (k).

  • scale (float) – Scale parameter (lambda).

Initialize a new distribution.

classmethod fit_mean_and_scv(mean, scv)[source]

Fit a Weibull to a mean and squared coefficient of variation.

Port of MATLAB Weibull.fitMeanAndSCV and jline.lang.processes.Weibull. The shape comes from the Justus et al. (1976) approximation k = CV^(-1.086) with CV = sqrt(scv); the scale then makes the MEAN exact, scale = mean / Gamma(1 + 1/k). Only the SCV is approximate: the error is below 3% inside the range the approximation was published for (k in [1,10], i.e. scv <= 1) and grows quickly outside it (12% at scv = 2, 48% at scv = 4).

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

classmethod fitMeanAndSCV(mean, scv)[source]

camelCase alias of fit_mean_and_scv (MATLAB/JAR spelling).

property shape: float

Get the shape parameter.

property scale: float

Get the scale parameter.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSkew()[source]

Get the skewness.

With g_k = Gamma(1 + k/shape), the Weibull third central moment gives skew = (g3 - 3*g1*g2 + 2*g1^3) / (g2 - g1^2)^1.5, scale-free. Without this the base-class default returned 0, i.e. a symmetric law.

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

evalLST(s)[source]

Numerical LST (rectangle rule, n=1000) matching MATLAB Weibull.evalLST.

numpy rather than math for the kernel, so a COMPLEX argument is admissible, as in MATLAB.

sample(n=1, rng=None)[source]

Generate random samples.

Phase-Type Distributions

class APH(alpha_or_mean=None, T_or_scv=1.0, skew=None, mean=None, scv=None)[source]

Bases: PH

Acyclic Phase-Type distribution.

An APH distribution is a phase-type distribution where the underlying Markov chain has an acyclic structure (upper triangular T).

Can be constructed in two ways: 1. From matrices: APH(alpha, T) - like PH 2. From moments: APH(mean, scv=1.0, skew=None) - moment matching

Args (matrix form):

alpha: Initial probability vector (1 x n). T: Sub-generator matrix (n x n).

Args (moment form):

mean: Target mean (must be a positive scalar). scv: Target squared coefficient of variation (default: 1.0). skew: Target skewness (optional, for 3-moment matching).

Initialize APH from matrices or moments.

__init__(alpha_or_mean=None, T_or_scv=1.0, skew=None, mean=None, scv=None)[source]

Initialize APH from matrices or moments.

evalCDF(t=None)[source]

Evaluate the CDF, as MATLAB APH.evalCDF does.

Three call forms, all matching the reference:

  • evalCDF() returns the law tabulated on its own grid, an (n, 2) array whose columns are [F(t), t] – the column order every CDF getter in LINE uses, NOT [t, F(t)]. The grid is 500 uniform points over [0, mean + 10*sigma], the reference’s own horizon.

  • evalCDF(t) with a scalar returns F(t) as a float.

  • evalCDF(t) with a vector returns F at those points, without the time column, again as the reference does.

Parameters:

t – time point, sequence of time points, or None for the default grid.

Returns:

An (n, 2) array of [F, t] when called with no argument, a float for a scalar argument, or an array of F values.

eval_cdf(t=None)

Evaluate the CDF, as MATLAB APH.evalCDF does.

Three call forms, all matching the reference:

  • evalCDF() returns the law tabulated on its own grid, an (n, 2) array whose columns are [F(t), t] – the column order every CDF getter in LINE uses, NOT [t, F(t)]. The grid is 500 uniform points over [0, mean + 10*sigma], the reference’s own horizon.

  • evalCDF(t) with a scalar returns F(t) as a float.

  • evalCDF(t) with a vector returns F at those points, without the time column, again as the reference does.

Parameters:

t – time point, sequence of time points, or None for the default grid.

Returns:

An (n, 2) array of [F, t] when called with no argument, a float for a scalar argument, or an array of F values.

classmethod fit(mean, scv, skew=None)[source]

Fit an APH to (mean, SCV, skewness), as MATLAB APH.fit does.

classmethod fit_mean_and_scv(mean, scv)[source]

Create an APH distribution from mean and SCV.

Uses moment matching to construct an acyclic phase-type distribution with the specified mean and squared coefficient of variation.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

APH distribution with given mean and SCV.

Return type:

APH

classmethod fitMeanAndScv(mean, scv)

Create an APH distribution from mean and SCV.

Uses moment matching to construct an acyclic phase-type distribution with the specified mean and squared coefficient of variation.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

APH distribution with given mean and SCV.

Return type:

APH

classmethod fitMeanAndSCV(mean, scv)

Create an APH distribution from mean and SCV.

Uses moment matching to construct an acyclic phase-type distribution with the specified mean and squared coefficient of variation.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

APH distribution with given mean and SCV.

Return type:

APH

classmethod fit_central(mean, scv, skew=None)[source]

Create an APH distribution from central moments.

Uses moment matching to construct an acyclic phase-type distribution with the specified mean, SCV, and optionally skewness.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

  • skew (float) – Target skewness (optional).

Returns:

APH distribution with given moments.

Return type:

APH

classmethod fitCentral(mean, scv, skew=None)

Create an APH distribution from central moments.

Uses moment matching to construct an acyclic phase-type distribution with the specified mean, SCV, and optionally skewness.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

  • skew (float) – Target skewness (optional).

Returns:

APH distribution with given moments.

Return type:

APH

classmethod fit_raw_moments(m1, m2, m3)[source]

Create an APH distribution from the first three raw moments.

Parameters:
  • m1 (float) – First raw moment E[X].

  • m2 (float) – Second raw moment E[X^2].

  • m3 (float) – Third raw moment E[X^3].

Returns:

APH distribution matching the given raw moments.

Return type:

APH

References

MATLAB: matlab/src/lang/processes/APH.m (fitRawMoments)

classmethod fitRawMoments(m1, m2, m3)

Create an APH distribution from the first three raw moments.

Parameters:
  • m1 (float) – First raw moment E[X].

  • m2 (float) – Second raw moment E[X^2].

  • m3 (float) – Third raw moment E[X^3].

Returns:

APH distribution matching the given raw moments.

Return type:

APH

References

MATLAB: matlab/src/lang/processes/APH.m (fitRawMoments)

class Coxian(means_or_rates, probs=None)[source]

Bases: PH

Coxian distribution.

A Coxian distribution is a special case of phase-type distributions where transitions can only go to the next phase or to absorption.

Parameters:
  • means_or_rates (list | ndarray) – Service rates (mu) for each phase. Rates are converted to means internally.

  • probs (list | ndarray | None) – Transition probabilities (phi). If length equals number of phases, the last element (which should be 1.0 for absorption) is dropped. If length is n-1, used as-is.

Initialize a new distribution.

get_feature_name()[source]

‘Cox2’ at two phases, ‘Coxian’ otherwise.

The registry carries both names, and the Cox2 entry can only mean the two-phase Coxian: MATLAB has no Cox2 object to mark (Cox2 there is a static factory returning a Coxian) and the C++ port already types every two-phase Coxian as ProcessType.COX2. Reading the entry off the phase count is therefore the one reading all four codebases share. A solver declaring just ‘Coxian’ stays accepting through SolverFeatureSet.GENERALIZATION_OF.

property means: ndarray

Get the phase means.

property probs: ndarray

Get the continuation probabilities.

classmethod fit_mean_and_scv(mean, scv)[source]

Create a Coxian distribution from mean and SCV.

Uses moment matching to construct a Coxian distribution. Matches MATLAB Coxian.fitMeanAndSCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Coxian distribution with given mean and SCV.

Return type:

Coxian

classmethod fit_central(mean, var, skew=None)[source]

Create a Coxian distribution from the first three central moments.

Port of MATLAB Coxian.fitCentral and jline.lang.processes.Coxian .fitCentral, which agree line for line: fit the third moment exactly with a 2-phase Coxian, and fall back to the two-moment fit only when that solution misses the target SCV by more than 1%.

Parameters:
  • mean (float) – Target mean.

  • var (float) – Target variance (NOT SCV – matches the MATLAB/JAR signature).

  • skew (float) – Target skewness. When omitted, reduces to a two-moment fit.

Returns:

Coxian distribution with given moments.

Return type:

Coxian

classmethod fitMeanAndSCV(mean, scv)

Create a Coxian distribution from mean and SCV.

Uses moment matching to construct a Coxian distribution. Matches MATLAB Coxian.fitMeanAndSCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Coxian distribution with given mean and SCV.

Return type:

Coxian

classmethod fitMeanAndScv(mean, scv)

Create a Coxian distribution from mean and SCV.

Uses moment matching to construct a Coxian distribution. Matches MATLAB Coxian.fitMeanAndSCV.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Coxian distribution with given mean and SCV.

Return type:

Coxian

classmethod fitCentral(mean, var, skew=None)

Create a Coxian distribution from the first three central moments.

Port of MATLAB Coxian.fitCentral and jline.lang.processes.Coxian .fitCentral, which agree line for line: fit the third moment exactly with a 2-phase Coxian, and fall back to the two-moment fit only when that solution misses the target SCV by more than 1%.

Parameters:
  • mean (float) – Target mean.

  • var (float) – Target variance (NOT SCV – matches the MATLAB/JAR signature).

  • skew (float) – Target skewness. When omitted, reduces to a two-moment fit.

Returns:

Coxian distribution with given moments.

Return type:

Coxian

class Cox2(mu1, mu2, phi1)[source]

Bases: Coxian

2-phase Coxian distribution.

Convenience class for the common 2-phase case. Mirrors jline.lang.processes.Cox2 and MATLAB Cox2: the phases are given by their RATES, and phi1 is the COMPLETION probability of phase 1 (the job absorbs after phase 1 with probability phi1 and continues to phase 2 with probability 1 - phi1), matching Coxian’s phi convention.

Parameters:
  • mu1 (float) – Rate of phase 1.

  • mu2 (float) – Rate of phase 2.

  • phi1 (float) – Completion probability after phase 1.

Initialize a new distribution.

classmethod fit_mean_and_scv(mean, scv)[source]

Fit a 2-phase Coxian to a mean and SCV.

Port of jline.lang.processes.Cox2.fitMeanAndSCV and MATLAB Cox2.fitMeanAndSCV, which agree branch for branch.

This override is REQUIRED: Coxian.fit_mean_and_scv builds its result as cls(mu_list, phi_list), a signature Cox2 does not have, so the inherited classmethod raises TypeError on every call. It also may return an Erlang-like fit with more than two phases, which a Cox2 cannot represent.

A 2-phase Coxian cannot achieve SCV < 0.5; there phi1 comes out negative and the constructor rejects it. MATLAB and the JAR build the infeasible object silently instead.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Cox2 with the given mean and SCV.

Return type:

Cox2

classmethod fit_mean(mean)[source]

Fit a 2-phase Coxian to a mean alone.

Port of jline.lang.processes.Cox2.fitMean and MATLAB Cox2.fitMean.

classmethod fit_central(mean, var, skew=None)[source]

Fit a 2-phase Coxian to the first three central moments.

Port of jline.lang.processes.Cox2.fitCentral and MATLAB Cox2.fitCentral. This override is REQUIRED for a different reason than fit_mean_and_scv: it is the exact three-moment solver that Coxian.fit_central delegates to, and it returns a Cox2 rather than the general Coxian its caller then rebuilds.

Falls back to a two-moment fit when the three-moment solution is infeasible, and to a mean-only fit when SCV < 0.5, as the JAR does.

Parameters:
  • mean (float) – Target mean.

  • var (float) – Target variance (NOT SCV – matches the JAR/MATLAB signature).

  • skew (float) – Target skewness. When omitted, reduces to a two-moment fit.

classmethod fitMeanAndSCV(mean, scv)

Fit a 2-phase Coxian to a mean and SCV.

Port of jline.lang.processes.Cox2.fitMeanAndSCV and MATLAB Cox2.fitMeanAndSCV, which agree branch for branch.

This override is REQUIRED: Coxian.fit_mean_and_scv builds its result as cls(mu_list, phi_list), a signature Cox2 does not have, so the inherited classmethod raises TypeError on every call. It also may return an Erlang-like fit with more than two phases, which a Cox2 cannot represent.

A 2-phase Coxian cannot achieve SCV < 0.5; there phi1 comes out negative and the constructor rejects it. MATLAB and the JAR build the infeasible object silently instead.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Cox2 with the given mean and SCV.

Return type:

Cox2

classmethod fitMeanAndScv(mean, scv)

Fit a 2-phase Coxian to a mean and SCV.

Port of jline.lang.processes.Cox2.fitMeanAndSCV and MATLAB Cox2.fitMeanAndSCV, which agree branch for branch.

This override is REQUIRED: Coxian.fit_mean_and_scv builds its result as cls(mu_list, phi_list), a signature Cox2 does not have, so the inherited classmethod raises TypeError on every call. It also may return an Erlang-like fit with more than two phases, which a Cox2 cannot represent.

A 2-phase Coxian cannot achieve SCV < 0.5; there phi1 comes out negative and the constructor rejects it. MATLAB and the JAR build the infeasible object silently instead.

Parameters:
  • mean (float) – Target mean.

  • scv (float) – Target squared coefficient of variation.

Returns:

Cox2 with the given mean and SCV.

Return type:

Cox2

classmethod fitMean(mean)

Fit a 2-phase Coxian to a mean alone.

Port of jline.lang.processes.Cox2.fitMean and MATLAB Cox2.fitMean.

classmethod fitCentral(mean, var, skew=None)

Fit a 2-phase Coxian to the first three central moments.

Port of jline.lang.processes.Cox2.fitCentral and MATLAB Cox2.fitCentral. This override is REQUIRED for a different reason than fit_mean_and_scv: it is the exact three-moment solver that Coxian.fit_central delegates to, and it returns a Cox2 rather than the general Coxian its caller then rebuilds.

Falls back to a two-moment fit when the three-moment solution is infeasible, and to a mean-only fit when SCV < 0.5, as the JAR does.

Parameters:
  • mean (float) – Target mean.

  • var (float) – Target variance (NOT SCV – matches the JAR/MATLAB signature).

  • skew (float) – Target skewness. When omitted, reduces to a two-moment fit.

class PH(alpha, T)[source]

Bases: ContinuousDistribution, Markovian

Phase-type distribution.

A phase-type distribution is defined by an initial probability vector alpha and a sub-generator matrix T. The distribution represents the time until absorption in a continuous-time Markov chain.

Parameters:
  • alpha (list | ndarray) – Initial probability vector (1 x n).

  • T (list | ndarray) – Sub-generator matrix (n x n). Must have negative diagonal and non-negative off-diagonal elements.

Initialize a new distribution.

property alpha: ndarray

Get the initial probability vector.

property T: ndarray

Get the sub-generator matrix.

evalLST(s)[source]

Laplace-Stieltjes transform, alpha (sI - T)^-1 t, mirroring MATLAB.

The inherited fallback is a rectangle rule over evalPDF, which carries percent-level error on a phase-type law and returns zero for a COMPLEX argument, since it evaluates math.exp. Every subclass here (Exp, Erlang, HyperExp, Coxian, APH) is phase type, so the closed form applies to all of them, and it is analytic, so it serves the complex arguments that transform inversion and root location need.

property t: ndarray

Get the exit rate vector.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSkew()[source]

Get the skewness, (E3 - 3 E1 E2 + 2 E1^3) / (E2 - E1^2)^(3/2).

The base Distribution returns 0.0, which is silently wrong for a phase-type; the moments are available in closed form here.

getNumberOfPhases()[source]

Get the number of phases.

getD0()[source]

Get the D0 matrix (equals T).

getD1()[source]

Get the D1 matrix.

getMu()[source]

Get the service rates in each phase.

getPhi()[source]

Get the completion probabilities from each phase.

getInitProb()[source]

Get the initial probability vector.

evalCDF(x)[source]

Evaluate the CDF at point x.

evalPDF(x)[source]

Evaluate the PDF at point x.

sample(n=1, rng=None)[source]

Generate random samples using simulation.

classmethod fit(mean, scv, skew=None)[source]

Fit a PH to (mean, SCV, skewness).

Mirrors MATLAB PH.fit, whose two-state search falls back to the acyclic fitter when it finds nothing; APH is a PH, so the acyclic fit is a valid return value here.

classmethod fit_central(mean, var, skew=None)[source]

Fit a PH to (mean, variance, skewness), as MATLAB PH.fitCentral.

classmethod fit_mean_and_scv(mean, scv)[source]

Fit a PH to (mean, SCV), as MATLAB PH.fitMeanAndSCV.

Two moments only, so the minimum-skewness third moment of the acyclic class is used. The MATLAB method referenced an undefined SKEW until 2026-07-22 and could not be called at all.

classmethod fit_raw_moments(m1, m2, m3)[source]

Fit a PH to the first three raw moments, as MATLAB PH.fitRawMoments.

Markovian Arrival Processes

class MAP(D0, D1)[source]

Bases: ContinuousDistribution, Markovian

Markovian Arrival Process.

A MAP is a generalization of the Poisson process that can capture correlation between inter-arrival times. It is defined by two matrices D0 and D1 where: - D0 contains transition rates without arrivals - D1 contains transition rates with arrivals - D0 + D1 is a valid generator matrix

Parameters:
  • D0 (list | ndarray) – Matrix of transition rates without arrivals.

  • D1 (list | ndarray) – Matrix of transition rates with arrivals.

Initialize a new distribution.

property D0: ndarray

Get the D0 matrix.

property D1: ndarray

Get the D1 matrix.

property pi: ndarray

Get the stationary distribution.

getD0()[source]

Get the D0 matrix.

getD1()[source]

Get the D1 matrix.

toTimeReversed()[source]

Return the time-reversed MAP.

Useful in departure-process and reversed-time arguments.

References

api/mam/map_analysis.py (map_timereverse)

to_time_reversed()[source]

snake_case alias for toTimeReversed().

getACF(lags=1)[source]

Return the autocorrelation coefficients of the inter-arrival times at the requested lag(s).

References

api/mam/map_analysis.py (map_acf)

get_acf(lags=1)[source]

snake_case alias for getACF().

getIDC()[source]

Return the asymptotic index of dispersion for counts (IDC).

References

api/mam/map_analysis.py (map_idc)

get_idc()[source]

snake_case alias for getIDC().

getMean()[source]

Get the mean inter-arrival time.

getVar()[source]

Get the variance of inter-arrival times.

getSkew()[source]

Get the skewness of the inter-arrival times.

getNumberOfPhases()[source]

Get the number of phases.

getMu()[source]

Get the total outgoing rate from each phase.

mu_i = -D0(i,i), matching MATLAB Markovian.getMu. This is the rate at which phase i is left by ANY transition, not only by an arrival. Summing D1 instead counts only the arrival transitions, so the two agree only when D0 carries no off-diagonal mass – which is why every renewal PH-like MAP in the test suite hid the difference.

getPhi()[source]

Get the probability that a transition out of a phase is an arrival.

phi_i = (D1*e)_i / -D0(i,i), matching MATLAB Markovian.getPhi. It equals 1 only when every exit from phase i emits an arrival; a MAP with hidden phase changes has phi < 1. The D0(0,0) == 0 guard mirrors the MATLAB special case for an immediate process.

getInitProb()[source]

Get the phase distribution embedded at arrival instants.

This is map_pie, the equilibrium of the embedded DTMC P = (-D0)^(-1) D1, and it is the vector that governs the inter-arrival time marginal. It is NOT the time-stationary phase distribution self._pi, which is a different vector whenever the phase process is not symmetric in its arrival rates. Matches MATLAB Markovian.getInitProb, which calls map_pie, and agrees with RAP.getInitProb on a (D0,D1) pair that is both a MAP and a RAP.

getRate()[source]

Get the arrival rate (1/mean).

set_mean(target_mean)[source]

Create a new MAP with the specified mean by scaling rates.

The structure of the MAP is preserved, only the rates are scaled to achieve the target mean.

Parameters:

target_mean (float) – Target mean inter-arrival/service time.

Returns:

New MAP with the specified mean.

Return type:

MAP

setMean(target_mean)

Create a new MAP with the specified mean by scaling rates.

The structure of the MAP is preserved, only the rates are scaled to achieve the target mean.

Parameters:

target_mean (float) – Target mean inter-arrival/service time.

Returns:

New MAP with the specified mean.

Return type:

MAP

sample(n=1, rng=None)[source]

Generate random inter-arrival times.

classmethod rand(n=2, seed=None)[source]

Create a random MAP with n phases.

Generates a random MAP with the specified number of phases. The D0 and D1 matrices are randomly generated to form a valid MAP.

Parameters:
  • n (int) – Number of phases (default: 2).

  • seed (int) – Random seed for reproducibility (optional).

Returns:

MAP distribution with random parameters.

Return type:

MAP

class MMPP2(lambda0, lambda1, sigma0, sigma1)[source]

Bases: MAP

Markov-Modulated Poisson Process with 2 states.

A special case of MAP with two states, where arrivals occur according to Poisson processes with rates lambda0 and lambda1, and the modulating chain switches between states with rates sigma0 and sigma1.

Parameters:
  • lambda0 (float) – Arrival rate in state 0.

  • lambda1 (float) – Arrival rate in state 1.

  • sigma0 (float) – Transition rate from state 0 to state 1.

  • sigma1 (float) – Transition rate from state 1 to state 0.

Initialize a new distribution.

property lambda0: float

Get arrival rate in state 0.

property lambda1: float

Get arrival rate in state 1.

property sigma0: float

Get transition rate from state 0 to 1.

property sigma1: float

Get transition rate from state 1 to 0.

getIDC()[source]

Get the Index of Dispersion for Counts.

get_idc()[source]

Index of Dispersion for Counts (snake_case alias for getIDC).

static fitRawMomentsAndACFDecay(m1, m2, m3, gamma2)[source]

MMPP(2) matching three raw moments and the ACF decay rate.

Port of MATLAB MMPP2.fitRawMomentsAndACFDecay: mmpp2_fit3 on the raw moments, then the MMPP2 parameters read off (D1 diagonal, D0 off-diagonal).

static fitRawMomentsAndACFLag1(m1, m2, m3, rho1)[source]

MMPP(2) matching three raw moments and the lag-1 autocorrelation.

rho1 = gamma2 * (1 - 1/SCV)/2 for every MMPP(2) (proved in io/sage/proofs/mmpp2_fit3.py), which is the conversion used here.

static fitRawMomentsAndIDC(m1, m2, m3, idc)[source]

MMPP(2) matching three raw moments and the asymptotic index of dispersion, using gamma2 = (IDC - SCV)/(IDC - 1).

static fitCentralAndACFDecay(mean, var, skew, gamma2)[source]

MMPP(2) from central moments and the ACF decay rate (MATLAB MMPP2.fitCentralAndACFDecay -> mmpp2_fit2).

static fitCentralAndACFLag1(mean, var, skew, rho1)[source]

MMPP(2) from central moments and the lag-1 autocorrelation (MATLAB MMPP2.fitCentralAndACFLag1 -> mmpp2_fit4).

static fitCentralAndIDC(mean, var, skew, idc)[source]

MMPP(2) from central moments and the asymptotic index of dispersion (MATLAB MMPP2.fitCentralAndIDC -> mmpp2_fit1).

static fit_mean_scv_acf(mean, scv, acf_decay)[source]

Fit MMPP2 to match mean, SCV and the autocorrelation decay rate.

Uses the exact closed form of mmpp2_fit3 at the minimum-skewness third moment, E3 = (3/2 + 1e-3) * E2^2 / E1, which is the SKEW=-1 convention of map_mmpp2. The fitted process matches E1, E2 and acf_decay = rho(k+1)/rho(k) identically; the lag-1 autocorrelation it realizes is acf_decay * (1 - 1/scv) / 2.

Parameters:
  • mean (float) – Target mean inter-arrival time.

  • scv (float) – Target squared coefficient of variation (>= 1).

  • acf_decay (float) – ACF decay rate gamma2, in [0, 1).

Returns:

Fitted MMPP2 distribution.

Raises:

ValueError – if the request is outside the MMPP(2) feasible set.

Return type:

MMPP2

Discrete Distributions

class Bernoulli(p)[source]

Bases: DiscreteDistribution

Bernoulli distribution.

The simplest discrete distribution with two outcomes: success (1) with probability p and failure (0) with probability 1-p.

Parameters:

p (float) – Probability of success (0 <= p <= 1).

Initialize a new distribution.

property p: float

Get the probability of success.

getMean()[source]

Get the mean (equals p).

getVar()[source]

Get the variance (p * (1 - p)).

getSkew()[source]

Get the skewness.

getSupport()[source]

Get the support {0, 1}.

evalPMF(x)[source]

Evaluate the PMF at point x.

evalCDF(x)[source]

Evaluate the CDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

class Binomial(n, p)[source]

Bases: DiscreteDistribution

Binomial distribution.

The binomial distribution models the number of successes in n independent Bernoulli trials.

Parameters:
  • n (int) – Number of trials.

  • p (float) – Probability of success on each trial.

Initialize a new distribution.

property n: int

Get the number of trials.

property p: float

Get the probability of success.

getMean()[source]

Get the mean (n * p).

getVar()[source]

Get the variance (n * p * (1 - p)).

getSkew()[source]

Get the skewness.

getSupport()[source]

Get the support [0, n].

evalPMF(x)[source]

Evaluate the PMF at point x.

evalCDF(x)[source]

Evaluate the CDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

class DiscreteUniform(a, b)[source]

Bases: DiscreteDistribution

Discrete Uniform distribution.

Assigns equal probability to all integers in [a, b].

Parameters:
  • a (int) – Lower bound (inclusive).

  • b (int) – Upper bound (inclusive).

Initialize a new distribution.

property a: int

Get the lower bound.

property b: int

Get the upper bound.

getMean()[source]

Get the mean ((a + b) / 2).

getVar()[source]

Get the variance ((n^2 - 1) / 12).

getSkew()[source]

Get the skewness (0 for symmetric distribution).

getSupport()[source]

Get the support [a, b].

evalPMF(x)[source]

Evaluate the PMF at point x.

evalCDF(x)[source]

Evaluate the CDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

class Geometric(p)[source]

Bases: DiscreteDistribution

Geometric distribution.

The geometric distribution models the number of failures before the first success in a sequence of Bernoulli trials.

Parameters:

p (float) – Probability of success on each trial (0 < p <= 1).

Initialize a new distribution.

property p: float

Get the probability of success.

getMean()[source]

Get the mean (1/p for number of trials).

getVar()[source]

Get the variance.

getSkew()[source]

Get the skewness.

getSupport()[source]

Get the support [1, inf).

evalPMF(x)[source]

Evaluate the PMF at point x (number of trials until first success).

evalCDF(x)[source]

Evaluate the CDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

class Poisson(lambda_)[source]

Bases: DiscreteDistribution

Poisson distribution.

The Poisson distribution models the number of events occurring in a fixed interval of time or space.

Parameters:

lambda – Rate parameter (mean = variance = lambda).

Initialize a new distribution.

property lambda_: float

Get the rate parameter.

getMean()[source]

Get the mean (equals lambda).

getVar()[source]

Get the variance (equals lambda).

getSCV()[source]

Get the SCV (1/lambda).

getSkew()[source]

Get the skewness.

getSupport()[source]

Get the support [0, inf).

evalPMF(x)[source]

Evaluate the probability mass function at point x.

evalCDF(x)[source]

Evaluate the CDF at point x.

sample(n=1, rng=None)[source]

Generate random samples.

class Zipf(s, n=10000)[source]

Bases: DiscreteDistribution

Zipf distribution.

The Zipf distribution is a power-law distribution often used to model rank-frequency relationships (e.g., word frequencies).

Parameters:
  • s (float) – Shape parameter (s > 0).

  • n (int) – Upper bound on support (optional, defaults to large value).

Initialize a new distribution.

property s: float

Get the shape parameter.

property n: int

Get the upper bound.

getMean()[source]

Get the mean.

getVar()[source]

Get the variance.

getSupport()[source]

Get the support [1, n].

evalPMF(x)[source]

Evaluate the PMF at point x.

evalCDF(x)[source]

Evaluate the CDF at point x.

sample(n=1, rng=None)[source]

Generate random samples using inverse transform.

Special Distributions

class Disabled[source]

Bases: ContinuousDistribution

Disabled distribution.

Represents a disabled service (no service at all). Used for nodes that don’t serve a particular job class.

Initialize a new distribution.

classmethod getInstance()[source]

Get singleton instance of Disabled distribution.

classmethod get_instance()

Get singleton instance of Disabled distribution.

getMean()[source]

Get the mean (NaN: a disabled class has no service law at all).

NaN and not infinity, matching MATLAB Disabled.getMean and the JAR’s Disabled.getMean. The difference is load bearing wherever a caller selects the served classes with a getMean() > tol test: NaN fails that test, infinity passes it and admits every disabled class.

getVar()[source]

Get the variance (NaN, as in MATLAB and the JAR).

getSCV()[source]

Get the SCV (NaN, as in MATLAB and the JAR).

getRate()[source]

Get the rate (NaN, as in the JAR; not 1/inf = 0).

getSkew()[source]

Get the skewness (NaN, as in the JAR).

evalCDF(x)[source]

Evaluate the CDF (NaN, as in MATLAB and the JAR).

evalLST(s)[source]

Evaluate the Laplace-Stieltjes transform (NaN, as in the JAR).

sample(n=1, rng=None)[source]

Draw n samples, all NaN, as in MATLAB and the JAR.

isDisabled()[source]

Check if this distribution is disabled.

class Immediate[source]

Bases: Det

Immediate (zero delay) distribution.

Represents instantaneous service with zero delay.

Initialize a new distribution.

classmethod getInstance()[source]

Get singleton instance of Immediate distribution.

classmethod get_instance()

Get singleton instance of Immediate distribution.

getSCV()[source]

SCV of an immediate service, 1 as in MATLAB and the JAR. The variance over a zero mean is undefined, and the deterministic 0 that Det returns made an Immediate look like a Det service to any SCV-driven fit.

isImmediate()[source]

Check if this is immediate service.

class Replayer(trace, loop=True)[source]

Bases: DiscreteDistribution

Trace-based distribution that replays recorded values.

Used for simulation where inter-arrival or service times are read from a trace file or data array.

Parameters:
  • trace (str | list | ndarray) – Array of values to replay.

  • loop (bool) – Whether to loop when trace is exhausted (default: True).

Initialize a new distribution.

property trace: ndarray

Get the trace data.

property loop: bool

Check if looping is enabled.

reset()[source]

Reset the replay index to the beginning.

getMean()[source]

Get the mean of the trace.

getVar()[source]

Get the variance of the trace.

getSkewness()[source]

Get the skewness of the trace.

getSupport()[source]

Get the support [min, max] of trace values.

evalPMF(x)[source]

Evaluate PMF based on trace frequency.

evalCDF(x)[source]

Evaluate CDF based on trace.

evalLST(s)[source]

Empirical Laplace-Stieltjes transform, mean(exp(-s*trace)).

Exact for the trace, and what the G/M/1 sigma-root needs: without it sn.lst stayed None for a Replayer arrival and the MVA gm1 branch fell back to a two-moment approximation. Matches MATLAB Replayer.evalLST and the JAR Replayer.evalLST.

next_value()[source]

Get the next value in the trace.

sample(n=1, rng=None)[source]

Get the next n values from the trace.

isNHPP(**kwargs)[source]

Test whether the trace is a sample path of a NON-HOMOGENEOUS POISSON process, by the conditional-uniform KS test with the Lewis refinement (infer_nhpp_ks()).

WHY THE QUESTION IS WORTH ASKING. A Replayer is used wherever a measured stream is fed to a solver, and every analytical method that consumes it as an arrival process assumes SOMETHING about its dependence structure. This test says whether the Poisson assumption – independent increments, whatever the rate does with time – survives contact with the data, which is the assumption a time-varying analysis (SolverFLD’s mtginf, mol, tvms) rests on. A small p-value says the stream is not Poisson at any rate function, so those methods are answering a different process.

The trace holds INTER-ARRIVAL times, so the arrival epochs are their cumulative sum and the horizon is the last of them.

Returns:

statistic, pvalue, n, uniforms and transformed.

Return type:

The dict of infer_nhpp_ks()

References

S.-H. Kim, W. Whitt (2014). Are call center and hospital arrivals well modeled by nonhomogeneous Poisson processes? Manufacturing & Service Operations Management 16(3), 464-480.

is_nhpp(**kwargs)

Test whether the trace is a sample path of a NON-HOMOGENEOUS POISSON process, by the conditional-uniform KS test with the Lewis refinement (infer_nhpp_ks()).

WHY THE QUESTION IS WORTH ASKING. A Replayer is used wherever a measured stream is fed to a solver, and every analytical method that consumes it as an arrival process assumes SOMETHING about its dependence structure. This test says whether the Poisson assumption – independent increments, whatever the rate does with time – survives contact with the data, which is the assumption a time-varying analysis (SolverFLD’s mtginf, mol, tvms) rests on. A small p-value says the stream is not Poisson at any rate function, so those methods are answering a different process.

The trace holds INTER-ARRIVAL times, so the arrival epochs are their cumulative sum and the horizon is the last of them.

Returns:

statistic, pvalue, n, uniforms and transformed.

Return type:

The dict of infer_nhpp_ks()

References

S.-H. Kim, W. Whitt (2014). Are call center and hospital arrivals well modeled by nonhomogeneous Poisson processes? Manufacturing & Service Operations Management 16(3), 464-480.

fit_exp()[source]

Fit an exponential to the trace mean (MATLAB Replayer.fitExp).

Returns:

Exp distribution with the mean of the trace.

fit_coxian()[source]

Fit a two-phase Coxian to the first three moments of the trace (MATLAB Replayer.fitCoxian, which calls Cox2.fit).

Returns:

Cox2 distribution fitted to the trace moments.

fitExp()[source]

camelCase alias of fit_exp (MATLAB/JAR spelling).

fitCoxian()[source]

camelCase alias of fit_coxian (MATLAB/JAR spelling).

fitAPH()[source]

camelCase alias of fit_aph (MATLAB/JAR spelling).

fit_aph()[source]

Fit an acyclic phase-type (APH) distribution to the trace data.

Uses 3-moment matching (mean, SCV, skewness) to determine the optimal APH representation, matching MATLAB’s behavior.

Returns:

APH distribution fitted to the trace data.

Layered Networks (line_solver.layered)

The layered module provides support for layered queueing networks (LQNs).

Main Classes

class LayeredNetwork(name)[source]

Bases: object

Native Python implementation of a Layered Queueing Network.

This class provides a pure Python way to define and analyze layered queueing networks.

Example

>>> model = LayeredNetwork('ClientServer')
>>> P1 = model.add_processor('ClientProc', 1, SchedStrategy.PS)
>>> P2 = model.add_processor('ServerProc', 1, SchedStrategy.PS)
>>> T1 = model.add_task('Client', 5, SchedStrategy.REF, P1)
>>> T1.set_think_time(2.0)
>>> T2 = model.add_task('Server', float('inf'), SchedStrategy.INF, P2)
>>> E1 = model.add_entry('ClientEntry', T1)
>>> E2 = model.add_entry('ServerEntry', T2)
>>> A1 = model.add_activity('ClientAct', 0.5, T1)
>>> A1.bound_to(E1).synch_call(E2, 1.0)
>>> A2 = model.add_activity('ServerAct', 1.0, T2)
>>> A2.bound_to(E2).replies_to(E2)

Initialize a new layered queueing network.

findSolver(metric='', showAll=False)[source]

Which solvers and solver methods can analyze THIS model.

model.findSolver() # every (solver, method) pair that runs model.findSolver(‘cdf’) # … that returns a passage-time law model.findSolver(‘getCdfRespT’) # the same question, asked by accessor model.findSolver(‘’, True) # also the pairs that are refused, and why

The returned DataFrame has one row per pair, with columns Solver, Method, Runnable, Class (‘exact’, ‘approx’, ‘bound’ or ‘simulation’), Metrics and Reason. Method is the method name to pass as a solver method, so a row can be acted on directly:

T = model.findSolver('cdf')
solver = LINE(model, T.Method[0])

findMethod and help are aliases of this method.

Parameters:
  • metric (str) – measure group (‘cdf’) or accessor (‘getCdfRespT’) to narrow the report to; ‘’ or ‘any’ keeps every pair.

  • showAll (bool) – also list the refused pairs, with the reason each was refused.

Returns:

pandas.DataFrame with the six columns above.

findMethod(metric='', showAll=False)[source]

Alias of findSolver: which solvers and solver methods can analyze this model.

The two names exist because the question is asked both ways round – “which solver do I use” and “which method do I pass” – and the answer is the same table, whose Method column carries the method name either caller needs.

help(metric='', showAll=False)[source]

Alias of findSolver: what can this model be solved with?

find_solver(metric='', showAll=False)

Which solvers and solver methods can analyze THIS model.

model.findSolver() # every (solver, method) pair that runs model.findSolver(‘cdf’) # … that returns a passage-time law model.findSolver(‘getCdfRespT’) # the same question, asked by accessor model.findSolver(‘’, True) # also the pairs that are refused, and why

The returned DataFrame has one row per pair, with columns Solver, Method, Runnable, Class (‘exact’, ‘approx’, ‘bound’ or ‘simulation’), Metrics and Reason. Method is the method name to pass as a solver method, so a row can be acted on directly:

T = model.findSolver('cdf')
solver = LINE(model, T.Method[0])

findMethod and help are aliases of this method.

Parameters:
  • metric (str) – measure group (‘cdf’) or accessor (‘getCdfRespT’) to narrow the report to; ‘’ or ‘any’ keeps every pair.

  • showAll (bool) – also list the refused pairs, with the reason each was refused.

Returns:

pandas.DataFrame with the six columns above.

find_method(metric='', showAll=False)

Alias of findSolver: which solvers and solver methods can analyze this model.

The two names exist because the question is asked both ways round – “which solver do I use” and “which method do I pass” – and the answer is the same table, whose Method column carries the method name either caller needs.

__init__(name)[source]

Initialize a new layered queueing network.

add_processor(name_or_proc, multiplicity=None, sched_strategy=None)[source]

Add a processor to the network.

Supports two calling conventions: 1. add_processor(Processor_instance) 2. add_processor(name, multiplicity, sched_strategy)

add_task(name_or_task, multiplicity=None, sched_strategy=None, processor=None)[source]

Add a task to the network.

Supports two calling conventions: 1. add_task(Task_instance) 2. add_task(name, multiplicity, sched_strategy, processor)

add_entry(name_or_entry, task=None)[source]

Add an entry to the network.

Supports two calling conventions: 1. add_entry(Entry_instance) 2. add_entry(name, task)

add_activity(name_or_activity, host_demand=None, task=None)[source]

Add an activity to the network.

Supports two calling conventions: 1. add_activity(Activity_instance) 2. add_activity(name, host_demand, task)

write_xml(filename, use_abstract_names=False)[source]

Write the layered network to LQNX XML (snake_case alias for writeXML).

writeXML(filename, use_abstract_names=False)[source]

Write the layered network to LQNX XML format.

This method generates an LQNX file compatible with the lqns/lqsim command-line tools, matching the MATLAB writeXML implementation.

Parameters:
  • filename (str) – Path to write the LQNX XML file

  • use_abstract_names (bool) – If True, use abstract names (P1, T1, E1, A1…) instead of actual element names

Example

>>> model = LayeredNetwork('ClientServer')
>>> # ... build model ...
>>> model.writeXML('model.lqnx')
summary()[source]

Get a text summary of the layered network structure.

getNodeCount()[source]

Get total number of nodes (processors + tasks + entries + activities).

get_node_count()

Get total number of nodes (processors + tasks + entries + activities).

getNodeByName(name)[source]

Get a node by its name.

Parameters:

name (str) – Name of the node to find

Returns:

The node with the given name, or None if not found

get_node_by_name(name)

Get a node by its name.

Parameters:

name (str) – Name of the node to find

Returns:

The node with the given name, or None if not found

registerNode(node)[source]

Register a node with the network (compatibility method).

In native mode, nodes are auto-registered when created with the model. This method is provided for API compatibility.

Parameters:

node – The node to register (Processor, Task, Entry, or Activity)

register_node(node)

Register a node with the network (compatibility method).

In native mode, nodes are auto-registered when created with the model. This method is provided for API compatibility.

Parameters:

node – The node to register (Processor, Task, Entry, or Activity)

getHosts()[source]

Get all processors (hosts).

getTasks()[source]

Get all tasks.

getEntries()[source]

Get all entries.

getActivities()[source]

Get all activities.

getNodeNames()[source]

Get names of all nodes.

get_number_of_stateful_nodes()[source]

Aggregate (sum over layers) stateful-node count.

init_from_marginal(n, options=None)[source]

Split the aggregate (M x K) marginal queue-length matrix into per-layer blocks and warm-start each layer network. Mirrors MATLAB LayeredNetwork.initFromMarginal; the cross-switch state-continuity mechanism for SolverENV.

get_init_marginal_blocks()[source]

Per-layer blocks of the warm start supplied by the last init_from_marginal call, or None if none was supplied.

The layered fixed point hard-resets every layer when it detects convergence, which discards the warm start, so SolverLN replays these blocks just before running the layer transients; that is what lets a stage of SolverENV resume from the marginal handed over at the environment switch.

getNumberOfStatefulNodes()

Aggregate (sum over layers) stateful-node count.

getInitMarginalBlocks()

Per-layer blocks of the warm start supplied by the last init_from_marginal call, or None if none was supplied.

The layered fixed point hard-resets every layer when it detects convergence, which discards the warm start, so SolverLN replays these blocks just before running the layer transients; that is what lets a stage of SolverENV resume from the marginal handed over at the environment switch.

plotGraph(method='nodes', ax=None, show=True)[source]

Plot the layered-network call graph (native equivalent of MATLAB LayeredNetwork.plotGraph).

Nodes are laid out in type layers and colored by element type: hosts (black), tasks (magenta; reference tasks gold), entries (red), activities (blue).

Parameters:
  • method (str) – label source – ‘nodes’/’names’ use hashnames/names, ‘ids’ uses indices.

  • ax – optional matplotlib Axes to draw into.

  • show (bool) – call plt.show() when True.

Returns:

The matplotlib Axes containing the plot.

plot(show_task_graph=False, show=True)[source]

Plot the layered-network graph (native equivalent of MATLAB plot).

view(show=True)[source]

Visualize the layered network (native equivalent of MATLAB view).

getLayers()[source]

Get layers (returns list of tasks grouped by layer).

getNumberOfLayers()[source]

Get number of layers.

getNumberOfModels()[source]

Get number of models (returns 1 for single LQN).

get_hosts()

Get all processors (hosts).

get_tasks()

Get all tasks.

get_entries()

Get all entries.

get_activities()

Get all activities.

get_node_names()

Get names of all nodes.

get_layers()[source]

Get layers (returns list of tasks grouped by layer).

get_number_of_layers()

Get number of layers.

get_number_of_models()

Get number of models (returns 1 for single LQN).

copy()[source]

Create a deep copy of this layered network.

Returns:

A new LayeredNetwork instance with the same structure

Return type:

LayeredNetwork

property obj

Return self for compatibility with wrapper code that accesses .obj

classmethod parse_xml(filename, verbose=False)[source]

Parse an LQNX XML file and create a LayeredNetwork model.

This method parses layered queueing network XML files in LQNX format and constructs the corresponding Python model.

Parameters:
  • filename (str) – Path to the LQNX XML file

  • verbose (bool) – If True, print parsing progress

Returns:

LayeredNetwork model

Return type:

LayeredNetwork

Example

>>> model = LayeredNetwork.parse_xml('model.lqnx')
classmethod parseXML(filename, verbose=False)

Parse an LQNX XML file and create a LayeredNetwork model.

This method parses layered queueing network XML files in LQNX format and constructs the corresponding Python model.

Parameters:
  • filename (str) – Path to the LQNX XML file

  • verbose (bool) – If True, print parsing progress

Returns:

LayeredNetwork model

Return type:

LayeredNetwork

Example

>>> model = LayeredNetwork.parse_xml('model.lqnx')
classmethod readXML(filename, verbose=False)

Parse an LQNX XML file and create a LayeredNetwork model.

This method parses layered queueing network XML files in LQNX format and constructs the corresponding Python model.

Parameters:
  • filename (str) – Path to the LQNX XML file

  • verbose (bool) – If True, print parsing progress

Returns:

LayeredNetwork model

Return type:

LayeredNetwork

Example

>>> model = LayeredNetwork.parse_xml('model.lqnx')
classmethod load(filename, verbose=False)

Parse an LQNX XML file and create a LayeredNetwork model.

This method parses layered queueing network XML files in LQNX format and constructs the corresponding Python model.

Parameters:
  • filename (str) – Path to the LQNX XML file

  • verbose (bool) – If True, print parsing progress

Returns:

LayeredNetwork model

Return type:

LayeredNetwork

Example

>>> model = LayeredNetwork.parse_xml('model.lqnx')
class Processor(model_or_name, name_or_mult=None, mult_or_sched=None, sched=None)[source]

Bases: AdmissionConstrained, RateDependent

Processor in a layered queueing network.

A processor represents a computing resource (CPU, server, etc.) that hosts tasks. Processors have a multiplicity (number of identical resources) and a scheduling strategy.

Supports two calling conventions: 1. Processor(name, multiplicity, sched_strategy) 2. Processor(model, name, multiplicity, sched_strategy)

Initialize a Processor with flexible arguments.

__init__(model_or_name, name_or_mult=None, mult_or_sched=None, sched=None)[source]

Initialize a Processor with flexible arguments.

property obj

Return self for compatibility with wrapper code that accesses .obj

getMultiplicity()[source]

Get multiplicity.

getReplication()[source]

Get replication level.

setReplication(replication)[source]

Set replication level.

getScheduling()[source]

Get scheduling strategy.

getQuantum()[source]

Get quantum for PS scheduling.

setQuantum(quantum)[source]

Set quantum for PS scheduling.

getSpeedFactor()[source]

Get speed factor.

setSpeedFactor(speed_factor)[source]

Set speed factor.

get_multiplicity()

Get multiplicity.

get_replication()

Get replication level.

set_replication(replication)[source]

Set replication level.

get_scheduling()

Get scheduling strategy.

get_quantum()

Get quantum for PS scheduling.

set_quantum(quantum)

Set quantum for PS scheduling.

get_speed_factor()

Get speed factor.

set_speed_factor(speed_factor)

Set speed factor.

class Task(model_or_name, name_or_mult=None, mult_or_sched=None, sched=None)[source]

Bases: AdmissionConstrained, RateDependent

Task in a layered queueing network.

A task represents a software process or thread that provides services through entries. Tasks are deployed on processors and have a multiplicity (number of instances/threads).

Supports two calling conventions: 1. Task(name, multiplicity, sched_strategy) 2. Task(model, name, multiplicity, sched_strategy)

Initialize a Task with flexible arguments.

__init__(model_or_name, name_or_mult=None, mult_or_sched=None, sched=None)[source]

Initialize a Task with flexible arguments.

property obj

Return self for compatibility with wrapper code that accesses .obj

set_priority(priority)[source]

Set the scheduling priority of this task (lower is served first).

setPriority(priority)

Set the scheduling priority of this task (lower is served first).

on(processor)[source]

Deploy this task on a processor.

set_think_time(think_time)[source]

Set the think time for this task.

setSetupTime(setup_time)[source]

Set the setup time (cold start delay) for this task.

set_setup_time(setup_time)[source]

Set the setup time (cold start delay) for this task.

setDelayOffTime(delay_off_time)[source]

Set the delay-off time (teardown delay) for this task.

set_delay_off_time(delay_off_time)[source]

Set the delay-off time (teardown delay) for this task.

add_precedence(precedence)[source]

Add activity precedence constraint(s) to this task.

Precedence constraints define the execution order of activities within the task, supporting serial, parallel, and conditional patterns.

Parameters:

precedence (ActivityPrecedence | List[ActivityPrecedence]) – An ActivityPrecedence object or list of ActivityPrecedence objects, created using serial(), and_fork(), and_join(), or_fork(), or_join(), or loop()

Returns:

self (for method chaining)

Return type:

Task

Example

>>> task.add_precedence(ActivityPrecedence.serial([a1, a2, a3]))
>>> task.add_precedence([
...     ActivityPrecedence.serial([a1, a2]),
...     ActivityPrecedence.and_fork(a1, [a3, a4])
... ])
getMultiplicity()[source]

Get multiplicity.

getReplication()[source]

Get replication level.

setReplication(replication)[source]

Set replication level.

getScheduling()[source]

Get scheduling strategy.

getThinkTimeMean()[source]

Get think time mean.

getThinkTimeSCV()[source]

Get think time SCV.

getParent()[source]

Get parent processor.

getPrecedences()[source]

Get activity precedences.

getSetupTimeMean()[source]

Get setup time mean.

getDelayOffTimeMean()[source]

Get delay-off time mean.

setThinkTime(think_time)[source]

Set think time.

setFanIn(source, value)[source]

Set fan-in from a source task (for replication load distribution).

getFanIn()[source]

Get fan-in mapping {source_task_name: value}.

setFanOut(dest, value)[source]

Set fan-out to a destination task (for replication load distribution).

getFanOut()[source]

Get fan-out mapping {dest_task_name: value}.

get_multiplicity()

Get multiplicity.

get_replication()

Get replication level.

set_replication(replication)[source]

Set replication level.

get_scheduling()

Get scheduling strategy.

get_think_time_mean()

Get think time mean.

get_think_time_scv()

Get think time SCV.

get_parent()

Get parent processor.

get_precedences()

Get activity precedences.

addPrecedence(precedence)

Add activity precedence constraint(s) to this task.

Precedence constraints define the execution order of activities within the task, supporting serial, parallel, and conditional patterns.

Parameters:

precedence (ActivityPrecedence | List[ActivityPrecedence]) – An ActivityPrecedence object or list of ActivityPrecedence objects, created using serial(), and_fork(), and_join(), or_fork(), or_join(), or loop()

Returns:

self (for method chaining)

Return type:

Task

Example

>>> task.add_precedence(ActivityPrecedence.serial([a1, a2, a3]))
>>> task.add_precedence([
...     ActivityPrecedence.serial([a1, a2]),
...     ActivityPrecedence.and_fork(a1, [a3, a4])
... ])
get_setup_time_mean()

Get setup time mean.

get_delay_off_time_mean()

Get delay-off time mean.

set_fan_in(source, value)[source]

Set fan-in from a source task (for replication load distribution).

get_fan_in()

Get fan-in mapping {source_task_name: value}.

set_fan_out(dest, value)[source]

Set fan-out to a destination task (for replication load distribution).

get_fan_out()

Get fan-out mapping {dest_task_name: value}.

has_setup_delayoff()[source]

Return False for regular Task. SetupTask overrides this.

class Entry(model_or_name, name=None)[source]

Bases: object

Entry in a layered queueing network.

An entry is a service interface provided by a task. Entries are called by other tasks and define the work performed through their bound activities.

Supports two calling conventions: 1. Entry(name) 2. Entry(model, name)

Initialize an Entry with flexible arguments.

__init__(model_or_name, name=None)[source]

Initialize an Entry with flexible arguments.

property obj

Return self for compatibility with wrapper code that accesses .obj

on(task)[source]

Assign this entry to a task.

getBoundToActivity()[source]

Get the bound activity.

getReplyActivity()[source]

Get the reply activity (same as bound activity for simple entries).

getParent()[source]

Get the parent task.

getForwardingDests()[source]

Get forwarding destinations.

getForwardingProbs()[source]

Get forwarding probabilities.

getArrival()[source]

Get arrival distribution for open arrivals.

addForwarding(target_entry, prob=1.0)[source]

Add forwarding to another entry.

Parameters:
  • target_entry (Entry) – Entry to forward requests to

  • prob (float) – Forwarding probability (default 1.0)

Returns:

self for method chaining

Return type:

Entry

get_bound_to_activity()

Get the bound activity.

get_reply_activity()

Get the reply activity (same as bound activity for simple entries).

get_parent()

Get the parent task.

get_forwarding_dests()

Get forwarding destinations.

get_forwarding_probs()

Get forwarding probabilities.

get_arrival()

Get arrival distribution for open arrivals.

set_arrival(arrival)[source]

Set arrival distribution for open arrivals.

add_forwarding(target_entry, prob=1.0)[source]

Add forwarding to another entry.

Parameters:
  • target_entry (Entry) – Entry to forward requests to

  • prob (float) – Forwarding probability (default 1.0)

Returns:

self for method chaining

Return type:

Entry

forward(target_entry, prob=1.0)

Add forwarding to another entry.

Parameters:
  • target_entry (Entry) – Entry to forward requests to

  • prob (float) – Forwarding probability (default 1.0)

Returns:

self for method chaining

Return type:

Entry

class Activity(model_or_name, name_or_demand=None, demand=None)[source]

Bases: object

Activity in a layered queueing network.

An activity represents a unit of work performed by a task. Activities have service time distributions and can make calls to other entries.

Supports two calling conventions: 1. Activity(name, host_demand) 2. Activity(model, name, host_demand)

Initialize an Activity with flexible arguments.

__init__(model_or_name, name_or_demand=None, demand=None)[source]

Initialize an Activity with flexible arguments.

property obj

Return self for compatibility with wrapper code that accesses .obj

on(task)[source]

Assign this activity to a task.

bound_to(entry)[source]

Bind this activity to an entry (first activity of entry).

synch_call(entry, mean_calls=1.0)[source]

Add a synchronous call to another entry.

asynch_call(entry, mean_calls=1.0)[source]

Add an asynchronous call to another entry.

synch_call_rrobin(entries, mean_calls=1.0)[source]

Dispatch synchronous calls round-robin over a set of target entries.

MEAN_CALLS is the total mean number of calls the activity issues per invocation; successive calls go to the targets in cyclic order, so each target receives MEAN_CALLS/len(ENTRIES) of them. The probabilistic model with the same per-target means is the ungrouped equivalent: what the group adds is the deterministic interleaving, not a different call rate.

Only the squashed (‘flat’) layering can represent this, because under ‘srvn’ the targets never share a submodel. See SolverLN._assert_call_groups.

synch_call_jsq(entries, mean_calls=1.0)[source]

Dispatch synchronous calls to the least loaded of a set of target entries.

Same contract as SYNCH_CALL_RROBIN, with the cyclic pointer replaced by join-the-shortest-queue: each call goes to the target task whose station holds the fewest jobs at dispatch time, ties split uniformly. The probabilistic twin with MEAN_CALLS/len(ENTRIES) per target is again the ungrouped equivalent.

Only the squashed (‘flat’) layering can represent this, and only a layer solver with state-dependent routing can honour it; see SolverLN._assert_call_groups.

record_call_group(strategy, entries)[source]

Record the grouping of synchronous calls this activity ALREADY declares.

_ADD_CALL_GROUP issues the member calls and then records them; the .lqnx reader has read them back as ordinary synch-call elements, so it records the grouping alone and must not issue them a second time.

replies_to(entry)[source]

Mark this activity as replying to an entry.

setPhase(phase_num)[source]

Set the phase number for this activity (1, 2 or 3).

Phase 1 is the default; phases 2 and 3 mark post-reply activities whose demand is incurred after the entry has replied. The range is 1..3 because lqn-core.xsd bounds the phase attribute there, and every consumer of lqn.actphase tests phase > 1, so 3 is served as 2 is.

set_phase(phase_num)[source]

Set the phase number (snake_case alias for setPhase).

getPhase()[source]

Get the phase number (1..3).

get_phase()[source]

Get the phase number (snake_case alias for getPhase).

setThinkTime(think_time)[source]

Set an activity-level think time, separate from the host demand and from the task-level think time. Accepts a numeric mean (converted to an exponential) or any Distribution.

set_think_time(think_time)[source]

snake_case alias for setThinkTime.

setHostDemand(value)[source]

Set the mean host demand. A numeric mean is converted to an exponential distribution (SCV=1); a Distribution is stored verbatim. Used by the LQN parameter identification routines (see infer_lqn).

set_host_demand(value)[source]

snake_case alias for setHostDemand.

getHostDemand()[source]

Get host demand distribution.

getHostDemandMean()[source]

Get host demand mean (handles native distributions and the internal Distribution dataclass).

getHostDemandSCV()[source]

Get host demand SCV (handles native distributions and the internal Distribution dataclass).

getCallOrder()[source]

Get call order.

getBoundToEntry()[source]

Get bound entry.

getParent()[source]

Get parent task.

getSyncCallDests()[source]

Get synchronous call destinations.

getSyncCallMeans()[source]

Get synchronous call mean counts.

getAsyncCallDests()[source]

Get asynchronous call destinations.

getAsyncCallMeans()[source]

Get asynchronous call mean counts.

getThinkTimeMean()[source]

Get think time mean.

get_host_demand()

Get host demand distribution.

get_host_demand_mean()

Get host demand mean (handles native distributions and the internal Distribution dataclass).

get_host_demand_scv()

Get host demand SCV (handles native distributions and the internal Distribution dataclass).

get_call_order()

Get call order.

get_bound_to_entry()

Get bound entry.

get_parent()

Get parent task.

get_sync_call_dests()

Get synchronous call destinations.

get_sync_call_means()

Get synchronous call mean counts.

get_async_call_dests()

Get asynchronous call destinations.

get_async_call_means()

Get asynchronous call mean counts.

get_think_time_mean()

Get think time mean.

boundTo(entry)

Bind this activity to an entry (first activity of entry).

synchCall(entry, mean_calls=1.0)

Add a synchronous call to another entry.

asynchCall(entry, mean_calls=1.0)

Add an asynchronous call to another entry.

synchCallRRobin(entries, mean_calls=1.0)

Dispatch synchronous calls round-robin over a set of target entries.

MEAN_CALLS is the total mean number of calls the activity issues per invocation; successive calls go to the targets in cyclic order, so each target receives MEAN_CALLS/len(ENTRIES) of them. The probabilistic model with the same per-target means is the ungrouped equivalent: what the group adds is the deterministic interleaving, not a different call rate.

Only the squashed (‘flat’) layering can represent this, because under ‘srvn’ the targets never share a submodel. See SolverLN._assert_call_groups.

synchCallJSQ(entries, mean_calls=1.0)

Dispatch synchronous calls to the least loaded of a set of target entries.

Same contract as SYNCH_CALL_RROBIN, with the cyclic pointer replaced by join-the-shortest-queue: each call goes to the target task whose station holds the fewest jobs at dispatch time, ties split uniformly. The probabilistic twin with MEAN_CALLS/len(ENTRIES) per target is again the ungrouped equivalent.

Only the squashed (‘flat’) layering can represent this, and only a layer solver with state-dependent routing can honour it; see SolverLN._assert_call_groups.

repliesTo(entry)

Mark this activity as replying to an entry.

recordCallGroup(strategy, entries)

Record the grouping of synchronous calls this activity ALREADY declares.

_ADD_CALL_GROUP issues the member calls and then records them; the .lqnx reader has read them back as ordinary synch-call elements, so it records the grouping alone and must not issue them a second time.

class ActivityPrecedence(prec_type, activities=<factory>, pre_activities=<factory>, post_activities=<factory>, probabilities=<factory>, count=1.0, pre_params=None)[source]

Bases: object

Activity precedence constraint for layered queueing networks.

Precedence constraints define the execution order of activities within a task, supporting serial, parallel, and conditional execution patterns.

prec_type: PrecedenceType
activities: List[Activity]
pre_activities: List[Activity]
post_activities: List[Activity]
probabilities: List[float]
count: float = 1.0
pre_params: Any | None = None
static Serial(*args)[source]

Create a serial (sequential) precedence for a list of activities.

Activities execute one after another in the given order.

Supports two calling conventions:
  • Serial(a1, a2, a3): Multiple activity arguments (MATLAB-style)

  • Serial([a1, a2, a3]): Single list argument

Parameters:

*args – Either multiple Activity objects or a single list of activities

Returns:

ActivityPrecedence object representing serial composition

Return type:

ActivityPrecedence

Example

>>> task.add_precedence(ActivityPrecedence.Serial(a1, a2, a3))
>>> task.add_precedence(ActivityPrecedence.Serial([a1, a2, a3]))
static serial(*args)[source]

Python snake_case alias for Serial().

static AndFork(pre_act, post_acts)[source]

Create an AND-fork precedence (parallel split).

All post-activities start executing when pre_act completes. Used together with AndJoin() to model parallel execution.

Parameters:
  • pre_act (Activity) – Activity that triggers the fork

  • post_acts (List[Activity]) – List of Activity objects to execute in parallel

Returns:

ActivityPrecedence object

Return type:

ActivityPrecedence

Example

>>> task.add_precedence(ActivityPrecedence.AndFork(start, [branch1, branch2]))
static and_fork(pre_act, post_acts)[source]

Python snake_case alias for AndFork().

static AndJoin(pre_acts, post_act, quorum=None)[source]

Create an AND-join precedence (synchronization).

Post-activity starts once quorum of the pre-activities have completed. With no quorum the join waits for ALL of them, which is the LQN default. Used together with AndFork() to model parallel execution.

Parameters:
  • pre_acts (List[Activity]) – List of Activity objects to synchronize on

  • post_act (Activity) – Activity that executes after synchronization

  • quorum (int | None) – Number k of pre-activities required to fire the join. Defaults to None, meaning all of them. Values outside [1, len(pre_acts)] are ignored.

Returns:

ActivityPrecedence object

Return type:

ActivityPrecedence

Example

>>> task.add_precedence(ActivityPrecedence.AndJoin([branch1, branch2], end))
>>> task.add_precedence(ActivityPrecedence.AndJoin([b1, b2, b3], end, quorum=2))
static and_join(pre_acts, post_act, quorum=None)[source]

Python snake_case alias for AndJoin().

static OrFork(pre_act, post_acts, probs)[source]

Create an OR-fork precedence (probabilistic branching).

Exactly one post-activity is selected based on probabilities when pre_act completes.

Parameters:
  • pre_act (Activity) – Activity that triggers the fork

  • post_acts (List[Activity]) – List of Activity objects as branch options

  • probs (List[float]) – List of probabilities for each branch (must sum to 1.0)

Returns:

ActivityPrecedence object

Return type:

ActivityPrecedence

Example

>>> task.add_precedence(ActivityPrecedence.OrFork(start, [fast, slow], [0.7, 0.3]))
static or_fork(pre_act, post_acts, probs)[source]

Python snake_case alias for OrFork().

static OrJoin(pre_acts, post_act)[source]

Create an OR-join precedence (merge).

Post-activity starts when ANY of the pre-activities complete. Used together with OrFork() to model probabilistic branching.

Parameters:
  • pre_acts (List[Activity]) – List of Activity objects to merge

  • post_act (Activity) – Activity that executes after merge

Returns:

ActivityPrecedence object

Return type:

ActivityPrecedence

Example

>>> task.add_precedence(ActivityPrecedence.OrJoin([fast, slow], end))
static or_join(pre_acts, post_act)[source]

Python snake_case alias for OrJoin().

static Loop(pre_act, loop_acts, count)[source]

Create a loop precedence for repeated execution.

Loop activities execute a specified number of times before continuing.

Parameters:
  • pre_act (Activity) – Activity that triggers the loop

  • loop_acts (List[Activity]) – List of Activity objects in the loop body

  • count (float) – Number of loop iterations (can be fractional for geometric mean)

Returns:

ActivityPrecedence object

Return type:

ActivityPrecedence

Example

>>> task.add_precedence(ActivityPrecedence.Loop(init, [compute], 5))
static loop(pre_act, loop_acts, count)[source]

Python snake_case alias for Loop().

static CacheAccess(access_act, outcome_acts)[source]

Create a cache access precedence pattern.

Models cache hit/miss behavior where access_act performs the cache lookup and outcome_acts contains [hit_activity, miss_activity].

Parameters:
  • access_act (Activity) – Activity that performs cache access

  • outcome_acts (List[Activity]) – List of [hit_activity, miss_activity]

Returns:

ActivityPrecedence object

Return type:

ActivityPrecedence

Example

>>> task.add_precedence(ActivityPrecedence.CacheAccess(lookup, [hit, miss]))
static cache_access(access_act, outcome_acts)[source]

Python snake_case alias for CacheAccess().

Cache Tasks

class CacheTask(model, name, total_items, cache_capacity, replacement_strategy, multiplicity=1)[source]

Bases: Task

Cache task in a layered queueing network.

A CacheTask models a caching service that stores items in a limited capacity cache. It tracks cache hits and misses based on a replacement strategy.

Create a cache task.

Parameters:
  • model – Parent LayeredNetwork

  • name (str) – Name of the cache task

  • total_items (int) – Total number of distinct items that can be requested

  • cache_capacity (int) – Maximum number of items the cache can hold

  • replacement_strategy (ReplacementStrategy) – Cache replacement policy (FIFO, LRU, RR, etc.)

  • multiplicity (float) – Number of task instances

__init__(model, name, total_items, cache_capacity, replacement_strategy, multiplicity=1)[source]

Create a cache task.

Parameters:
  • model – Parent LayeredNetwork

  • name (str) – Name of the cache task

  • total_items (int) – Total number of distinct items that can be requested

  • cache_capacity (int) – Maximum number of items the cache can hold

  • replacement_strategy (ReplacementStrategy) – Cache replacement policy (FIFO, LRU, RR, etc.)

  • multiplicity (float) – Number of task instances

on(processor)[source]

Deploy this cache task on a processor.

set_retrieval(retrieval=True)[source]

Enable/disable a delayed-hit retrieval system on the cache miss path.

When set, concurrent misses for the same item arriving while a fetch (the miss-branch activity and its backend calls) is in flight are parked and released together as delayed hits when the fetch completes, instead of each triggering an independent fetch. Mirrors MATLAB CacheTask.setRetrieval.

has_retrieval()[source]
class ItemEntry(model, name, total_items, access_prob)[source]

Bases: Entry

Item entry for a cache task.

An ItemEntry represents the interface to request items from a cache. It specifies the total number of items and their access probabilities.

Create an item entry.

Parameters:
  • model – Parent LayeredNetwork

  • name (str) – Name of the entry

  • total_items (int) – Total number of distinct items

  • access_prob – Access probability distribution (DiscreteSampler or list)

__init__(model, name, total_items, access_prob)[source]

Create an item entry.

Parameters:
  • model – Parent LayeredNetwork

  • name (str) – Name of the entry

  • total_items (int) – Total number of distinct items

  • access_prob – Access probability distribution (DiscreteSampler or list)

on(task)[source]

Assign this item entry to a cache task.