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,ElementQueueing 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
- 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:
- 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:
- 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:
- 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:
- add_link(source, dest)[source]
Add a link between two nodes.
- Parameters:
- Raises:
ValueError – If nodes not in network
- add_links(routing_matrix)[source]
Add multiple links via routing matrix.
- Parameters:
routing_matrix (RoutingMatrix | ndarray) – RoutingMatrix or numpy array with probabilities
- add_link_list(*nodes)[source]
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:
- 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)
- init_routing_matrix()[source]
Initialize an empty routing matrix for all nodes and classes.
- Returns:
RoutingMatrix with zeros (no routing initially)
- Return type:
- 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.
- link(routing_matrix)[source]
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 onis_routing_ergodic, which is the one adjacency build and SCC decomposition; this addsisRoutingErgodicandsuggestedFixes.- 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 totarget_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:
- 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:
- setLogPath(path)
Set the path for logger output files.
- Parameters:
path (str) – Directory path for log files
- link_and_log(P, is_node_logged, log_path=None)[source]
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.
- 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.
- get_connection_matrix()[source]
Get the network connection (adjacency) matrix.
- Returns:
(N x N) binary matrix where 1 indicates a connection
- Return type:
- 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 classrcan switch into classsat 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_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
- 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.
- 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.
- 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.
- 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)
- 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:
- 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
- relink(routing_matrix)[source]
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
- 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.
- 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:
- 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:
- 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 notinitFromMarginalAndStarted: ‘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 notinitFromMarginalAndStarted: ‘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 NOTinitFromMarginalAndStartedwith 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:
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.
- 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 classrcan switch into classsat 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.
- 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:
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:
Example
>>> model = Network('MyModel') >>> # ... build model ... >>> model.jsimgView() # Opens in JMT graphical editor
References
- 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:
Example
>>> model = Network('MyModel') >>> # ... build model ... >>> model.jsimwView() # Opens in JMT wizard
References
- 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:
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:
References
- 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:
References
- static tandem(lambda_rates, D, strategies)[source]
Create a tandem network with specified scheduling strategies.
Creates an open queueing network in tandem configuration.
- Parameters:
- Returns:
Configured tandem queueing network
- Return type:
References
- static cluster_ps(lambda_rates, D, S=None, dispatching=None)[source]
Open PS cluster (single-server queues unless S overrides multiplicity).
- 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:
- Returns:
Configured tandem queueing network
- Return type:
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
- 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:
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:
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:
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:
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:
- 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:
objectStatic 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:
NetworkElementAbstract 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.
- 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
- 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.
- link(node_to)[source]
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_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.
- 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.
- class Source(model, name)[source]
Bases:
StationSource 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 isline_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.
mmapis 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.
- 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.
mmapis 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.
- 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.
- 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 isline_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:
StationQueue 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.)
- 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)wheremuis a callable taking the ordered state vectorc(a list/array of class indices,c[0]the oldest job) and returning the scalar total service ratemu(c). Per-class service distributions are not used by an order-independent/PAS queue.
- 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_classincache’s retrieval system. The default (when not overridden) is the read class’s own service distribution at this queue.itemis 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_classincache’s retrieval system. The default (when not overridden) is the read class’s own service distribution at this queue.itemis 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.)
- 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.
- 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.
- 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.
- 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:
- 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
- set_hetero_sched_policy(policy)[source]
Set the heterogeneous scheduling policy.
- Parameters:
policy – HeteroSchedPolicy value
- 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.
- get_total_num_of_servers()[source]
Get total number of servers across all server types.
- Returns:
Total server count
- Return type:
- isHeterogeneous()
Check if this queue has heterogeneous servers.
- Returns:
True if server types have been defined
- Return type:
- 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:
- 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)
- 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
- getSetupTime(jobclass)
Get setup time distribution for a job class.
- getDelayOffTime(jobclass)
Get delay-off time distribution for a job class.
- 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)
- 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.
- 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, []).
- 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).
- 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.
- 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)
- 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.
- 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:
QueueDelay 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:
NodeSink 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
- class Router(model, name)[source]
Bases:
NodeRouter 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
- class Fork(model, name)[source]
Bases:
NodeFork 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
- setStatePrior(prior)
Set Fork state prior (FJ tag-augmented copies only).
- set_tasks_per_link(ntasks, jobclass=None, dest_node=None)[source]
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
- set_tasks_per_link_distribution(jobclass, dist, dest_node=None)[source]
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.
- 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).
- getTasksPerLink()
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.
- class Join(model, name, fork=None)[source]
Bases:
StationJoin 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:
- set_strategy(jobclass, strategy)[source]
Set join strategy for a job class.
- Parameters:
jobclass (JobClass) – Job class
strategy (JoinStrategy) – Join strategy (STD, QUORUM, etc.)
- class Cache(model, name, num_items, item_level_cap, replacement_strategy=ReplacementStrategy.LRU)[source]
Bases:
StationMulti-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:
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 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.
- 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.
- 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.
- 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.
- 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.
- set_result_list_cost(list_cost)[source]
Store the mean storage cost held by each list, as computed by a solver.
- 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).
- 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).
- set_result_item_prob(item_prob)[source]
Set the per-item occupancy [items x (lists+1)]; col 0 = miss, cols 1.. = per-list.
- 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.
- get_capacity_vector()[source]
Get the cache capacity vector.
- Returns:
Array of cache level capacities
- Return type:
- 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.
- set_retrieval_class(input_class, output_class, item)[source]
Set the retrieval class for (item, input_class). item is 0-based.
- 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 withqueue.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).
- set_item_routing_probability(jobin_class, item, source, dest, probability)[source]
Probability of routing the retrieval class for
itembetween two nodes of the retrieval system.source/destare either a retrieval queue or the cache itself: pass the cache assourcefor a cache->queue entry, or asdestfor a queue->cache exit.itemis 0-based. Requires a priorset_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 withqueue.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).
- 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
itembetween two nodes of the retrieval system.source/destare either a retrieval queue or the cache itself: pass the cache assourcefor a cache->queue entry, or asdestfor a queue->cache exit.itemis 0-based. Requires a priorset_retrieval_system()call.
- set_item_routing_prob(jobin_class, item, source, dest, probability)
Probability of routing the retrieval class for
itembetween two nodes of the retrieval system.source/destare either a retrieval queue or the cache itself: pass the cache assourcefor a cache->queue entry, or asdestfor a queue->cache exit.itemis 0-based. Requires a priorset_retrieval_system()call.
- setItemRoutingProb(jobin_class, item, source, dest, probability)
Probability of routing the retrieval class for
itembetween two nodes of the retrieval system.source/destare either a retrieval queue or the cache itself: pass the cache assourcefor a cache->queue entry, or asdestfor a queue->cache exit.itemis 0-based. Requires a priorset_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
- class Logger(model, name, log_file_name)[source]
Bases:
NodeLogger 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:
- set_prob_routing(jobclass, destination, probability)[source]
Set probabilistic routing to a destination.
- getStartTime()
- getLoggerName()
- getTimestamp()
- getJobID()
- getJobClass()
- getTimeSameClass()
- getTimeAnyClass()
- setLoggerName(value)
- property fileName
- property filePath
- class ClassSwitch(model, name, cs_matrix=None)[source]
Bases:
NodeClassSwitch 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:
StationPlace 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).
- 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_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:
StatefulNodeTransition 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
- set_enabling_conditions(mode, jobclass, input_node, enabling_condition)[source]
Set enabling conditions for a mode.
- 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.
- set_firing_outcome(mode, jobclass, output_node, firing_outcome)[source]
Set firing outcome for a mode.
- 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.)
- 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:
NetworkElementAbstract 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.
- 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).
- 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).
- 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
- set_patience(*args)[source]
Set the class-level patience (impatience) distribution.
Mirrors MATLAB
JobClass.setPatienceand JavaJobClass.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.
- 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).
- set_number_of_jobs(njobs)
Set the class population; only meaningful for closed classes.
- setPatience(*args)[source]
Alias for set_patience (CamelCase).
Accepts
setPatience(distribution)orsetPatience(impatience_type, distribution). It previously forwarded a single argument into the two-argument-only set_patience, so every call raised TypeError.
- 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:
JobClassOpen 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:
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
- 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:
JobClassClosed 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:
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
- set_number_of_jobs(njobs)
Set 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:
ClosedClassSelf-looping closed class.
Jobs in this class perpetually loop at their reference station, useful for modeling background workloads or special scheduling.
- Parameters:
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:
objectMatrix 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:
- __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.
- 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.
- 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).
- 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.
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:
objectState 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.
- 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:
- Returns:
State space matrix with the requested total.
- Return type:
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:
- Returns:
State space matrix with the requested totals.
- Return type:
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
lbandub(inclusive), subject to the node capacitycap.- 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).
Noneis 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:
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.
- 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).
- 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.
- 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:
- Returns:
State space matrix with the requested total.
- Return type:
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:
- Returns:
State space matrix with the requested totals.
- Return type:
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:
- 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
lbandub(inclusive), subject to the node capacitycap.- 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).
Noneis 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:
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:
- Returns:
True if the state satisfies all capacity, server, disabled-process and chain-population constraints; False otherwise.
- Return type:
References
MATLAB: matlab/src/lang/+State/isValid.m
- class Mode(transition, name, index)[source]
Bases:
objectA 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
Solvers (line_solver.solvers)
The solvers module provides various analytical and simulation-based solvers.
Base Solver Classes
- class Solver(options=None, *args, **kwargs)
Bases:
objectBase 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:
SolverBase 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_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:
- 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:
- 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.orderselects which moment orders to report. It is a SET: a scalarkis read as1: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 = 3also 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: scalingL(i,r)alone is Theorem 1 of Akyildiz and Strelen with the class subsetT = {r}, and it generates the moments ofn(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.methodselects 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.methodis 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
momis 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, andmom['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.orderselects which moment orders to report. It is a SET: a scalarkis read as1: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 = 3also 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: scalingL(i,r)alone is Theorem 1 of Akyildiz and Strelen with the class subsetT = {r}, and it generates the moments ofn(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.methodselects 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.methodis 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
momis 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, andmom['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.orderselects which moment orders to report. It is a SET: a scalarkis read as1: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.
momis the underlying pfqn_sens_mom / pfqn_sens_linearizer object, which also carries the cross-station covariance matrix.Covthat 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.orderselects which moment orders to report. It is a SET: a scalarkis read as1: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.
momis the underlying pfqn_sens_mom / pfqn_sens_linearizer object, which also carries the cross-station covariance matrix.Covthat 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
snis 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_snon the caller’s model would corrupt every later solve off it;_has_struct = True(MATLAB’shasStruct) soget_struct()does not regenerate the struct and discard the perturbation;_rates_dirty = Falsebecause a solver’s_extract_network_paramscallsmodel.refresh_rates()when the flag is set, which would recomputeratesfrom 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
snis 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_snon the caller’s model would corrupt every later solve off it;_has_struct = True(MATLAB’shasStruct) soget_struct()does not regenerate the struct and discard the perturbation;_rates_dirty = Falsebecause a solver’s_extract_network_paramscallsmodel.refresh_rates()when the flag is set, which would recomputeratesfrom 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.
orderselects which moment orders to report. It is a SET: a scalarkis read as1: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
Tat a station, and the moments it produces are those ofsum_(r in T) n(i,r).T = {r}gives getMomentTable,T = chaingives this table,T = all classesgives getMomentStationTable. That is Theorem 1 of Akyildiz and Strelen; Strelen’s ownx_iis 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.
momis the underlying pfqn_sens_mom struct. Its.Covis (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.
orderselects which moment orders to report. It is a SET: a scalarkis read as1: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
Tat a station, and the moments it produces are those ofsum_(r in T) n(i,r).T = {r}gives getMomentTable,T = chaingives this table,T = all classesgives getMomentStationTable. That is Theorem 1 of Akyildiz and Strelen; Strelen’s ownx_iis 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.
momis the underlying pfqn_sens_mom struct. Its.Covis (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.
- 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).
- 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.
- 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 insupportsModelMethod(), which runs later and is allowed to.The case this exists for is a method that MOVED. Dropping the name from
listValidMethodsis 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_methodcallsrcat_moved_to_agBEFORE 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
- 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.
- 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.
- 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).
- 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).
- 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).
- 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:
SolverBase 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,NetworkSolverNative 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.
- 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:
- 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.
- 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).
- 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.
- 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
- 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).
- get_avg_item_table()
Item-level cache occupancy table (see cache_table).
- avg_item_table()
Item-level cache occupancy table (see cache_table).
- 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:
- 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:
- 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:
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:
- Returns:
NotImplementedError (sampling not supported)
- Raises:
NotImplementedError – Always - MVA does not support sampling
- Return type:
- 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)
- 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.
- 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:
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:
- Returns:
NotImplementedError (sampling not supported)
- Raises:
NotImplementedError – Always - MVA does not support sampling
- Return type:
- 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:
- 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:
- 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:
- 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.
- 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:
- 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:
- 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,NetworkSolverNative 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
- is_chain_solver()
True when the solver was built from a MarkovProcess or a MarkovChain.
- is_discrete_chain()
True in chain mode when the user supplied a DTMC (MarkovChain).
- get_trans_mat()
Transition matrix of the user-supplied DTMC (chain mode only).
- 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).
- 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
- 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]
- 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:
- 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.
- getPerctRespT(percentiles=None, jobclass=None)[source]
Extract percentiles from response time distribution.
- 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:
- 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:
- 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:
- 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:
- 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:
- getAsymptoticVariance(f)[source]
The asymptotic variance of the time-average of a reward
falong 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, andsim_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,asymptoticVarianceand 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:
- 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.
- 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.
- 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.
- 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:
nameidentifier used in reports;valuenominal value theta;setcallable (model, value) -> None applying theta;stepoptional 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:
nameidentifier used in reports;valuenominal value theta;setcallable (model, value) -> None applying theta;stepoptional 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.
- runRewardAnalyzer(reward_vector=None)[source]
Run reward analysis on the CTMC.
Computes expected rewards in steady-state and optionally transient.
- getTranReward(t=1.0, reward_vector=None)[source]
Get transient reward at time t.
Computes expected reward at time t using matrix exponential.
- 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:
- 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:
- 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:
- 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.
- GetReward(reward_vector=None)
Compute reward function over steady-state distribution.
- 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.
- GetTranProbAggr(node, t=1.0)
Get transient aggregated state probabilities at a node.
- 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.
- GetTranProbSysAggr(t=1.0)
Get transient aggregated system state probabilities.
- 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.
- RunRewardAnalyzer(reward_vector=None)
Run reward analysis on the CTMC.
Computes expected rewards in steady-state and optionally transient.
- GetTranReward(t=1.0, reward_vector=None)
Get transient reward at time t.
Computes expected reward at time t using matrix exponential.
- 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.
- 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
- 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).
- get_avg_item_table()
Item-level cache occupancy table (see cache_table).
- avg_item_table()
Item-level cache occupancy table (see cache_table).
- 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.
- 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:
- 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
runAnalyzerCheckscall it;runAnalyzerreads 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
runAnalyzerCheckscall it;runAnalyzerreads 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).
- 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
modelfits 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 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
- 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
- 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.
- 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:
- 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.
- GetPerctRespT(percentiles=None, jobclass=None)
Extract percentiles from response time distribution.
- 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.
- 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.
- 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.
- 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:
- 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.
- 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.
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- tran_prob(node, t=1.0)
Get transient state probabilities at a node.
Computes π(t) = π(0) * exp(Q*t) using matrix exponential.
- tran_prob_aggr(node, t=1.0)
Get transient aggregated state probabilities at a node.
- 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.
- class SolverFLD(network, method_or_options='default', options=None, **kwargs)[source]
Bases:
ForkJoinDriverMixin,NetworkSolverNative 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 behaviorresult (
FLDResultorNone) – 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 (
NetworkStructorobject) –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 (
NetworkStructorobject) –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) + lambdafor 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:
- 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) + lambdafor 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- Returns:
When stationandjob_class are both None– List of lists where RD[station][class] is a 2D array with columns [cdf, time]When stationandjob_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:
- Returns:
CDF value F(t) = P(response_time ≤ t) at specified time
- Return type:
- 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:
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 againstmethod (
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:
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.
- 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
methodrun 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:
- 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:
- 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:
- 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:
- getAvgArvR()[source]
Get average arrival rates per station and class.
- Returns:
(M, K) array of arrival rates
- Return type:
- getAvgTput()[source]
Get average throughputs per station and class.
- Returns:
(M, K) array of throughputs
- Return type:
- 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.
- getProbMarg(station, jobclass)[source]
Get marginal queue-length distribution at station for class.
- getProbSys()[source]
Get system state probabilities.
- Returns:
System state probability vector
- Return type:
- getProbSysAggr()[source]
Get aggregated system state probabilities.
- Returns:
System state probability vector (aggregated over classes)
- Return type:
- 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.
- 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.
- 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.
- 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.
- 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 stationandjob_class are both None– List of lists where RD[station][class] is a 2D array with columns [cdf, time]When stationandjob_class are specified– dict with keys ‘t’, ‘cdf’, ‘mean’, ‘var’, ‘method’
See also
getCdfRespTsteady-state response time distribution (same quantity)
getTranCdfPassTpassage 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
- 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).
- 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:
- 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:
- Returns:
When stationandjob_class are both None– List of lists where RD[station][class] is a 2D array with columns [cdf, time]When stationandjob_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:
- Returns:
When stationandjob_class are both None– List of lists where RD[station][class] is a 2D array with columns [cdf, time]When stationandjob_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:
- Returns:
When stationandjob_class are both None– List of lists where RD[station][class] is a 2D array with columns [cdf, time]When stationandjob_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:
- 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:
- 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:
- 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:
- 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}")
- class SolverMAM(network, method='default', options=None, **kwargs)[source]
Bases:
AvgResultsMixin,NetworkSolverNative 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
- runAnalyzer()[source]
Run the analyzer with selected method.
- Returns:
self (for method chaining)
- Return type:
- 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
runAnalyzerCheckscan call it before the struct is built;supportsModelMethodandrunAnalyzerreturn 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
runAnalyzerCheckscan call it before the struct is built;supportsModelMethodandrunAnalyzerreturn 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:
- getPerctRespT(percentiles=None, jobclass=None)[source]
Extract percentiles from response time distribution.
- 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.
- 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.
- 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).
- 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
- 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).
- 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:
- GetPerctRespT(percentiles=None, jobclass=None)
Extract percentiles from response time distribution.
- 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.
- 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:
- 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:
- perct_resp_t(percentiles=None, jobclass=None)
Extract percentiles from response time distribution.
- static list_valid_methods()
List all valid solution methods.
- 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,NetworkSolverNative 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.
- getAvgTable()[source]
Get comprehensive average performance metrics table.
- Returns:
Station, JobClass, QLen, Util, RespT, ResidT, ArvR, Tput
- Return type:
pandas.DataFrame with columns
- 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]
- 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:
- getLogNormalizingConstant()[source]
Get the log normalizing constant log(G).
- Returns:
log(G)
- Return type:
- getProbNormConstAggr()[source]
Get the log normalizing constant (alias).
- Returns:
log(G)
- Return type:
- 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
- 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:
- getPerctRespT(percentiles=None, jobclass=None, method='default')[source]
Extract percentiles from response time distribution.
- 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.
- 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.
- 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:
- 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:
- 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:
- 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:
- 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)
- 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).
- 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
- 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).
- get_avg_item_table()
Item-level cache occupancy table (see cache_table).
- avg_item_table()
Item-level cache occupancy table (see cache_table).
- getTranAvg()[source]
Get transient average metrics (not supported for NC).
NC is a steady-state solver. Returns steady-state values.
- 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.mand JavaSolverNC.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) andmethod='mem'.- Return type:
- Reference:
D.D. Kouvatsos, “Entropy Maximisation and Queueing Network Models”, Annals of Operations Research, 48:63-126, 1994.
- 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.
- 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:
- 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:
- 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:
- 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:
- 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).
- 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:
- GetLogNormalizingConstant()
Get the log normalizing constant log(G).
- Returns:
log(G)
- Return type:
- GetProbNormConstAggr()
Get the log normalizing constant (alias).
- Returns:
log(G)
- Return type:
- 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:
- GetPerctRespT(percentiles=None, jobclass=None, method='default')
Extract percentiles from response time distribution.
- MeOpen(options=None)
Maximum Entropy Method (MEM) for open queueing networks.
Implements the Kouvatsos (1994) entropy-maximisation algorithm (mirrors MATLAB
@SolverNC/me_open.mand JavaSolverNC.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) andmethod='mem'.- Return type:
- 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.
- 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:
- get_log_normalizing_constant()
Get the log normalizing constant log(G).
- Returns:
log(G)
- Return type:
- 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.
- 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:
- 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:
- 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:
- 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:
- class SolverSSA(model, method_or_options=None, **kwargs)[source]
Bases:
FJTagTransformMixin,NetworkSolverNative 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
- 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]
- 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.
- 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
- 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
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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.
- getAvgRespTChain()[source]
Get average response times aggregated by chain.
Uses alpha-weighted sum matching MATLAB: RN(:,c) = sum(RNclass(:,inchain).*alpha(:,inchain),2)
- 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.
- 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
- 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).
- get_avg_item_table()
Item-level cache occupancy table (see cache_table).
- avg_item_table()
Item-level cache occupancy table (see cache_table).
- getTranAvg()[source]
Get transient average metrics from simulation.
SSA provides transient metrics from the simulation trajectory.
- 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
- 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 issn_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 issn_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.afterEventmachinery 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.
- 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.afterEventmachinery 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.
- 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.
- 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.
- GetTranAvg()
Get transient average metrics from simulation.
SSA provides transient metrics from the simulation trajectory.
- 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:
NetworkSolverJMT 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:
- getAvgTable()[source]
Get average performance metrics as a DataFrame.
- Returns:
Station, Class, QLen, Util, RespT, Tput, ArvR
- Return type:
DataFrame with columns
- 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
- 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:
- 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:
- 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.
- 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 getJMVAFeatureSet()[source]
What the JMVA ANALYTICAL engine accepts, much less than JSIM.
Derived from the writer rather than guessed:
write_jmvaemits, 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_jmvaemits, 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).
- 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().
- QN2JSIMG(outputFileName=None)[source]
Convert queueing network to JSIMG format. Wrapper for writeJSIM.
- 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).
- 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:
- 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:
- 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.
- class SolverQNS(model_or_sn, options=None, **kwargs)[source]
Bases:
NetworkSolverNative 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:
- 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
- getAvgSys()[source]
Get system-level average metrics.
- Returns:
Tuple of (CN, XN) - system response times and throughputs
- getAvgChain()[source]
Get all average metrics aggregated by chain.
- Returns:
Tuple of (QN, UN, RN, WN, AN, TN) aggregated by chain
- getAvgNode()[source]
Get average metrics per node.
- Returns:
Tuple of (QNn, UNn, RNn, WNn, ANn, TNn) - node-level metrics
- 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.
- 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:
- 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:
SolverNative 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:
- 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
- getAvgTable()[source]
Get average performance metrics table.
- Returns:
pandas.DataFrame with layered network performance metrics
- Return type:
DataFrame
- 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:
- 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:
EnsembleSolverNative 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; seesolver_ln_nlp_analyzer. It is served natively and refuses a delegatedoptions.lang.options.langdelegates the whole layered solve instead:'java'tojline.jarover JSON,'cpp'to the C++line-cliover the.lqnxinterchange (steady state only, and it refuses what that interchange cannot carry – seesolvers/cpp_dispatch.py). Either way the native fixed point never runs andoptions.arithselects 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
listValidMethodsreturns 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 MATLABln_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
listValidMethodsreturns 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 MATLABln_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.
- pre(it)[source]
Operations before each iteration (matches MATLAB pre).
Seed control for stochastic layer solvers.
- 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_routing_probabilities(it)[source]
Update routing probabilities (matches MATLAB updateRoutingProbabilities).
- 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:
Burn-in: for the first stochiter_burnin iterations the plain Picard iteration runs with the relaxation factor configured at init.
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.
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).
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.
- getCdfRespT()[source]
Response time distribution of every entry of the layered network.
Mirrors MATLAB
@SolverLN/getCdfRespT.m. The distribution is formed by themoment3pass alone – the mean-based update builds no law at all – so a solver constructed with any other method re-runs the ensemble undermoment3here and restores the caller’s method afterwards. The routing layers already built servemoment3unchanged, so only the update pass changes.- Returns:
A list of
nentriesitems, 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:
- get_cdf_resp_t()
Response time distribution of every entry of the layered network.
Mirrors MATLAB
@SolverLN/getCdfRespT.m. The distribution is formed by themoment3pass alone – the mean-based update builds no law at all – so a solver constructed with any other method re-runs the ensemble undermoment3here and restores the caller’s method afterwards. The routing layers already built servemoment3unchanged, so only the update pass changes.- Returns:
A list of
nentriesitems, 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:
- 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.
- update_solver(solver_factory)[source]
Replace all per-layer solvers with ones built by solver_factory, preserving the current solution state for refinement.
- 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 onDataFrame.attrs['layer_methods'], withattrs['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 onDataFrame.attrs['layer_methods'], withattrs['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
Solver Configuration
Distributions (line_solver.distributions)
The distributions module provides probability distributions for service and arrival processes.
Base Distribution Classes
- class Distribution[source]
Bases:
ABCBase 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.
- 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.
- eval_pmf(x)[source]
Evaluate the probability mass function at point x (snake_case alias for evalPMF; dispatches to the concrete distribution’s override).
- 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.
- 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.
- 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:
DistributionBase class for continuous probability distributions.
Initialize a new distribution.
- 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:
DistributionBase class for discrete probability distributions.
Initialize a new distribution.
- class Markovian[source]
Bases:
DistributionBase 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.
- getRepresentation()[source]
Return the
(D0, D1)matrix representation used in the theory of Markovian arrival processes, as a list[D0, D1](elementkcorresponds to matrixD_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,MarkovianExponential 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.
- 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.
- 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.
- 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.
- 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:
ContinuousDistributionDeterministic (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.
- 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.
- class Erlang(phase_rate, nphases)[source]
Bases:
ContinuousDistribution,MarkovianErlang 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:
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.
- classmethod fit_mean_and_order(mean, phases)[source]
Create an Erlang distribution from mean and number of phases.
- 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.
- 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.
- classmethod fitMeanAndOrder(mean, phases)
Create an Erlang distribution from mean and number of phases.
- class Gamma(shape, scale)[source]
Bases:
ContinuousDistributionGamma distribution.
The gamma distribution is a two-parameter continuous distribution that generalizes the exponential and Erlang distributions.
Initialize a new distribution.
- 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.
- classmethod fitMeanAndSCV(mean, scv)
Create a Gamma distribution from mean and SCV.
- class HyperExp(p_or_probs, rate1_or_rates, rate2=None)[source]
Bases:
ContinuousDistribution,MarkovianHyperexponential 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 distributiondistITSELF 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 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.
- 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.
- classmethod fitMeanAndScvBalanced(mean, scv)
Create a 2-phase hyperexponential distribution with balanced means.
Uses balanced means representation where p/mu1 = (1-p)/mu2.
- classmethod fitMeanAndSCVBalanced(mean, scv)
Create a 2-phase hyperexponential distribution with balanced means.
Uses balanced means representation where p/mu1 = (1-p)/mu2.
- 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.
- 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.
- 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).
- class Lognormal(mu, sigma)[source]
Bases:
ContinuousDistributionLognormal distribution.
A random variable X has a lognormal distribution if log(X) is normally distributed.
- Parameters:
Initialize a new distribution.
- 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.
- 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:
ContinuousDistributionPareto distribution.
The Pareto distribution is a power-law distribution often used to model heavy-tailed phenomena.
- Parameters:
Initialize a new distribution.
- 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.
- 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. Substitutingx = k/umaps 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 ass -> 0, where the incomplete-gamma product underflows to 0/inf. Heres = 0givesalpha*int_0^1 u^(alpha-1) du = 1exactly, and the integrand is bounded and C^inf on a FINITE interval foralpha >= 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 returnedA*(0)=0.96914, not 1).
- classmethod fit_mean_and_scv(mean, scv)[source]
Create a Pareto distribution from mean and SCV.
- Parameters:
- Returns:
Pareto distribution with given mean and SCV.
- Return type:
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:
- Returns:
Pareto distribution with given mean and SCV.
- Return type:
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:
- Returns:
Pareto distribution with given mean and SCV.
- Return type:
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:
ContinuousDistributionUniform distribution on [min, max].
Initialize a new distribution.
- class Weibull(shape, scale)[source]
Bases:
ContinuousDistributionWeibull distribution.
The Weibull distribution is commonly used in reliability engineering to model time to failure.
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).
- classmethod fitMeanAndSCV(mean, scv)[source]
camelCase alias of fit_mean_and_scv (MATLAB/JAR spelling).
- 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.
Phase-Type Distributions
- class APH(alpha_or_mean=None, T_or_scv=1.0, skew=None, mean=None, scv=None)[source]
Bases:
PHAcyclic 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.evalCDFdoes.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 returnsF(t)as a float.evalCDF(t)with a vector returnsFat 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 ofFvalues.
- eval_cdf(t=None)
Evaluate the CDF, as MATLAB
APH.evalCDFdoes.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 returnsF(t)as a float.evalCDF(t)with a vector returnsFat 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 ofFvalues.
- 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.
- 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.
- 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.
- 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.
- 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.
- classmethod fit_raw_moments(m1, m2, m3)[source]
Create an APH distribution from the first three raw moments.
- Parameters:
- Returns:
APH distribution matching the given raw moments.
- Return type:
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:
- Returns:
APH distribution matching the given raw moments.
- Return type:
References
MATLAB: matlab/src/lang/processes/APH.m (fitRawMoments)
- class Coxian(means_or_rates, probs=None)[source]
Bases:
PHCoxian 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.
- 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.
- 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%.
- classmethod fitMeanAndSCV(mean, scv)
Create a Coxian distribution from mean and SCV.
Uses moment matching to construct a Coxian distribution. Matches MATLAB Coxian.fitMeanAndSCV.
- classmethod fitMeanAndScv(mean, scv)
Create a Coxian distribution from mean and SCV.
Uses moment matching to construct a Coxian distribution. Matches MATLAB Coxian.fitMeanAndSCV.
- 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%.
- class Cox2(mu1, mu2, phi1)[source]
Bases:
Coxian2-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:
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.
- 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.
- 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.
- 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.
- 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.
- class PH(alpha, T)[source]
Bases:
ContinuousDistribution,MarkovianPhase-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:
Initialize a new distribution.
- 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.
- 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.
- 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.
Markovian Arrival Processes
- class MAP(D0, D1)[source]
Bases:
ContinuousDistribution,MarkovianMarkovian 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:
Initialize a new distribution.
- 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)
- getIDC()[source]
Return the asymptotic index of dispersion for counts (IDC).
References
api/mam/map_analysis.py (map_idc)
- 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.
- 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.
- 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.
- class MMPP2(lambda0, lambda1, sigma0, sigma1)[source]
Bases:
MAPMarkov-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:
Initialize a new distribution.
- 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:
- Returns:
Fitted MMPP2 distribution.
- Raises:
ValueError – if the request is outside the MMPP(2) feasible set.
- Return type:
Discrete Distributions
- class Bernoulli(p)[source]
Bases:
DiscreteDistributionBernoulli 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.
- class Binomial(n, p)[source]
Bases:
DiscreteDistributionBinomial distribution.
The binomial distribution models the number of successes in n independent Bernoulli trials.
Initialize a new distribution.
- class DiscreteUniform(a, b)[source]
Bases:
DiscreteDistributionDiscrete Uniform distribution.
Assigns equal probability to all integers in [a, b].
Initialize a new distribution.
- class Geometric(p)[source]
Bases:
DiscreteDistributionGeometric 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.
- class Poisson(lambda_)[source]
Bases:
DiscreteDistributionPoisson 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.
- class Zipf(s, n=10000)[source]
Bases:
DiscreteDistributionZipf distribution.
The Zipf distribution is a power-law distribution often used to model rank-frequency relationships (e.g., word frequencies).
- Parameters:
Initialize a new distribution.
Special Distributions
- class Disabled[source]
Bases:
ContinuousDistributionDisabled 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 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.
- class Immediate[source]
Bases:
DetImmediate (zero delay) distribution.
Represents instantaneous service with zero delay.
Initialize a new distribution.
- classmethod get_instance()
Get singleton instance of Immediate distribution.
- class Replayer(trace, loop=True)[source]
Bases:
DiscreteDistributionTrace-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:
Initialize a new distribution.
- 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.
- 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,uniformsandtransformed.- 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,uniformsandtransformed.- 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.
Layered Networks (line_solver.layered)
The layered module provides support for layered queueing networks (LQNs).
Main Classes
- class LayeredNetwork(name)[source]
Bases:
objectNative 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.
- 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.
- 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.
- 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.
- 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:
Example
>>> model = LayeredNetwork('ClientServer') >>> # ... build model ... >>> model.writeXML('model.lqnx')
- 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)
- 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).
- plot(show_task_graph=False, show=True)[source]
Plot the layered-network graph (native equivalent of MATLAB plot).
- 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_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:
- 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:
- Returns:
LayeredNetwork model
- Return type:
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:
- Returns:
LayeredNetwork model
- Return type:
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:
- Returns:
LayeredNetwork model
- Return type:
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:
- Returns:
LayeredNetwork model
- Return type:
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,RateDependentProcessor 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
- get_multiplicity()
Get multiplicity.
- get_replication()
Get 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,RateDependentTask 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
- setPriority(priority)
Set the scheduling priority of this task (lower is served first).
- 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:
Example
>>> task.add_precedence(ActivityPrecedence.serial([a1, a2, a3])) >>> task.add_precedence([ ... ActivityPrecedence.serial([a1, a2]), ... ActivityPrecedence.and_fork(a1, [a3, a4]) ... ])
- setFanOut(dest, value)[source]
Set fan-out to a destination task (for replication load distribution).
- get_multiplicity()
Get multiplicity.
- get_replication()
Get 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:
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}.
- class Entry(model_or_name, name=None)[source]
Bases:
objectEntry 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.
- property obj
Return self for compatibility with wrapper code that accesses .obj
- 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.
- class Activity(model_or_name, name_or_demand=None, demand=None)[source]
Bases:
objectActivity 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
- 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.
- 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.
- 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.
- 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).
- 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).
- 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:
objectActivity 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
- 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:
Example
>>> task.add_precedence(ActivityPrecedence.Serial(a1, a2, a3)) >>> task.add_precedence(ActivityPrecedence.Serial([a1, a2, a3]))
- 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:
- Returns:
ActivityPrecedence object
- Return type:
Example
>>> task.add_precedence(ActivityPrecedence.AndFork(start, [branch1, branch2]))
- 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:
- Returns:
ActivityPrecedence object
- Return type:
Example
>>> task.add_precedence(ActivityPrecedence.AndJoin([branch1, branch2], end)) >>> task.add_precedence(ActivityPrecedence.AndJoin([b1, b2, b3], end, quorum=2))
- 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:
- Returns:
ActivityPrecedence object
- Return type:
Example
>>> task.add_precedence(ActivityPrecedence.OrFork(start, [fast, slow], [0.7, 0.3]))
- 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:
- Returns:
ActivityPrecedence object
- Return type:
Example
>>> task.add_precedence(ActivityPrecedence.OrJoin([fast, slow], end))
- 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:
- Returns:
ActivityPrecedence object
- Return type:
Example
>>> task.add_precedence(ActivityPrecedence.Loop(init, [compute], 5))
- 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:
- Returns:
ActivityPrecedence object
- Return type:
Example
>>> task.add_precedence(ActivityPrecedence.CacheAccess(lookup, [hit, miss]))
Cache Tasks
- class CacheTask(model, name, total_items, cache_capacity, replacement_strategy, multiplicity=1)[source]
Bases:
TaskCache 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
- 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.
- class ItemEntry(model, name, total_items, access_prob)[source]
Bases:
EntryItem 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: