io

srvn_is_file(filename)

TF = SRVN_IS_FILE(FILENAME) true when FILENAME holds SRVN text rather than LQN XML.

@brief Sniffs an LQN file: SRVN text (the LQNS .in / .lqn grammar) or LQN XML. @details The file is SRVN when its first non-blank character is not ‘<’, the rule the Python (line_solver.io.srvn_io.is_srvn_file), JAR and C++ loaders apply, so a .lqn or .xml file in either interchange loads the same way everywhere. @param filename Path of an existing file. @return tf True for SRVN text, false for XML.

See also srvn2lqnx(), LayeredNetwork.parseSRVN()

srvn2lqnx(src, name)

XML = SRVN2LQNX(SRC, NAME) translate SRVN text into an LQN XML document.

@brief Translates an SRVN document (the LQNS .in / .lqn text grammar) into the LQN XML interchange. @details The translation is deterministic and reproduced byte for byte by the JAR (jline.io.SRVN2LQNX), Python (line_solver.io.srvn_io.srvn2lqnx) and C++ (line::lqn::srvn2lqnx) twins, which is what their parity tests compare against the golden in line-test.git/test/testsLQNS/srvn/expected. LayeredNetwork.parseSRVN feeds the result to LayeredNetwork.parseXML, so loading model.in builds the model that loading the .lqnx written by “lqn2xml -Oxml model.in” builds.

Grammar covered (LQNS 6): the G, P, U, T, E, A and R sections; processor, task, group, entry and activity declarations with every option LQNS accepts; fan-in/fan-out (I/O); semaphore and rwlock entries; activity precedence (sequence, AND/OR fork and join, quorum join, loop, reply lists); #pragma lines; and SPEX scalar parameters ($x = expr), which are substituted. A SPEX sweep ($x = [ … ]) used by the model is refused (LINE:unsupported:Format), since one LayeredNetwork carries one value per parameter. SPEX observations (%u $U), histogram requests (H) and the R result list are requests for output and are skipped. Numbers are copied verbatim from the source token (a leading ‘+’ dropped); only computed parameter values are spelled by srvn_format_number.

@param src The SRVN document text (char or string). @param name The model name written to <lqn-model name=…>; defaults to ‘model’. @return xml The LQN XML document, LF line endings, with a trailing newline.

Errors: LINE:input:Format on a syntax or reference error, LINE:unsupported:Format when the model uses a SPEX sweep.

See also LayeredNetwork.parseSRVN(), srvn_is_file(), srvn_format_number()

srvn_format_number(x)

S = SRVN_FORMAT_NUMBER(X) the spelling srvn2lqnx gives a computed SPEX parameter value.

@brief Shortest C %g spelling of X that reads back to X exactly. @details An integral X below 1e15 prints as %d; otherwise the first of %.1g … %.17g that round-trips through str2double. The JAR, Python and C++ SRVN readers share this rule, so a computed value is written identically by all four (Python: line_solver.io.srvn_io.format_number). @param x A real scalar. @return s Its spelling.

See also srvn2lqnx()

pnml_save(model, filename)

PNML_SAVE Write a LINE Petri net to a PNML (ISO/IEC 15909-2) file.

PNML_SAVE(MODEL, FILENAME) writes the Place/Transition net held by MODEL to FILENAME in the PNML place/transition grammar http://www.pnml.org/version-2009/grammar/ptnet, so that a LINE net can be read by the tools built around that corpus (GreatSPN, TINA, the Model Checking Contest harnesses).

THE P/T GRAMMAR IS UNCOLOURED, so what it can carry is narrower than what LINE can express, and the difference is REFUSED rather than approximated:

  • more than one job class – a LINE net whose tokens carry a class is a coloured net, and flattening the colours would change the model;

  • an open class, a Source or a Sink – the P/T grammar has no unbounded token source;

  • a queueing place, which is a station rather than a place;

  • a firing-rate dependence, which no PNML element can carry;

  • a distribution outside the scalar-parameter families listed in PNML_DIST_TO_XML below, e.g. a phase-type or a Markovian arrival process given by matrices.

TIMING RIDES IN A TOOLSPECIFIC BLOCK, which is where the grammar puts anything it does not define. Each LINE MODE becomes one PNML transition, so that the arcs of a mode are the arcs of a transition as the grammar requires; the block records which LINE transition and mode the PNML transition came from, so PNML_LOAD regroups the modes it split. A reader that ignores the block still sees a correct untimed P/T net.

See also PNML_LOAD, LINEMODEL_SAVE.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

pnml_load(filename, netName)

PNML_LOAD Read a PNML (ISO/IEC 15909-2) place/transition net into LINE.

MODEL = PNML_LOAD(FILENAME) reads the first net of a PNML document in the place/transition grammar and returns the equivalent LINE Network: one Place per PNML place, one Transition per PNML transition, and a single ClosedClass holding the tokens of the initial marking.

MODEL = PNML_LOAD(FILENAME, NETNAME) reads the net with the given id, for a document holding more than one.

THE GRAMMAR IS UNTIMED. A PNML place/transition net says nothing about how long a transition takes to fire, so a file written by another tool is read with every transition TIMED and EXPONENTIAL AT RATE 1, which is the convention of the stochastic Petri net literature and of GreatSPN’s own default. A file written by PNML_SAVE carries LINE’s timing in its toolspecific block and reads back with the distributions, servers, priorities and weights it was written with, and with the modes regrouped into the transitions they were split from.

Inhibitor arcs are read from the <type value=”inhibitor”/> extension that GreatSPN, TINA and PIPE all write, since the grammar itself has no inhibitor arc.

See also PNML_SAVE, LINEMODEL_LOAD.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

saturation_cutoffs(fAt, K, cmax)

[TAU, BOUNDED] = SATURATION_CUTOFFS(FAT, K, CMAX) Per-class saturation cutoffs of a count-indexed function f(n), the search behind the OI/PAS and class/joint-dependence table extents of linemodel_save.

FAT(n) returns the row of values the writer would tabulate at the 0-based count vector n (1xK), ALREADY SANITIZED (non-finite -> 0), so the search compares what the table holds. Starting from tau = 1, a class whose extra job still changes f anywhere on the current box has its cutoff raised, until no class moves. Only pairs (m, m + e_r) with sum(m) + 1 <= CMAX are compared: a state above the buffer is never visited, so whatever a reloaded table answers there cannot move the search, which is what makes save -> load -> save a fixed point. Passing 256, or a cutoff reaching CMAX (the box would be no smaller than the buffer), gives BOUNDED = false. CMAX defaults to Inf. Twins: _saturation_cutoffs (Python linemodel_io), io::saturation_cutoffs (C++), LineModelIO.saturationCutoffs (JAR).

Copyright (c) 2012-2026, Imperial College London All rights reserved.

map2renv(model, options)

[ENVMODEL, INFO] = MAP2RENV(MODEL, OPTIONS)

Markov-modulated image of a network with MAP/MMPP/MMAP arrival or service processes as a queueing network in a random environment.

Every non-renewal process is a point process modulated by the CTMC with generator Q = D0 + D1, whose conditional intensity in phase k is lambda(k) = sum_j D1(k,j). The transformation freezes each phase into an environment stage in which the process is the Poisson process of that intensity, i.e. an exponential arrival or service time, and lets the environment switch stages at the rates of Q. With P modulated processes the stage set is the Cartesian product of their phase spaces and the environment generator is the Kronecker sum of the individual Q’s, so only one process changes phase at a time, as in the original model.

The image is exact in structure for an MMPP (diagonal D1): the modulating chain, its stationary distribution and the phase-conditional intensities are all preserved. For a general MAP the phase jumps that occur AT an event epoch (off-diagonal D1) are aggregated into Q and their correlation with the event stream is lost, so the image matches the modulating chain and the conditional intensities but not the full inter-event autocorrelation.

Populations are carried across stage switches unchanged (identity reset), as a phase switch moves no job.

OPTIONS is a solver options structure; OPTIONS.config.map_env_maxstages caps the number of environment stages (default 64).

INFO reports nstages, the per-process phase orders, whether every process was an MMPP, and the modulation records themselves.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

lqnx2srvn(xmlText, name)

TEXT = LQNX2SRVN(XMLTEXT, NAME) translate an LQN XML document into SRVN text.

@brief Writes an LQN XML document (a char/string, not a file name) as SRVN text, the LQNS .in grammar. @details The writer is the inverse of srvn2lqnx: LayeredNetwork.writeSRVN is writeXML followed by lqnx2srvn, so the mapping of a model onto the LQN schema stays in one place. The output is deterministic and byte-identical to the JAR, Python (line_solver.io.srvn_io.lqnx2srvn) and C++ twins (golden: line-test.git/test/testsLQNS/srvn/expected_srvn). What SRVN cannot say is refused by name rather than dropped: an LQX program, SPEX parameters, a semaphore’s initial state, the LINE dialect elements (cache, setup, call groups) and names that are not LQNS identifiers. Requests for output (result-*, service-time-distribution, mva-info, convergence-collection) are skipped, as the reader skips them. Entry phases carry no activity names in SRVN, so a phase activity reads back as <entry>_<phase>. Numbers are written by srvn_format_number, so 1.500000 becomes 1.5.

@param xmlText The LQN XML document text. @param name The model name for the header comment; defaults to the <lqn-model name>. @return text The SRVN document, LF line endings, with a trailing newline.

Errors: LINE:unsupported:Format for content SRVN cannot express, LINE:input:Format for malformed XML.

See also srvn2lqnx(), LayeredNetwork.writeSRVN()

linemodel_save(model, filename)

LINEMODEL_SAVE Save a LINE model to JSON.

LINEMODEL_SAVE(MODEL, FILENAME) saves the model to the specified JSON file, conforming to the line-model.schema.json specification.

Parameters:
  • model - Network, LayeredNetwork, Workflow, or Environment object

  • filename - output file path (should end in .json)

Example

model = Network(‘M/M/1’); source = Source(model, ‘Source’); queue = Queue(model, ‘Queue’, SchedStrategy.FCFS); sink = Sink(model, ‘Sink’); oclass = OpenClass(model, ‘Class1’); source.setArrival(oclass, Exp(1.0)); queue.setService(oclass, Exp(2.0)); P = model.initRoutingMatrix(); P{1}(1,2) = 1; P{1}(2,3) = 1; model.link(P); linemodel_save(model, ‘mm1.json’);

Copyright (c) 2012-2026, Imperial College London All rights reserved.

linemodel_load(filename)

LINEMODEL_LOAD Load a LINE model from JSON.

MODEL = LINEMODEL_LOAD(FILENAME) loads a model from the specified JSON file (conforming to line-model.schema.json) and returns a Network, LayeredNetwork, Workflow, or Environment object.

Parameters:

filename - path to a .json file

Returns:

model - Network, LayeredNetwork, Workflow, or Environment object

Example

model = linemodel_load(‘mm1.json’); solver = SolverMVA(model); AvgTable = solver.getAvgTable();

Copyright (c) 2012-2026, Imperial College London All rights reserved.

pas_saturation_cutoffs(muFun, K, cmax)

[TAU, BOUNDED] = PAS_SATURATION_CUTOFFS(MUFUN, K, CMAX) Per-class saturation cutoffs of an order-independent (OI/PAS) rate mu(c).

Port of the search in Python save_model (_saturation_cutoffs): the smallest per-class counts tau with mu(n) = mu(min(n, tau)) on every pair the search visits inside the buffer sum(n) <= CMAX (default Inf, no buffer). mu receives 1-based class tags, as in oi_rate_table (linemodel_save), and is compared as the table holds it (non-finite -> 0). BOUNDED is false when mu keeps changing past 256, or saturates no earlier than the buffer. Shared by linemodel_save (table extent and oiCutoffs) and ldes_require_finite_pas_buffers (the uncapped-buffer refusal, CMAX = Inf); the generic search is saturation_cutoffs.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

line_warning(caller, MSG, varargin)

LINE_WARNING(CALLER, ERRMSG)

line_verbosity(level)

LINE_VERBOSITY Sets the global verbosity level for the LINE toolbox.

LINE_VERBOSITY(LEVEL) sets the verbosity of LINE’s output and configures MATLAB’s warning behavior accordingly.

LEVEL should be one of the following (defined in VerboseLevel):
  • VerboseLevel.SILENT : Disables warnings and suppresses all output.

  • VerboseLevel.STD : Enables standard verbosity and warning backtrace.

  • VerboseLevel.DEBUGTurns the SOLVER CONSOLE on – a running

    progress log of every solver run (LineConsole).

DEBUG is the only way to switch the console on; there is no separate console switch and no ‘console’ solver option. A single run can ask for it on its own through the ‘verbose’ option:

SolverMVA(model,’verbose’,VerboseLevel.DEBUG).getAvgTable

If LEVEL is not provided, it defaults to VerboseLevel.STD.

Example

line_verbosity(VerboseLevel.SILENT);

See also: VerboseLevel, LineConsole, warning

line_printf(MSG, varargin)

LINE_PRINTF(MSG, VARARGIN)

line_native_env()

PREFIX = LINE_NATIVE_ENV()

Shell prefix that restores a SYSTEM loader environment for a native binary spawned out of MATLAB (line-cli, the ldes native binary).

MATLAB exports its own bin/glnxa64 and sys/os/glnxa64 to every child process, and the libstdc++ shipped there is older than the one a current toolchain links against. A binary this checkout compiled then fails to load:

common/line-cli: …/sys/os/glnxa64/libstdc++.so.6: version `GLIBCXX_3.4.32’ not found (required by common/line-cli)

which surfaces as “line-cli exited with code 1” and, one level up, as every lang=’cpp’ row of the model reporting “solver X missing from output”. Neither the binary nor the model is at fault, so the fix belongs here.

Only the entries under matlabroot (and MATLAB’s per-user .MathWorks tree) are dropped: a path the user set before starting MATLAB is theirs and is kept. A JVM runner must NOT be wrapped – it is MATLAB’s own JRE and wants those directories.

Returns ‘’ on Windows and macOS, where no such shadowing occurs.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

line_warning_always(caller, MSG, varargin)

LINE_WARNING_ALWAYS(CALLER, MSG, …)

Emit a warning without the repeat suppression applied by LINE_WARNING.

LINE_WARNING keeps only the last message and hides an identical repeat for 60 seconds. That is right for configuration notices cast once per model, but wrong for a warning that reports a correctness limitation of the analysis: solving several models in one session would then flag only the first one, and the user would read the silence on the others as a clean bill of health. Warnings that say “these numbers are not exact” must be raised for every model they apply to, so they go through here instead. Verbosity gating is unchanged: SILENT still silences everything.

line_java_exe()

JAVAEXE = LINE_JAVA_EXE()

Resolve the Java launcher used to spawn JMT (and any other JVM tool driven out of MATLAB). Returns ‘’ when no JVM is reachable, so a caller can raise a diagnosis of its own instead of letting the failure surface as a raw java.io.IOException (“Cannot run program “”java””: CreateProcess error=2”).

Search order:
  1. LINE_JAVA - full path of a launcher chosen by the user

  2. JAVA_HOME/bin - the conventional JDK/JRE variable

  3. MATLAB_JAVA/bin - the JVM MATLAB itself was pointed at

  4. the JRE bundled with MATLAB (layout varies across releases)

  5. “java” on PATH

Step 4 is what keeps the JMT viewers usable on a host with no system-wide Java, the common case on Windows: MATLAB ships its own JRE, so a missing PATH entry is not a missing JVM.

The result is cached per session: the PATH probe costs a process launch, and a JVM neither appears nor vanishes mid-session.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

line_java_cmd(caller)

JAVACMD = LINE_JAVA_CMD(CALLER)

Quoted Java launcher to prefix a JVM command line with, or a clear error naming the missing runtime when none is reachable. CALLER is the function reported in that error, usually mfilename.

Callers must go through here rather than hardcoding ‘java’: on a host with no Java on PATH, the bare string produces “‘java’ is not recognized” from the shell and, if the failure is then retried through java.lang.Runtime.exec, an unhandled java.io.IOException with CreateProcess error=2 that says nothing about what is actually missing.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

line_method_type(solvername, method)

LABEL = LINE_METHOD_TYPE(SOLVERNAME, METHOD)

Classification of a solution method, as printed in the solver banner:

<accuracy>, <randomness>

with ACCURACY in {exact, approximate, bound} and RANDOMNESS in {deterministic, randomized}. SOLVERNAME is the banner solver name (‘MVA’, ‘NC’, …) and METHOD the resolved method label, which may carry the ‘default/’ prefix produced by the banner (‘default/exact’).

Conventions, applied uniformly across the four codebases:
  • exact: the algorithm targets the metric with no modeling

    approximation; numerical truncation and floating-point error do not make a method approximate. Integral representations and transform inversions are exact, asymptotic expansions are not.

  • approximate: the algorithm introduces a heuristic, an asymptotic

    expansion, a decomposition, or a statistical estimate.

  • bound: the algorithm returns a formal one-sided bound on the

    metric, not a point estimate: the value is guaranteed to lie on the stated side of the exact one, and the two sides of a family bracket it. The side is read off the method label and printed with it (‘gb.upper’ -> ‘upper bound’).

  • randomized: the algorithm consumes pseudo-random numbers, so two runs

    agree only if the seed does.

Perfect sampling (cftp) is classified by the law it samples from, which is the stationary one, hence exact; the ordinary simulators are approximate because a finite horizon leaves warm-up bias on top of the sampling error.

Lookup order: ‘<solver>.<method>’, ‘<method>’, the tail after each dot of METHOD (longest suffix first), the head before its first dot, ‘<solver>’, then the global default ‘approximate, deterministic’. The unknown-method default is deliberately the conservative one: claiming exactness that a method does not have is the costlier error.

Keep this registry in step with line_citations.m and with its twins MethodType.java, solvers/base.py:method_type and cpp method_type.h.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

line_error_is(ME, category, kind)

LINE_ERROR_IS True when ME carries an id of the given error-vocabulary category.

TF = LINE_ERROR_IS(ME, CATEGORY) is true when ME.identifier is ‘LINE:<CATEGORY>:<kind>’ for any kind. CATEGORY is one of ‘unsupported’, ‘numerical’, ‘input’, ‘external’, ‘internal’.

TF = LINE_ERROR_IS(ME, CATEGORY, KIND) also requires the kind, e.g. line_error_is(ME, ‘unsupported’, ‘Cache’).

The legacy id ‘LINE:Error’ belongs to no category. See _kb/17-error-vocabulary.md; Java twin LineException.getErrorId().is(), Python twin is_error_category().

line_error_foreign_id(ME)

LINE_ERROR_FOREIGN_ID Error-vocabulary id carried by a foreign LINE failure, or ‘’.

ID = LINE_ERROR_FOREIGN_ID(ME) reads the id a Java, Python or C++ LINE failure carried across a bridge (_kb/17-error-vocabulary.md, “Crossing a codebase boundary”):

  • Java: ME.ExceptionObject, or a Throwable on its cause chain, is a jline.io.LineException, whose getIdentifier() is the id;

  • Python: the raised exception (ME.ExceptionObject, the value of the (type, value, traceback) tuple) has an ‘identifier’ attribute;

  • C++ and any text channel: the message holds ‘[<id>]’, the form line-cli prints after its prefix.

ID is ‘’ when ME carries none, when ME already carries a LINE id of its own (nothing to translate), or when the foreign id is the unclassified ‘LINE:Error’.

See also LINE_ERROR_RETHROW_FOREIGN, LINE_ERROR_IS.

line_error(caller, msg, varargin)

LINE_ERROR Display a plain-text error message with file and line info.

LINE_ERROR(CALLER, MSG, …) throws an error with CALLER’s name and message, including the source file and line number, in plain text (no hyperlink). Extra arguments are passed to sprintf to format MSG. The identifier is the legacy, unclassified ‘LINE:Error’.

LINE_ERROR(ID, CALLER, MSG, …) throws the same message under ID, an identifier of the cross-codebase error vocabulary ‘LINE:<category>:<kind>’ with category one of unsupported, numerical, input, external, internal (see _kb/17-error-vocabulary.md), e.g.

line_error(‘LINE:unsupported:Cache’, mfilename, ‘msg’)

A malformed ID is itself thrown as ‘LINE:internal:BadErrorId’. Java, Python and C++ carry the same identifiers, so callers match on ME.identifier (or line_error_is) rather than on the message text.

The exception carries NO MATLAB stack, so the console shows the message alone rather than the chain of internal frames that led to it. Every line_error is a diagnostic LINE wrote on purpose and its message already names the throwing function and line, so the frames in between (runAnalyzerChecks -> runAnalyzer -> getAvg -> getAvgTable -> aT …) are noise to a user who only asked a solver for a result. Genuine MATLAB faults (index out of range, undefined function) are untouched and still report in full.

Set the verbosity to VerboseLevel.DEBUG to get the stack back, e.g. SolverNC(model,’verbose’,VerboseLevel.DEBUG) or GlobalConstants.setVerbose(VerboseLevel.DEBUG). dbstop if error stops at the throw site either way, since this is still error().

line_debug(varargin)

LINE_DEBUG(MSG, VARARGIN) LINE_DEBUG(OPTIONS, MSG, VARARGIN)

Print debug message if verbose level is DEBUG. If OPTIONS struct is passed as first argument, checks options.verbose. Otherwise checks GlobalConstants.Verbose.

line_citations(tokens)

ENTRIES = LINE_CITATIONS(TOKENS)

Bibliographic references for the algorithms named by TOKENS, as a struct array with fields:

.key - bibliography key, as used in doc/latex/biblio.bib .ref - short reference, author-title-venue-year .covers - one line saying which part of the solution process it covers

METHOD NAMES is a cell array of algorithm or feature names (solver methods such as ‘bs’ or ‘comom’, transformations such as ‘mmt’, percentile methods such as ‘forktail’). Unknown tokens are ignored, so a caller may pass whatever it knows about a run. The registry is the manual’s method-to-citation table (doc/latex/manual.tex) and its bibliography; keep the two in step.

Attribution in LINE is pull-based: nothing is printed during a solve, and a user asks for the references with solver.citations() when writing them up.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

line_citation(toolName)

BIB = LINE_CITATION(TOOLNAME)

Return the BibTeX entry for the canonical paper of an external tool that a wrapper solver delegates to, so that the acknowledgement printed by LINE_ACK can be turned into a citation without retyping it. With no output argument the entry is printed instead.

TOOLNAME is ‘JMT’, ‘LQNS’ or ‘QNS’ (‘QNS’ shares the LQNS reference, qnsolver being part of that distribution). An unknown tool returns ‘’. Wrapper solvers that live outside this tree carry their own reference: their tools must not be named in this codebase.

The keys match doc/latex/biblio.bib and BIBLIOGRAPHY.md. Mirror any edit in jline.io.InputOutput.line_citation and line_solver.api.io.logging.line_citation.

Example

line_citation(‘JMT’)

See also LINE_ACK.

line_ack(toolName, verbose, msg, cite)

LINE_ACK(TOOLNAME, VERBOSE, MSG, CITE)

Print, once per session, the acknowledgement of the external tool that a wrapper solver delegates to, together with the pointer to its official website and the canonical paper to cite. The acknowledgement is pull-based, like the library attribution: nothing is printed at the default verbosity, and only a caller that asks for it explicitly, by running at VerboseLevel.DEBUG, gets the line. It is printed at most once per TOOLNAME per session. SOLVER.CITATIONS and LINE_CITATION are the quiet ways to obtain the same reference.

MSG and CITE supply the acknowledgement text and the reference for a solver that lives outside this tree; when omitted both come from the table below, which covers the in-tree wrapper solvers only. Mirror any edit to that table in the Java (jline.io.InputOutput.line_ack) and Python (line_solver.api.io.logging.line_ack) tables.

The machine-readable form of the same reference is LINE_CITATION(TOOLNAME), which returns the BibTeX entry.

See also LINE_CITATION.

lineTimeoutExceeded(options)

TF = LINETIMEOUTEXCEEDED(OPTIONS) Cooperative wall-clock checkpoint for iterative solvers. Returns true if the elapsed time since the solver launch (options.timeout_tic, a tic handle set at the top of the per-solver runAnalyzer) exceeds options.timeout (seconds). A missing/non-finite/non-positive budget, or a missing tic, means no budget.

lineDownloadLineCli(verbose)

LINEDOWNLOADLINECLI Download the line-cli binary from SourceForge if absent.

cli_path = lineDownloadLineCli() cli_path = lineDownloadLineCli(verbose)

Fetches the line-cli build published for this platform under https://line-solver.sourceforge.net/latest/ and places it in the common/ directory of this checkout or release, marked executable. It is the C++ twin of lineDownloadJAR, and the twin of find_line_cli’s fetch in the native Python package (python/line_solver/solvers/cpp_dispatch.py).

WHAT IS PUBLISHED IS A ZIP, and the uncompressed name is its fallback. The binary is ~94 MB stripped and ~36 MB deflated, so the archive is what a first-time lang=’cpp’ user waits through; the uncompressed copy stays published for LINE releases older than the archive and is what this function falls back to if the .zip is missing. MATLAB’s unzip does carry the exec bit across and Python’s zipfile does not; the chmod below is unconditional either way, exactly as it already was for the uncompressed copy.

A BINARY IS NOT A JAR. jline.jar runs wherever a JVM does; line-cli is one ELF per host triple, so the fetch is gated on a name the platform table below knows rather than attempted and diagnosed afterwards from an exec failure. upload-line-cli.sh publishes exactly the names this table asks for.

Parameters:

verbose (optional) – If true, print status messages (default: true)

Returns:

cli_path – Full path to the downloaded line-cli

Errors when this platform has no published build, when LINE_CLI_DOWNLOAD=0 refuses the download, or when the fetch fails. It never returns ‘’: a caller that cannot tell “no binary” from “a binary this call failed to obtain” is how a lang=’cpp’ request comes back with numbers from another engine.

line_error_rethrow_foreign(ME)

LINE_ERROR_RETHROW_FOREIGN Rethrow ME under the id of the foreign LINE failure it carries.

LINE_ERROR_RETHROW_FOREIGN(ME) is called in a bridge’s catch block. When ME wraps a Java, Python or C++ LINE failure that carried an error-vocabulary id (see LINE_ERROR_FOREIGN_ID), it throws a MATLAB error with that id and ME’s message, ME attached as the cause, so callers and SolverAUTO match on ME.identifier exactly as for a native failure. Otherwise ME is rethrown unchanged. It never returns.

jsimwView(filename)

JSIMWVIEW(FILENAME) Open model in JSIMwiz

jsimgView(filename)

JSIMGVIEW(FILENAME) Open model in JSIMgraph

lineDownloadJAR(verbose)

LINEDOWNLOADJAR Download jline.jar from SourceForge if not found locally.

class SAGE

SAGE MATLAB-to-SageMath bridge for symbolic analysis.

Static helpers that talk to the line-sage-rest service, the same JSON protocol the JAR (jline.api.sym) and native Python (line_solver.api.sym) use. The service is SageMath in a container; see io/sage/server.py for the endpoints.

It exists for two reasons. The Symbolic Math Toolbox is licence-gated, so a session without it cannot run any symbolic analysis at all; and where both are present, routing through one engine gives all three codebases the same normal form, which is what makes a symbolic result comparable across them.

Backend resolution, in order:
  1. an explicit URL in options.config.symbolic;

  2. the LINE_SAGE_URL environment variable;

  3. a line-sage-rest service already listening on a conventional port;

  4. a container started here from a locally present image;

  5. nothing, in which case the caller falls back to the toolbox.

Step 3 checks identity through /api/v1/info rather than trusting the port: every imperialqore line-*-rest service listens on 8080 by convention, so a health probe alone would accept the LQNS service.

Expressions cross as plain char infix strings, e.g. ‘2*x1 - 3*x2’. Numeric coefficients are read server side as exact rationals, so ‘0.1’ is 1/10 and not the binary double nearest to it.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Property Summary
CANARY_ARG
CANARY_EXPR

A service that answers /health can still be unable to COMPUTE. The line-sage-rest image ships a FLINT built for CPUs that have BMI2 and ADX; on an older host the first multi-limb exact operation raises SIGILL, the worker dies mid-request and the call returns no bytes at all. Health is pure Python and keeps answering, so it cannot see this. The canary is the weighted-average softmin form – what the fluid export actually sends – and is the smallest expression observed to trigger it; see _kb/11-conventions-and-gotchas.md.

CANARY_VALUE
DEFAULT_TIMEOUT

seconds per request

DOCKER_IMAGES
PROBE_PORTS
REQUIRED_ROUTES

The POST routes this client calls; a service that does not serve them all is older than the client and is refused by name. See servesRequiredRoutes.

STARTUP_TIMEOUT

seconds to wait for a container to answer

Method Summary
static asCellMatrix(x)

ASCELLMATRIX Nested JSON array to a cell matrix.

static asCellstr(x)
static ctmcMeasures(Q, symbols, weights, names, ratios, url, timeout)

CTMCMEASURES Stationary distribution and weighted sums of it.

[MEAS, RATIOS, PI, NUM, DEN] = CTMCMEASURES(Q, SYMBOLS, WEIGHTS,

NAMES, RATIOS, URL, TIMEOUT)

Every mean SolverCTMC reports is a NUMERIC linear functional of pi, so the caller builds one weight vector per measure with no algebra at all and this single round trip returns each w*pi as a rational function: a throughput is pi*depRates, a queue length is pi*stateSpaceAggr, a marginal probability is pi*indicator and a mean reward is pi*r.

WEIGHTS is (nmeasures x nstates), numeric or a cellstr of expressions: the entries are parsed in the same field as Q, so an expression is admissible and not only a number. RATIOS is a struct array with fields name, num and den, the last two being 0-based indices into WEIGHTS. A ratio whose denominator measure is identically zero comes back with expr ‘undefined’ and a reason, never as 0.

MEAS and RATIOS are ALWAYS struct arrays with the fields name, expr, num, den and reason, ‘’ where the service sent nothing. The normalization is not cosmetic: a defined measure and an undefined one carry different JSON fields, and jsondecode answers a cell array rather than a struct array whenever they are mixed.

static ctmcPassage(S, s0, alpha, atom, symbols, svar, want, nmax, url, timeout)

CTMCPASSAGE First passage time into a target set.

OUT = CTMCPASSAGE(S, S0, ALPHA, ATOM, SYMBOLS, SVAR, WANT, NMAX,

URL, TIMEOUT)

With A the complement of the target, S = Q(A,A) the sub-generator, S0 = -S*1 the exit vector and ALPHA the initial law restricted to A, following Harrison and Knottenbelt (2002),

L(s) = alpha*(s*I - S)^-1*s0 + atom Eqs. 1-2 (-S)*M(n) = n*M(n-1), M(0) = 1 Eq. 3

The MOMENTS come from the recursion, not from differentiating L: it carries no transform symbol, stays in the same fraction field and is NMAX right solves against one matrix. WANT is any of ‘lst’, ‘lstall’, ‘moments’, ‘momall’. SVAR is adjoined to the field only when a transform is asked for and must differ from every rate symbol.

static diff(exprs, variable, order, url, timeout)

DIFF Differentiate expressions with respect to VARIABLE.

static eval(exprs, assignment, url, timeout)

EVAL Substitute values for symbols and evaluate.

ASSIGNMENT is a struct whose fields are symbol names. This is how symbolic results are compared across codebases: symbol numbering follows event enumeration order, so comparing expression text is unsound while comparing substituted values is not.

static exprToChar(e)
static fluidODEs(rhs, vars, want, url, timeout, constraints, domain)

FLUIDODES Jacobian, LaTeX form and equilibria of a fluid drift.

CONSTRAINTS are expressions g(x) the equilibria must also zero, e.g. ‘x1 + x2 - 3’ for a closed chain of 3 jobs; DOMAIN ‘nonnegative’ keeps only equilibria with x >= 0. A service older than 1.1.0 ignores constraints, and a closed model’s equilibria then form a continuum it never finishes solving, so such a service is refused by name rather than left to time out.

static freePort()

FREEPORT An ephemeral port nothing is listening on.

There is a race between finding the port and docker binding it; it is the same race the JAR and Python clients run, and losing it fails the start loudly rather than silently sharing a port.

static getDockerImage()

GETDOCKERIMAGE First locally present image tag, or ‘’ if none.

static hasFeature(url, feature)

HASFEATURE True if /api/v1/info lists FEATURE under “features” (line-sage-rest 1.1.0 and newer); false for an older service.

static hasLocalImage(image)

HASLOCALIMAGE True if IMAGE is present in the local Docker store.

static hasSymbolicToolbox()

HASSYMBOLICTOOLBOX True if sym objects can be constructed here.

static isAvailable(requested)

ISAVAILABLE True if a symbolic backend can be resolved.

static isReachable(url)

ISREACHABLE True if the service answers a health probe.

static isSageService(url)

ISSAGESERVICE True if the service is line-sage-rest and not another line-*-rest service sharing the conventional port.

static isUsable(url)

ISUSABLE True if the service answers AND can evaluate.

Verdicts are cached per URL: this costs one small request the first time a service is considered, and nothing after.

static numToChar(v)

NUMTOCHAR Decimal text the service reads as an exact rational.

THE TEXT IS THE VALUE, not a rendering of it: the service reads a decimal literal off its source text and converts it to an exact rational, so 0.1 is 1/10 while 0.10000000000000001 is a different number. This prints the SHORTEST decimal that round-trips to the same double, which is what the JAR’s Double.toString and the C++ decimal_string emit. A fixed ‘%.17g’ does not: on 1/10 it sends 0.10000000000000001 and the same model then yields a different normal form from MATLAB than from the other three codebases.

static post(url, endpoint, payload, timeout)

POST One JSON request, with the service’s own errors raised here.

static probeHosts()

PROBEHOSTS Host names to search for a service, in order.

INSIDE THE CONTAINER, localhost IS THE CONTAINER. matlab_docker.sh gives MATLAB its own network namespace on purpose (the MathWorks Service Host claims an abstract AF_UNIX socket, which is scoped to that namespace), so a line-sage-rest published on the host’s port answers on host.docker.internal and on nothing else this process can name. Without this the whole symbolic ladder skipped under test phase 6 on hosts that had the image and could have run it, and startContainer could not help: there is no docker in here. The alias comes from the –add-host in matlab_docker.sh; an older container without it simply fails the probe, as before.

static pullDockerImage(target)

PULLDOCKERIMAGE Pull TARGET if Docker is usable and there is room.

Returns the tag on success, else ‘’. Storage-guarded via lineDockerHasStorageFor (the same guard as the LQNS/JMT wrappers), so an opt-in symbolic request never silently fills the Docker disk; on refusal the caller keeps its native algebra.

static require()

REQUIRE Resolve a backend or error with how to get one.

static resolve(requested)

RESOLVE Base URL of a usable service, or ‘’ if there is none.

REQUESTED is ‘’ or ‘auto’ to search, a URL to use a specific service, ‘none’ to disable the backend, or an image name.

static scalarSymOrChar(item)
static sensitivity(Q, symbols, theta, reward, url, timeout)

SENSITIVITY Exact d(pi)/d(theta) and, given a reward, d(E[r])/d(theta).

Exact where @SolverCTMC/getSensitivity uses a central difference accurate to O(h^2). As there, dr/dtheta is taken to be zero.

static servesRequiredRoutes(url)

SERVESREQUIREDROUTES True if the service is new enough to use.

A SERVICE CAN BE HEALTHY AND STILL TOO OLD, and that case used to reach the caller as a request failure rather than as a resolution failure. The roster ran a line-sage-rest image predating the ctmc/measures and ctmc/passage routes on 2026-09-11: it answered /api/v1/health, passed the isUsable arithmetic canary, and then failed every call to those routes with “unknown endpoint /api/v1/ctmc/measures” – an error that reads as a defect in the solver rather than as an out-of-date container. /api/v1/info lists what the running server actually routes, so asking it turns that into a named refusal here, where the caller falls back to its own algebra or skips and is told why.

static simplify(exprs, form, url, timeout)

SIMPLIFY Rewrite expressions: simplify, factor, together, cancel, expand or latex.

static solveCTMC(Q, symbols, url, timeout)

SOLVECTMC Symbolic stationary distribution, pi*Q = 0, sum(pi) = 1.

Q is a cell array of expression strings or a sym matrix, SYMBOLS a cellstr of the symbols occurring in it. PI comes back as a cellstr, or as a sym vector when the Symbolic Toolbox is present.

static startContainer(image)

STARTCONTAINER Run the service and wait for it to answer.

The container is bound to a free host port, so several MATLAB sessions, or a session next to a hand-started service, do not collide. It stays warm for the session (repeated symbolic calls are then cheap) and is best-effort stopped when MATLAB exits or SAGE is cleared, via the onCleanup guard below. Stop it sooner with SAGE.stopContainer. Held only for its destructor: cleared at MATLAB exit / clear SAGE, which fires the onCleanup below and stops the container.

static startedContainer(name)

STARTEDCONTAINER Get/set the container this session started.

Call with a name to set, ‘’ to clear, or no argument to get the tracked name (parity with the Java/Python startedContainer state).

static stopContainer()

STOPCONTAINER Stop the container this session started, if any.

Parity with the Java (shutdown hook) and Python (atexit) clients: it stops only the container started here, not every line-sage-rest-* on the host.

static stopContainerByName(name)

STOPCONTAINERBYNAME Stop a single named container, best effort.

static toExpressionList(exprs)

TOEXPRESSIONLIST Expressions as a cellstr, whatever came in.

static toExpressionMatrix(Q, symbols)

TOEXPRESSIONMATRIX Generator as a cell array of expressions.

static toExpressionRows(W)

TOEXPRESSIONROWS A possibly NON-SQUARE block as nested cells.

toExpressionMatrix is for the generator and assumes a square matrix; a weight block is (measures x states) and rarely square. jsonencode flattens a cell matrix, so the rows are handed over as a cell of row cells, which is the nested array the service wants.

static toMeasureStruct(x)

TOMEASURESTRUCT Measures as a struct array with a FIXED field set.

jsondecode returns a struct array only when every object in the JSON array carries the SAME fields, and a cell array of structs otherwise. The measure objects are heterogeneous by design: a defined one carries num and den, an undefined one carries reason instead. So the return type flipped between struct array and cell depending on whether any ratio happened to be undefined, which is a property of the MODEL rather than of the call. Normalizing here gives the caller one shape to write against, with ‘’ for whatever the service did not send.

static toSymOrChar(items)

TOSYMORCHAR Expressions as sym when the toolbox is present.

Returning sym where possible keeps every existing caller working unchanged; without the toolbox the same result is still usable as text, which is the whole point of the Sage backend.

static trimUrl(url)
QN2MATLAB(MODEL, MODELNAME, FID)
QN2LQN(model)
QN2LINE(sn, modelName)

MODEL = QN2LINE(QN, MODELNAME)

QN2JSIMG(model, outputFileName, options)

QN2JSIMG Writes a Network model to JMT JSIMG format

OUTPUTFILENAME = QN2JSIMG(MODEL) exports the Network MODEL to a temporary JSIMG file and returns the file path.

OUTPUTFILENAME = QN2JSIMG(MODEL, OUTPUTFILENAME) exports to the specified file path.

OUTPUTFILENAME = QN2JSIMG(MODEL, OUTPUTFILENAME, OPTIONS) uses the specified solver options for export configuration.

Parameters:
  • model - Network model to export

  • outputFileName - Optional output file path (default – temp file)

  • options - Optional SolverOptions with simulation parameters

Returns:

outputFileName - Path to the created JSIMG file

Example

model = Network(‘example’); % … define model … fname = QN2JSIMG(model); jsimgView(fname); % View in JMT

See also: JMTIO, jsimgView()

Copyright (c) 2012-2026, Imperial College London All rights reserved.

QN2JAVA(MODEL, MODELNAME, FID, HEADERS)
class WfCommonsLoader

Bases: handle

WfCommonsLoader - Load WfCommons JSON workflows into LINE Workflow objects.

WfCommonsLoader provides static methods to load workflow traces from the WfCommons format (https://github.com/wfcommons/workflow-schema) into LINE Workflow objects for queueing analysis.

Supported schema versions: 1.4, 1.5

Example

wf = WfCommonsLoader.load(‘workflow.json’); ph = wf.toPH();

Example with options:

options.distributionType = ‘exp’; options.defaultRuntime = 1.0; wf = WfCommonsLoader.load(‘workflow.json’, options);

options.resolveChildrenByName (default true): a child reference is looked up among the task ids first and, failing that, among the task names. The Pegasus traces of schema 1.4 (e.g. Montage) give each task an id such as ‘ID0000001’ but list its children by name (‘mBackground_ID0000013’), so an id-only lookup drops every edge. A name shared by several tasks is ambiguous and does not resolve. A reference that resolves to no task is reported by line_warning. Set it false for the id-only lookup.

Edges are the union of the children and parents lists, without duplicates, so a trace that gives either form (the Montage dss trace lists parents only) yields the same graph. Parent references resolve by the same id-then-unique-name rule.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Property Summary
SUPPORTED_SCHEMA_VERSIONS
Method Summary
static load(jsonFile, options)

LOAD Load a WfCommons JSON file into a Workflow object.

WF = WFCOMMONSLOADER.LOAD(JSONFILE) WF = WFCOMMONSLOADER.LOAD(JSONFILE, OPTIONS)

Parameters:
  • jsonFile - Path to WfCommons JSON file

  • options - Optional struct with – .distributionType - ‘exp’ (default), ‘det’, ‘aph’, ‘hyperexp’ .defaultSCV - Default SCV for APH/HyperExp (default: 1.0) .defaultRuntime - Default runtime when missing (default: 1.0) .useExecutionData - Use execution data if available (default: true) .storeMetadata - Store WfCommons metadata (default: true)

Returns:

wf - Workflow object

static loadFromStruct(data, options)

LOADFROMSTRUCT Load from pre-parsed struct.

WF = WFCOMMONSLOADER.LOADFROMSTRUCT(DATA) WF = WFCOMMONSLOADER.LOADFROMSTRUCT(DATA, OPTIONS)

Parameters:
  • data - Struct from jsondecode

  • options - Options struct (see load method)

Returns:

wf - Workflow object

static loadFromUrl(urlString, options)

LOADFROMURL Load WfCommons JSON from a URL.

WF = WFCOMMONSLOADER.LOADFROMURL(URLSTRING) WF = WFCOMMONSLOADER.LOADFROMURL(URLSTRING, OPTIONS)

Useful for loading workflows directly from repositories like wfcommons/pegasus-instances.

Parameters:
  • urlString - URL pointing to WfCommons JSON file

  • options - Options struct (see load method)

Returns:

wf - Workflow object

static validateFile(jsonFile)

VALIDATEFILE Check if file is valid WfCommons schema.

ISVALID = WFCOMMONSLOADER.VALIDATEFILE(JSONFILE)

Returns:

isValid - true if file is valid WfCommons format

class PYLINE

Bases: pybridge.Model, pybridge.Matrix, pybridge.Solvers, pybridge.Result

PYLINE MATLAB-to-native-Python bridge (lang=’python’).

Static helpers that construct a native line_solver (pure Python, no JVM) model element-by-element through MATLAB’s in-process Python interface (py.*), run a native Python solver, and marshal results back. Mirrors the structure of JLINE.m (line_to_jline/from_line_network/from_line_node/…) with the jline.* Java backend replaced by py.line_solver.* and the Java Matrix class replaced by numpy arrays.

Requirements: MATLAB pyenv must point at a CPython where the in-tree python/ line_solver package is importable. No JPype/JVM is used.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

THIS CLASS IS A FACADE. Its static methods live in +pybridge, one class per job (Model, Matrix, Solvers, Result), all inherited here, so every PYLINE.<method> call site resolves unchanged. Add a method to the part that does its job, never here.

MAPQN2RENV(model, options)

ENVMODEL = MAPQN2RENV(MODEL, OPTIONS) Random-environment image of a network with MAP/MMPP service or arrival processes.

Retained name for the transformation now implemented by MAP2RENV, which generalizes it from a single MMPP2 service process to any number of MAP, MMPP2 or MMAP arrival and service processes of arbitrary phase order (the stage set is then the Cartesian product of the phase spaces). New code should call MAP2RENV directly.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

class LineStatus

LINESTATUS One line of output that is REWRITTEN rather than repeated.

An iterative solver reports its progress once per iteration. Printed as one line each, a run of a few hundred iterations scrolls everything else out of the terminal to say the same six numbers over and over. LINESTATUS keeps that report on a SINGLE row and updates it in place:

LineStatus.set(‘Iter %2d. Analyze time: %.3fs.’, it, t); LineStatus.append(’ MaxIterErr=%.3e’, err); % same row, more fields LineStatus.close(); % end the row, once

The row is rewound with BACKSPACES, not with a carriage return: ‘b’ is the idiom that works both in the MATLAB desktop Command Window and under matlab -batch, whereas ‘r’ is handled inconsistently between them. A shorter row is padded with blanks so the tail of a longer predecessor cannot survive underneath it.

AN INTERLEAVED PRINT ENDS THE ROW, it does not corrupt it. Backspaces walk back over whatever is actually on the terminal and cannot cross a newline, so a warning raised mid-iteration used to land inside the row and leave the NEXT rewind short, splicing two iterations into one line:

Iter 51. … Runtime: 6.177s. MaxIterErr=1.08e-14 (tol=5.0000Iter 52. …

LINE_PRINTF therefore calls CLOSE before it writes anything, which is why this class emits through its own raw path rather than through line_printf – otherwise every update would close the row it was drawing. A warning now gets its own line and the next iteration opens a fresh row. Inside SolverLN the interleaving is rare to begin with, because the layer solvers are silenced (see SolverLN.setSolver).

CLOSE is idempotent and emits the newline the row never had.

A row containing a newline cannot be rewound, so SET and APPEND strip them. At VerboseLevel.SILENT every entry point is a no-op and no state is kept, so the bookkeeping cannot drift against what was actually written.

See also: line_printf(), LineConsole, VerboseLevel

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Method Summary
static append(fmt, varargin)

APPEND(FMT, …) extends the row and rewrites it

The row is assembled by more than one caller – EnsembleSolver ITERATE lays down the timings and SolverLN CONVERGED adds the iteration error – so appending has to re-render the whole row rather than print a fragment after it.

static close()

CLOSE() ends the row with a newline and forgets it

The blanks that padded a SHRINKING row stay on the finished line. They cannot be taken back: a backspace moves the cursor, it does not erase, so rewinding over the pad and then emitting the newline changes only where the cursor was, not what the line holds. They are invisible on a terminal and only surface if the line is copied. The way to avoid them is to keep one-off notices OFF the row – see how SolverLN.converged prints its averaging notice on a line of its own – so that the row never shrinks.

static isOpen()

TF = ISOPEN() true while a status row is being rewritten

static reset()

RESET() forgets the open row WITHOUT emitting anything

For an interrupted solve, where the row is already lost: closing it would backspace over whatever the error printed instead.

static set(fmt, varargin)

SET(FMT, …) replaces the row with this text

static state(newst)

ST = STATE(NEWST) reads, and optionally replaces, the row state

TWO FIELDS, AND THEY ARE NOT THE SAME STRING. text is the row’s LOGICAL content, which APPEND extends; width is how many characters are actually on the terminal, which is what the next rewind has to walk back over. Keeping only the on-screen string and appending to that made every append start after the blanks that padded the previous row, so the row grew by its own padding on every iteration and ran to hundreds of thousands of columns.

class PythonBackend

Bases: LineBackend

PYTHONBACKEND lang=’python’: the native line_solver, in process via PYLINE.

getAvg serves the steady-state averages from runAnalyzerPreamble; the other methods are the getters lang=’python’ answers through LineBackend.serve. A getter with no method here runs its MATLAB body under lang=’python’.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Property Summary
dispatchEarly
lang
servesGetAvg
Method Summary
envGetAvg(~, solver)

The stage solvers this ensemble was built with decide the bridge’s stage-solver class AND its options; see PYLINE.getEnvAvg. The forwarded horizon is the RESOLVED one, so the engine integrates the interval the native path does instead of dropping a non-finite value and falling back to its own default of 100. On renv_fourstages_repairmen that was 30 against 100 – a different question asked of each codebase, and most of what the example’s parity slack used to measure. stageOptions is a struct COPY, so this states the horizon without leaving it on the stage solver.

getAvg(~, solver, options, varargin)

RES = GETAVG(SELF, SOLVER, OPTIONS) solve with the native python solver of the same name; per-chain metrics are not carried.

getTranAvg(~, solver)

The averages are delegated in runAnalyzerPreamble, so the transient tables are never populated by the solve; the native getTranAvg fills them here. PYLINE.tranAvg returns the result-table form and @NetworkSolver/getTranAvg does the metricVal wrapping.

lnGetEnsembleAvg(~, solver)

The layer solver this ensemble was built with decides the bridge’s layer-solver class; see PYLINE.SolverLN. Under lang=’python’ no layer is constructed, so the class is read off the factory the same way CPPLINE.lnLayerSolver reads it.

lnGetSensitivityTable(~, solver, varargin)
lnGetTranAvg(~, solver)

The native SolverLN runs the whole layered transient, coupled or decoupled per options.config.ln_transient, over options.timespan. Under lang=’python’ no MATLAB layer is built, so the MATLAB getTranAvgCoupled/Decoupled have nothing to run on. The layer solver class is resolved as for lnGetEnsembleAvg.

ssaGetProb(~, solver, node, varargin)
ssaGetProbAggr(~, solver, node, varargin)
ssaGetProbSys(~, solver)
ssaGetProbSysAggr(~, solver)
class LineConsole

LINECONSOLE Running progress log of a LINE solver run.

LineConsole narrates what a solver is doing while it does it: reading the model, compiling the network structure, computing chains, visits and demands, resolving the method, iterating, and closing with the figures of merit. Each line carries the elapsed time since the run started:

[ 0.014s] compiling the network structure (sn) [ 0.031s] computing chains and visit ratios [ 0.052s] closed queueing network: 3 stations, 1 class, 1 chain [ 0.061s] AMVA sweep 10: residual 1.11e-01, X = 1.2225

It prints no tables: the result table stays the caller’s own getAvgTable. THE CONSOLE IS VerboseLevel.DEBUG: it narrates exactly when the run is at DEBUG and is silent at every lower level, and it never alters a numerical result. There is no separate console switch – the console was one, until it became clear that a running progress log IS what a debug verbosity is for, and two switches for one channel only let a session ask for DEBUG and get nothing.

Typical use:

line_verbosity(VerboseLevel.DEBUG) SolverMVA(model).getAvgTable

or, for one run alone:

SolverMVA(model,’verbose’,VerboseLevel.DEBUG).getAvgTable

Nested runs (an inner solver invoked by SolverLN or SolverAUTO) do not narrate: only the outermost run writes, so several layer solves cannot interleave their lines. Use ISACTIVE to suppress a legacy print, OWNSLOG to emit.

See also: line_verbosity(), VerboseLevel, GlobalConstants

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Method Summary
static article(word)

S = ARTICLE(WORD) the indefinite article that fits WORD

static beginRun(options)

GUARD = BEGINRUN(SELF, OPTIONS) opens a console run

Returns an onCleanup handle that closes the run, writing the closing lines, when the caller’s analyzer returns or errors. The caller MUST hold the handle for the whole analysis.

static blankState()

ST = BLANKSTATE() initial console state

static className(sn, r)

S = CLASSNAME(SN, R) display name of a class index

static closeRun()

CLOSERUN(SELF) closes the innermost open run

static closingLines()

CLOSINGLINES(SELF) the figures of merit and the timing split

static compileDetail(fmt, varargin)

COMPILEDETAIL(FMT, …) one stage line of a structure compile

Silenced inside a PUSHQUIET scope, and inside an open run, where the structures being compiled are those of auxiliary models (SolverLN layers) rather than of the model under study. BEGINRUN lifts the second rule while it compiles its own model.

static compileStruct()

COMPILESTRUCT(SELF) compiles sn, narrated by refreshStruct

static compiling(name)

COMPILING(NAME) announces the compilation of a model structure

Inside an open run the model being compiled is an auxiliary one (a SolverLN layer), and saying so keeps it apart from the model the user asked about.

static deferPrint(fmt, varargin)

DEFERPRINT(FMT, …) queues a legacy line for after the run closes

The console owns the log while a run narrates, so a solver’s standard completion message is held here and written once the closing DONE line has gone out, reading as it would with the console off.

static delayMask(sn)

MASK = DELAYMASK(SN) stations that serve without queueing

static detail(text, reset)

DETAIL(TEXT) reports a solver’s own debug message as a substep

LINE_DEBUG routes here while a run narrates. Consecutive repeats are dropped, at most three messages of the same SHAPE (the text with its numbers masked) are reported, and the channel is capped per run, since a message inside a loop would bury the narration.

DETAIL(‘’, TRUE) resets the bookkeeping at the start of a run. It is held in persistent variables of this method rather than in the shared state struct: growing a cell array nested inside that struct, thousands of times through a static method, crashed the MATLAB JIT thread outright (segmentation violation) on a long SolverENV run.

static emitLine(indent, text)

EMITLINE(INDENT, TEXT) writes one timestamped line

The timestamp is the elapsed time since the opening line of the current run. Each run also reports its own duration in its closing line. A top-level row opens with a capital, an indented substep stays lowercase.

static ensembleClosing(st)

ENSEMBLECLOSING(SELF, ST) closing lines of an ensemble run

static featureList()

S = FEATURELIST(SELF) the model’s used language features

static field(s, name, dflt)

V = FIELD(S, NAME, DFLT) struct field with a default

static has(sn, name)

TF = HAS(SN, NAME) field test that works on structs and objects

static hasCompiledStruct(model)

TF = HASCOMPILEDSTRUCT(MODEL) true when sn is already built

static isActive()

TF = ISACTIVE() true while a run is narrating

static isDebug(level)

TF = ISDEBUG(LEVEL) true when a verbosity LEVEL asks for DEBUG

A logical is not a level (see WANTED) and never reaches here from that path; treated as false so a stray one cannot switch the console on by itself.

static iter(k, fmt, varargin)

ITER(K, FMT, …) reports iteration K of the current loop

Lines are decimated: the first 20 iterations report in full, then every 10th, and the loop stops reporting after 30 lines, so that a long run cannot bury the rest of the narration.

static loop(fmt, varargin)

LOOP(FMT, …) announces an iteration loop and resets its budget

Each loop of a run gets its own budget of reported iterations, so a solver that restarts a loop (the fluid integration passes) does not exhaust the budget of the next one.

static lowerFirst(s)

S = LOWERFIRST(S) lowercase the first letter of a sentence

The routed messages were written as standalone sentences; the console reads as one narration, so they join it in lower case unless they open with an acronym (CTMC, AMVA, LQN, …).

static modelKind(sn)

S = MODELKIND(SN) the phrase that names this kind of model

static modelName()

NAME = MODELNAME(SELF) the analyzed model’s name

static modelOf()

MODEL = MODELOF(SELF) the analyzed model, or []

static openRate(sn, c)

LAMBDA = OPENRATE(SN, C) total arrival rate of an open chain

static openingLines(options)

OPENINGLINES(SELF, OPTIONS) narrates the setup of the run

static ownsLog()

TF = OWNSLOG() true only inside the OUTERMOST open run

Analyzer hooks test this rather than ISACTIVE: an inner solver driven by SolverLN or SolverAUTO runs its own loops, and letting it write would interleave several narrations. Use ISACTIVE to suppress a legacy print, OWNSLOG to emit.

static plural(n, singular, plural)

S = PLURAL(N, SINGULAR, PLURAL) count with an agreeing noun

static popQuiet()

POPQUIET() ends one PUSHQUIET scope

static populationLine(sn)

S = POPULATIONLINE(SN) per-class population or open marker

static presolve()

PRESOLVE(SELF) demands, bottleneck and the elementary bounds

static pushQuiet()

GUARD = PUSHQUIET() suppresses detail lines for a bounded scope

Used where many auxiliary models are compiled in a row (the SolverLN layer builders): each keeps its one headline STEP and drops its SUBSTEP breakdown. The caller must hold the handle.

static readModel()

READMODEL(SELF) what the model object declares, before compiling

static recognizeModel()

RECOGNIZEMODEL(SELF) states what kind of model this is

static reportMethod(options)

REPORTMETHOD(SELF, OPTIONS) the method that will actually run

static reset()

RESET() forgets any open run (used after an interrupted solve)

static resultLines(res)

RESULTLINES(SELF, RES) one line per figure of merit

static resultOf()

RES = RESULTOF(SELF) the Avg result block, or []

static schedMix(sn)

S = SCHEDMIX(SN) counts of each scheduling strategy in use

static sessionClock(restart)

T = SESSIONCLOCK(RESTART) elapsed seconds on the console clock

The clock is restarted by the opening line of each run, so that every run’s timeline starts at zero rather than accumulating across the session.

static solverTag()

TAG = SOLVERTAG(SELF) short solver name, e.g. MVA

static sourceMask(sn)

MASK = SOURCEMASK(SN) stations that inject rather than serve

static state(newst)

ST = STATE(NEWST) reads, and optionally replaces, console state

static stationName(sn, i)

S = STATIONNAME(SN, I) display name of a station index

static step(fmt, varargin)

STEP(FMT, …) writes one progress line

static structOf()

SN = STRUCTOF(SELF) the model’s struct, or [] when it has none

static substep(fmt, varargin)

SUBSTEP(FMT, …) writes one indented progress line

static unmute()

UNMUTE() ends one muted scope opened by a silenced run

static vec(v)

S = VEC(V) compact rendering of a numeric row

static versionString()

S = VERSIONSTRING() the running LINE version

static wanted(options)

TF = WANTED(OPTIONS) resolves whether this run should narrate

THE CONSOLE IS DEBUG, and this is the whole rule: a run narrates when it is at VerboseLevel.DEBUG and at no lower level. The run’s own options.verbose decides when it carries one, otherwise the session level does; there is no third switch that could put the two out of step.

static writes()

TF = WRITES() true when a progress line should be printed

Either the outermost run is narrating, or no run is open and the session is at DEBUG – the second case is what lets model construction (refreshStruct) narrate before any solver exists.

class LineResultRecorder

Bases: handle

LINERESULTRECORDER Capture result tables with the solver that produced them.

Cross-codebase parity is asserted against one shared golden per example (goldens/baselines/*.json), keyed by SOLVER NAME. Until 2026-08-19 the only way to recover that key was to scrape the banner an example printed above each table – one regex dialect per codebase, and a value truncated to whatever the printer showed. The recorder supplies the same attribution BY CONSTRUCTION: a getter knows which solver it belongs to, which method that solver resolved, and the full-precision values it is returning. This is the MATLAB twin of python/line_solver/result_recorder.py.

It is OFF unless asked for, and an ordinary run pays one appdata lookup per getter and nothing else – no object is even created:

LineResultRecorder.enable(); % in-process setenv(‘LINE_RECORD_RESULTS’,’1’); LineResultRecorder.autoEnable();

WHAT IS RECORDED. One entry per OUTERMOST getter call. SolverAUTO and the ensemble solvers call a member solver’s getter of the same name, and recording both would report the member where the example named the ensemble; the depth guard in ENTER/CAPTURE keeps the outermost only.

WHY THE STATE LIVES IN ROOT APPDATA. Fifteen of the examples in the corpus run clear all, which wipes globals and persistents alike. A buffer stored in either would come back empty half way through those runs and read downstream as a solver that produced nothing – a parity failure with no defect behind it. Root (handle 0) appdata survives clear all and close all force.

See also LINERESULTRECORDER.ENTER, LINERESULTRECORDER.CAPTURE.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Property Summary
depth

getter nesting depth; only depth 1 records

enabled

recording on/off

notes

non-table facts a run reported (refusals)

records

cell array of record structs

seq

call order, 0-based, as python’s recorder numbers

Method Summary
append(label, method, view, labels, rows, derived)

APPEND(SELF, …) Add one record to the buffer.

static asChar(value)

TEXT = ASCHAR(VALUE) A label cell as plain char.

static asDoubles(value)

OUT = ASDOUBLES(VALUE) Every number in a returned scalar, in order.

static asTable(tbl)

DATA = ASTABLE(TBL) The MATLAB table behind a returned result.

Handles the IndexedTable wrapper (which holds it on .data), a bare table, and anything else by declining.

static autoEnable()

TF = AUTOENABLE() Enable if LINE_RECORD_RESULTS is set to 1.

static capture(depth, solver, view, tbl)

CAPTURE(DEPTH, SOLVER, VIEW, TBL) Record one returned table.

DEPTH is ENTER’s first output; 0 (recording off) and any nested depth record nothing. VIEW names which table this is.

static captureScalar(depth, solver, quantity, value)

CAPTURESCALAR(DEPTH, SOLVER, QUANTITY, VALUE) Record a derived scalar.

Twenty-nine of the goldens hold a quantity no result table carries – a state probability, a workflow’s phase-type moments, a cache hit ratio. Where a library call returns the value, the same construction that records a table records it: the value, the call that produced it, and the solver it belongs to. ParityDerived is what maps a recorded scalar onto the key a particular golden uses, because THAT part is example-specific.

The value is flattened to a list of doubles because these getters return a scalar, a pair or an array depending on the solver; the consumer names the element its golden means. DEPTH is ENTER’s first output, so a delegating solver records once and not twice.

static disable()

DISABLE() Stop recording. The buffer is left intact.

static dump()

OUT = DUMP() Everything recorded, as a struct ready for JSONENCODE.

static enable()

ENABLE() Start recording, and drop anything recorded before.

static enter()

[DEPTH, GUARD] = ENTER() Open a getter’s recording scope.

DEPTH is 0 when recording is off, which makes CAPTURE a no-op and keeps an ordinary run free of everything below. GUARD is an ONCLEANUP whose destruction pops the depth, so it is popped on EVERY exit from the getter – including one that raises, which is how the guard survives an example that catches a solver error and carries on.

THE GUARD IS RETURNED SEPARATELY, not folded into a struct with the depth, because MATLAB does not promise to destroy an ONCLEANUP held inside a struct or a cell when the enclosing value goes out of scope. A depth left elevated would make every later getter in the same run look like a nested call and record nothing.

static instance()

R = INSTANCE() The session recorder, created on first use.

static isEnabled()

TF = ISENABLED() True when a getter should record.

Deliberately does NOT create the recorder: a run that never asked to record must not pay for an object it will never read.

static memberSolver(solver)

MEMBER = MEMBERSOLVER(SOLVER) The layer/stage solver an ensemble ran.

Getting this wrong is not a spelling difference – MVA layers and NC layers are different fixed points (lcq_threehosts: cache hit 0.5 against 0.48331). SolverLN, SolverENV and UQ all populate self.solvers while solving, so the member is read from the object that actually ran rather than guessed from the factory handle.

static note(text)

NOTE(TEXT) Record something that is not a table.

Used for a solver’s own refusal (‘this engine does not implement X’), which a consumer must be able to tell apart from a table that simply never arrived: the first is a fact about the port and is a named skip, the second is a failure.

static pop()

POP() Close one recording scope. Called by ENTER’s onCleanup.

static qualifiedLabel(solver)

LABEL = QUALIFIEDLABEL(SOLVER) ‘MVA:schmidt’ for a pinned AUTO, ‘’ else.

SolverAUTO SPLITS the method name it was given: RESOLVEMETHODTOKEN puts the family in SELECTIONMODE and the submethod in OPTIONS.METHOD, so the pair has to be re-joined here. Reading OPTIONS.METHOD alone would label the solve by a bare submethod (‘schmidt’), which names no family and matches no golden key.

reset()

RESET(SELF) Drop the buffer and the nesting depth.

static resetBuffer()

RESETBUFFER() Drop the buffer without changing the on/off flag.

static solverLabel(solver)

LABEL = SOLVERLABEL(SOLVER) The golden’s key for this solver.

A LAYERED or ENVIRONMENT solve is qualified by the member solver it drove – ‘LN(NC)’, ‘ENV(FLD)’ – because that is how several goldens spell it and because the two are genuinely different computations. The comparator still reconciles the qualified and bare spellings against the golden’s own key; recording the member is what gives it the evidence to do so safely.

static solverMethod(solver)

METHOD = SOLVERMETHOD(SOLVER) The method this solver resolved.

MAM(dec.source) and MAM(inap) differ by 236% on the same model, so the method is not decoration: it is what makes a recorded table attributable to the run the golden was generated from.

static tableRows(tbl, view)

[ROWS, LABELS] = TABLEROWS(TBL, VIEW) Rows of a returned table.

Values come out at FULL precision: quantizing to the golden’s printed precision is the comparator’s job, and doing it here would throw away the digits a future full-precision golden needs. A cell that will not convert stays a string, which is right for a label column and harmless for anything else – the comparator only reads the metrics its golden names.

static viewLabels(view)

NAMES = VIEWLABELS(VIEW) The two label columns a view declares.

Only consulted for a table whose columns carry none of the names in LABEL_COLUMNS, so an unlisted view still records correctly.

LQN2QN(lqn, replication)

LQN2QN Convert a LayeredNetwork (LQN) to a Network (QN) using REPLY signals

model = LQN2QN(lqn) flattens a LayeredNetwork into a single queueing network in which synchronous call blocking is represented by REPLY signals.

model = LQN2QN(lqn, replication) selects how task and processor replication is represented: ‘auto’ (default) materialises the replicas while the expansion stays within the instantiation budget and pools them otherwise, ‘materialize’ always materialises, ‘pool’ always pools.

Construction - One station per host processor (scheduling and multiplicity taken from

the processor). Tasks sharing a processor share the station, as in the LQN semantics where the processor is the contended resource.

  • One Delay per reference task, holding its think time.

  • One closed class per step of the expanded activity graph. A step is an activity, or one call stage of an activity that issues synchronous calls. Steps of the reference task chain carry population 0 except the think class, which carries the reference task multiplicity.

  • A synchronous call site blocks its caller: the step class has a REPLY signal bound to it (sn.syncreply), the token proceeds to the callee, and the callee’s replying activity class-switches to that signal, which returns to the caller station and unblocks it.

  • Call multiplicity mean m is unrolled into floor(m) mandatory call stages plus, if m is not integer, one further stage entered with probability m-floor(m). This preserves both the mean number of calls and the blocking of each individual call.

  • OR-branch and loop probabilities are read from the activity graph weights lsn.graph(a,b). Multiple entries per task and branching call trees are expanded, each call site receiving its own copy of the callee subgraph so that per-call-path response times remain distinguishable.

  • AND precedences become Fork and Join nodes: a POST_AND successor set is entered through a Fork and one Router per branch, since a Fork cannot switch class per output link, and the PRE_AND branch tails switch back to the class that entered the Fork, which is the class the Join matches its siblings on. A branch tail that issues a synchronous call is given a merge step, so that it reaches the Join in an ordinary class rather than as a REPLY signal, which carries no forked-task identity.

  • A CacheTask becomes a Cache node. The activity bound to an ItemEntry is the read step and sits on that node, carrying the item popularity and cardinality of the entry; its two CacheAccess successors become the hit and the miss class, and the class switch is performed by the Cache node itself, so the routes leaving it are written in the successor class.

  • An asynchronous call is lowered to a non-blocking visit: the caller does not hold its server for the duration of the call, but it is serialised behind it, since a closed network has no means of creating the second token that a truly concurrent send would require.

  • Entry forwarding splits the reply exits of the forwarding entry: with the forwarding probability the request is handed to the target entry, which replies to the original caller, so the forwarder is released while the caller stays blocked.

  • The multiplicity of a non-reference task is its thread pool: at most that many requests may be inside the task at once, where inside spans the task’s own steps and those of its nested callees, since a thread is held for the whole of a synchronous call. It is enforced by a finite capacity region with one linear admission constraint per task, and the calls of such a task do not hold the caller’s server, since the processor is released while a thread waits for a reply.

  • An entry with an open arrival process receives requests from a Source: they traverse the entry’s subgraph in open classes and leave through a Sink where the entry would reply. Calls on an open chain do not hold the caller’s server, since a REPLY signal is a closed class that cannot be woven into an open chain; the thread pools of the traversed tasks are still enforced by the finite capacity region.

  • Phase-2 activities, the successors of a replying activity, run after the reply: the replying step’s exit routes back to the caller, and each of its service completions spawns the continuation at the host station (sn.classspawn). The spawned token walks the phase-2 subgraph holding only the task’s own thread and is destroyed at the chain end, through a NEGATIVE signal that always misses on a closed chain, or through the Sink on an open one. A boundary that ends on a call site is normalised through a merge step at the host station; a phase 2 that opens with an AND-fork spawns into an immediate head that feeds the Fork; at an AND-join branch tail the spawned token inherits the fork identity of the trigger and stands in for it at the Join; at a cache read the reply is emitted by an immediate trigger step per hit/miss outcome, whose completion spawns the matching branch continuation.

  • An AND-join quorum k of n is applied to the Join node in the class that entered the Fork; k equal to the branch count is the default wait-for-all and is left alone. An activity think time becomes an extra step on a shared ActivityThink delay, in series with the host demand, so the task keeps its thread for it while its processor is released.

  • Replication is represented in one of two ways. Under materialisation each replica of a processor is a station of its own and each replica of a task carries its own copy of the expanded step graph, its own reply signals and its own admission row; a call from replica i of the caller reaches the fan-out block {(i*f+k) mod r} of the callee replicas and splits its call mean uniformly over them, which is deterministic pairing at f=1 and a uniform broadcast at f=r. Under pooling the replicas of a processor collapse into one station of r times the servers, a replicated thread pool into one admission row of r times the bound, and a replicated reference task into one class of r times the population; that is exact at an infinite-server host and optimistic elsewhere, since pooled servers share one queue while the replicas hold r separate ones.

  • A CacheTask with delayed-hit retrieval gets a retrieval system, which is an ordinary queueing network: one PS fetch station per cache replica, entered and left by the read class, with the Cache node coalescing concurrent misses of the same item. The fetch is what the miss branch does, so the miss activity’s host demand moves onto that station; calls issued by the miss activity stay outside the retrieval system and are warned.

  • A SetupTask carries its setup and delay-off times onto its host station as the Queue setup/delay-off pair, per step class: the server shuts down after the delay-off idle period and pays the setup on the next arrival. An infinite-server processor never shuts down, so the pair is dropped there with a warning, as is a setup with no delay-off time.

Not yet represented: retrieval on a cache read with phase-2 successors and the thread pool of a task with an internal AND-fork. Each is reported through line_warning.

Example

lqn = LayeredNetwork(‘MyLQN’); % … define LQN model … model = LQN2QN(lqn); SolverLDES(model).getAvgTable()

Copyright (c) 2012-2026, Imperial College London All rights reserved.

LQN2MATLAB(varargin)

LQN2MATLAB Generate the MATLAB script of a LayeredNetwork, or load a .lqnx file.

GENERATOR FORM (a LayeredNetwork as first argument):

LQN2MATLAB(MODEL) LQN2MATLAB(MODEL, MODELNAME) LQN2MATLAB(MODEL, MODELNAME, FID)

writes to FID a MATLAB script that rebuilds MODEL in a variable named model, by the same constructors a user would write (Processor, Task, CacheTask, SetupTask, Entry, ItemEntry, Activity, ActivityPrecedence). FID defaults to 1 (standard output); a char or string FID is a file name, which is opened for writing and closed on return. MODELNAME defaults to MODEL.getName(). The script reads the model OBJECTS, not the struct, so everything the objects carry is re-declared exactly: numeric values are printed at the shortest spelling that reads back bit for bit, and the hosts, tasks, entries and activities are declared in model order so the regenerated model has the same LayeredNetworkStruct indexing.

This is the layered twin of QN2MATLAB, and LQN2JAVA is its Java twin.

LOADER FORM (a file name as first argument), kept for backward compatibility; LayeredNetwork.parseXML(FILENAME) is the preferred spelling:

MODEL = LQN2MATLAB(FILENAME) MODEL = LQN2MATLAB(FILENAME, MODELNAME)

parses a .lqnx/.xml layered model (or SRVN text: a .in file, or a .lqn/.xml/.lqnx whose first non-blank character is not ‘<’, see srvn_is_file) and, when MODELNAME is given, renames it.

See also QN2MATLAB(), LQN2JAVA(), LINE2MATLAB(), LayeredNetwork.parseXML()

LQN2JAVA(MODEL, MODELNAME, FID)
class LineBackend

LINEBACKEND One interface over the engines a MATLAB solver can delegate to.

options.lang selects where a solve runs. ‘matlab’ is the solver’s own analyzer; every other value names a backend that solves the model elsewhere and hands back ONE standard result struct (LineBackend.avgResult), which LineBackend.store writes into the solver. Before this class each runAnalyzer carried its own copy of the bridge call, the M-by-R reshape and the result bookkeeping for every language it delegated to.

be = LineBackend.forLang(options.lang); % [] for lang=’matlab’ res = be.getAvg(solver, options, …); LineBackend.store(solver, res);

Backends: JavaBackend (JLINE, in process), PythonBackend (PYLINE, native line_solver in process), CppBackend (CPPLINE, line-cli subprocess).

Every OTHER engine-specific answer – transient tables, probabilities, sample paths, generators, CDFs, the layered (SolverLN) and random environment (SolverENV) ensembles – is a named method of the backend and is reached through ONE call at the head of the getter:

[served, out1, out2] = LineBackend.serve(solver, ‘ctmcGetTranProb’, node); if served, return; end

SERVE calls the backend method of that name when the solver’s backend defines it, and reports served=false otherwise, in which case the getter runs its own MATLAB code exactly as under lang=’matlab’ and SERVE warns the user that the requested engine did not answer. What a backend does not define is therefore the MATLAB answer, and what it defines either answers or refuses by name. Method names carry the solver family as a prefix (ctmc, ssa, nc, mva, mam, fld, jmt, ln, env) because one getter means different things on different solvers; unprefixed names (getTranAvg) are shared by every solver that reaches them. The list of methods a backend defines is the list of getters it serves.

WHERE a backend is dispatched is part of its contract, not a detail: python and cpp are served in runAnalyzerPreamble, ahead of the MATLAB-side gates, because the delegate applies its own (DISPATCHEARLY true). java is served from each solver’s own lang switch, after those gates, because several solvers resolve or refuse the method there first and the JAR result must answer the same question (SolverBA, SolverFLD, SolverSSA).

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Property Summary
dispatchEarly

true when runAnalyzerPreamble serves it

lang

the options.lang value this backend serves

servesGetAvg

true when @NetworkSolver/getAvg asks the backend

Method Summary
static avgResult(QN, UN, RN, TN, AN, WN, CN, XN, runtime, method, iter)

RES = AVGRESULT(QN,UN,RN,TN,AN,WN,CN,XN,RUNTIME,METHOD,ITER) The standard steady-state result. Station metrics are M-by-R, CN/XN are per chain or empty. Optional fields, empty when unset: lG (stored as Prob.logNormConstAggr) and CI (struct Q,U,R,T,A,W of M-by-R half-widths, stored through setAvgResultsCI).

static fallbackDepth(delta)

D = FALLBACKDEPTH(DELTA) number of MATLAB fallbacks currently open (see SERVE), after adding DELTA.

static forLang(lang)

BE = FORLANG(LANG) backend serving LANG, or [] for ‘matlab’.

static forSolver(solver)

BE = FORSOLVER(SOLVER) backend serving SOLVER, or [] when the solver answers natively. A solver whose engine is not chosen by options.lang (SolverLDES, SolverLN) defines lineBackend() itself.

static resolvedMethod(requested, resolved)

METHOD = RESOLVEDMETHOD(REQUESTED, RESOLVED) the banner label of a delegated solve: ‘default/<resolved>’ when ‘default’ was resolved by the engine, the engine’s own name otherwise, REQUESTED if it named none.

static serve(solver, method, varargin)

[SERVED, OUT1, …] = SERVE(SOLVER, METHOD, ARGS…) call BE.METHOD(SOLVER, ARGS…) on the solver’s backend when it defines METHOD. When it does not, SERVED is false, every output is [] and a warning tells the user that the MATLAB implementation answers. The outputs requested here are the ones the backend method must produce, so a getter asks for its full output list.

static setVerbose(solver, verbose)

SETVERBOSE(SOLVER, VERBOSE) restore SOLVER.options.verbose.

static store(solver, res)

STORE(SOLVER, RES) write the standard result into SOLVER. A delegated engine that printed its own banner (the JAR and native python do, with their own env) must not get a second one from here.

LINE2JLINE(line_model)

JAVA_MODEL = LINE2JLINE(LINE_MODEL) the jline mirror of a Network, JNetwork or LayeredNetwork

JLINE.line_to_jline forwards here, so this is the single implementation.

LINE2JAVA(MODEL, FILENAME)
LINE2MATLAB(model, filename)

MODEL = LINE2MATLAB(MODEL, FILENAME)

JSIM2LINE(filename, modelName)

MODEL = JSIM2LINE(FILENAME,MODELNAME)

JMVA2LINE(filename, modelName)

MODEL = JMVA2LINE(FILENAME,MODELNAME)

class JavaBackend

Bases: LineBackend

JAVABACKEND lang=’java’: the JAR solver of the same name, in process via JLINE.

RES = JavaBackend().getAvg(SOLVER, OPTIONS, Name, Value, …) converts the model with LINE2JLINE, builds JLINE.<Solver>(jmodel, OPTIONS), reads the unfiltered average table and returns the standard result, with the JLINE objects attached as RES.jsolver and RES.jmodel for any solver-specific read-back. Name-value options, all off by default:

‘Prepare’ @(jsolver) called before the solve (e.g. a warm start) ‘Cache’ ‘none’ | ‘full’ | ‘hitclass’ | ‘hitclass+extras’: copy

cache results back onto the MATLAB Cache nodes. ‘full’ carries hit, miss, delayed-hit, per-list hit, item and residence results; ‘hitclass’ carries hit and miss only, zeroed outside the node’s hit classes, as the MATLAB analyzers report them; ‘hitclass+extras’ is ‘hitclass’ plus the four retrieval-cache results

‘RefreshStruct’ refresh the MATLAB struct after the solve. It is also

refreshed whenever cache results were copied back

‘SysMetrics’ read CN/XN from getAvgSysRespT/getAvgSysTput ‘ConfInt’ read the per-station confidence half-widths into RES.CI ‘Tic’ a tic value: report toc(Tic) as the runtime instead of

the JAR’s own result.runtime

‘LogNormConst’ value stored as Prob.logNormConstAggr (RES.lG)

RES = JavaBackend().getAvgObj(SOLVER) is the other java entry point: the one @NetworkSolver/getAvg (and so getAvgTable) uses, which solves through the JLINE solver object SOLVER keeps in SOLVER.obj instead of building a fresh one, so the JAR-side state that object carries (the fork-join warm start) survives across solves. The remaining methods are the getters lang=’java’ answers through LineBackend.serve.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Property Summary
dispatchEarly
lang
servesGetAvg
Method Summary
getAvg(~, solver, options, varargin)
getAvgObj(~, solver)

RES = GETAVGOBJ(SELF, SOLVER) steady-state averages from the JLINE solver object SOLVER.obj, rebuilt over a fresh Java model on every call. RES is the standard result with the JAR’s own method and runtime and iter=1; RES.regions holds the Finite Capacity Region rows (fields Q,U,R,W,T,A, one F-by-R matrix each) that SolverJMT appends as pseudo-stations M+1..M+F, and is empty otherwise.

jmtGetProbAggr(~, solver, node, varargin)

The JAR’s own log-based estimator; see jmtGetProbSysAggr for why the model is rebuilt first and why the state crosses with the model. A state named in the CALL is passed through, since the JAR’s getProbAggr(Node, Matrix) takes one.

jmtGetProbSysAggr(~, solver)

The JAR’s OWN log-based estimator, which is the same estimator the MATLAB getter builds: jline SolverJMT does its own linkAndLog, runs jsim, parses the per-node CSVs back into a trajectory and weighs the time the declared state is held. setLang builds that solver on solver.obj, and the state crosses WITH THE MODEL (line_to_jline copies each node’s state), which is what the JAR matches against. The model is rebuilt first for the same reason getAvgObj rebuilds it: these getters are called after setState, so a stale Java copy would be matched against the wrong state.

jmtSampleAggr(~, solver, varargin)

THE TRAJECTORY, NOT THE PROBABILITY. getProbAggr IS served (see jmtGetProbAggr). What has no twin is sampleAggr’s RETURN VALUE, a MATLAB struct whose .state is one cell per class and whose .event carries Event objects; jline’s SampleResult keeps the state as one matrix and reports no events at all (“Event processing is unavailable from JMT output”), so a marshalled struct would be missing the field callers index and silently shaped like the real thing.

jmtSampleSysAggr(~, solver, varargin)

SAME AS jmtSampleAggr: getProbSysAggr IS served, the raw trajectory is not. jline’s SampleResult holds the whole system state in one station-major matrix and carries no event column, while sampleSysAggr returns one cell per station plus an event list.

lnGetEnsembleAvg(~, solver)

The JAR SolverLN built in the SolverLN constructor owns the layer ensemble in this mode.

lnGetSensitivityTable(~, solver, varargin)

Delegate to the JAR SolverLN and marshal its LayeredNetworkSensitivityTable back into the same MATLAB table and UserData that the native branch returns.

class JLINE

Bases: jlinebridge.Model, jlinebridge.Layered, jlinebridge.Process, jlinebridge.Matrix, jlinebridge.Solvers, jlinebridge.Result

JLINE Conversion utilities for JLINE format models

JLINE provides static methods to convert between LINE MATLAB models and JLINE Java models. This class serves as the primary interface for interoperability between the MATLAB and Java implementations of LINE.

@brief JLINE format conversion and Java interoperability utilities

Main functionality: - Convert LINE MATLAB models to JLINE Java models - Convert JLINE Java models back to LINE MATLAB format - Access JLINE solvers from MATLAB - Handle serialization between MATLAB and Java representations

Example: @code % Convert a LINE model to JLINE format jnetwork = JLINE.from_model(network); % Get a JLINE solver jssa = JLINE.get_solver(jnetwork, ‘ssa’); @endcode

THIS CLASS IS A FACADE. Its static methods live in +jlinebridge, one class per job (Model, Layered, Process, Matrix, Solvers, Result), all inherited here, so every JLINE.<method> call site resolves unchanged. Add a method to the part that does its job, never here.

JMT2LINE(filename, modelName)

MODEL = JMT2LINE(FILENAME,MODELNAME)

class CppBackend

Bases: LineBackend

CPPBACKEND lang=’cpp’: the C++ line-cli, as a subprocess via CPPLINE.

getAvg serves the steady-state averages from runAnalyzerPreamble. Every other method is a getter served through LineBackend.serve: the method list below IS what lang=’cpp’ answers beyond the averages, and a method that only calls CPPLINE.cppUnsupported is a getter the port refuses by name rather than one it leaves to MATLAB. A getter with no method here runs its MATLAB body under lang=’cpp’.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Property Summary
dispatchEarly
lang
servesGetAvg
Method Summary
ctmcGetAvgReward(~, solver)

-a reward. Only a reward built from a Reward.* template crosses the wire; a bare function handle has no serializable form and linemodel_save refuses to emit one, naming it.

ctmcGetCdfFirstPassT(~, solver, resolve, tset)

-a firstpasst. The state sets cross the boundary as STATE ROWS, not row indices: the C++ engine does not purge vanishing states, so the two enumerations need not agree row for row, while a state row resolves by content in either space. RESOLVE(n) returns the A, B and taboo C row indices against an n-state space, as the getter resolves them; TSET is the caller’s grid, empty for the default.

ctmcGetCdfSysRespT(~, solver)

The per-chain system law, computed in the SAME -a cdf payload as the per-station curves (solve_ctmc_cdf calls both solver_ctmc_cdf_respt and solver_ctmc_cdf_sys_respt off one tagged-chain solve).

ctmcGetFirstPassTMoments(~, solver, resolve, nmax)

-a firstpasstmom, with the state sets sent as STATE ROWS for the reason given in ctmcGetCdfFirstPassT.

ctmcGetGenerator(~, solver)

-a gen plus -a states, never the MATLAB state-space generator: returning MATLAB’s chain here would label a MATLAB enumeration as a C++ one, and every CTMC getter reads it.

ctmcGetProb(~, solver, node, varargin)

The Prob column of -a prob, the DETAILED marginal (phases and buffer arrangement included) of the state the model carries; a state prior over several rows is refused by name (CPPLINE.assertSingleState). A STATE NAMED IN THE CALL is refused too: -a prob takes no per-call state, so serving one would mean writing it onto the caller’s model first.

ctmcGetProbAggr(~, solver, ist)

-a prob; a state prior over several rows is refused by name.

ctmcGetSensitivity(~, solver, varargin)
ctmcGetSensitivityRanking(~, solver, varargin)
ctmcGetStateSpace(~, solver, nout)

-a states. The PER-NODE COLUMN WIDTHS ride with it (key NodeWidths), so the second output is sliced off the flat space exactly as the native getter slices it: one column block per stateful node, every row kept. Returning {stateSpace} instead – one cell holding the whole space – handed a caller indexing it per node the GLOBAL space for every node, and any caller reaching past the first cell (tut03_repairmen does) died on “Index exceeds the number of array elements”. NOUT is the getter’s nargout: only a caller asking for the local spaces needs the widths.

ctmcGetStateSpaceAggr(~, solver)

The aggregate space off the same -a states payload the space and the stationary law come from, so the three are one enumeration.

ctmcGetSymbolicGenerator(~, solver, varargin)
ctmcGetSymbolicSolution(~, solver, varargin)
ctmcGetTranProb(~, solver, node)
ctmcGetTranProbAggr(~, solver, node)
ctmcGetTranProbSys(~, solver)

A CHAIN HAS NO MODEL TO SERIALIZE: isChainSolver means the caller handed SolverCTMC a bare generator rather than a Network, and linemodel_save has nothing to write for it.

ctmcGetTranProbSysAggr(~, solver)
ctmcGetTranReward(~, solver, rewardName)

-a tranreward, which integrates E[r(X(t))] over the same horizon on the same generator.

envGetAvg(~, solver)

THE STAGE SOLVER IS NOT IN THE MODEL: model.json carries the stage Networks and the transition process, and the ensemble’s solver choice lives on the SolverENV object. An engine that defaulted to the fluid transient therefore answered a DIFFERENT model in silence – on renv_threestages_repairmen, whose stages are SolverCTMC, that returned Queue1 throughput 1.5577 against the 1.3333 the CTMC stages give. line-cli takes –stage-solver, and its mean-field coupling runs the enumerated CTMC as well as the fluid transient, so CPPLINE.getEnvAvg names the ensemble’s own. A MIXED ensemble is refused: one coupling runs one stage solver, so sending the first stage’s name would answer for the rest under it.

fldExportODEs(~, solver, filename, notation)

-a odes, with –notation reaching the C++ exporter so ‘matrix’ returns the matrix document and not the scalar one under another name. SYS is the structural description solver_fluid_symodes builds on the MATLAB side; the CLI sends the document only, so SYS is empty rather than reconstructed from another engine’s numbers.

fldGetCdfPT(~, solver, varargin)
fldGetCdfPassT(~, solver, varargin)
fldGetJacobian(~, solver, wantEquilibria)

-s fluid -a jacobian. Both sides are SYMBOLIC and reach the same computer-algebra backend: the C++ arm builds the drift with fluid_symodes and differentiates it there, as the MATLAB SAGE call does, so the entries are expressions on both paths. The equilibria only when the caller asks.

fldGetProbAggr(~, solver, varargin)

-a prob, the probability that a station holds the marginal population of the state the model carries; a prior over several rows is refused by name.

fldGetSymbolicDrift(~, solver, varargin)
fldGetTranAvgVar(~, solver, options)

-s fluid -a tranvar, the same kp fluid AND diffusion limits over the horizon the getter resolved. OPTIONS are those RESOLVED options, so both paths use the same slowest-rate rule when the caller left the timespan unbounded. QCOVT is not carried: line-cli reports the phase-layout second moments only.

fldGetTranCdfPassT(~, solver, varargin)
fldLoadAoI(~, solver)

Fill aoiResults from line-cli (-s fluid -a aoi, the same solve_mfq_aoi analysis); the getter then builds its answer from them in one place. The payload carries the (g,A,h) triples, not just a curve, so a caller-supplied t_values is still honoured.

getAvg(~, solver, options, varargin)

RES = GETAVG(SELF, SOLVER, OPTIONS) solve with line-cli -s <solver>; per-chain metrics are not carried.

getCdfRespT(~, solver)

The response-time law from -a cdf: CTMC’s tagged-chain law, and the fluid passage-time law as an APH fit to the passage time LSODA integrates. Each is the quantity the MATLAB getter builds, so the two are comparable curve for curve; recomputing it there would report a MATLAB integration as a C++ one.

getProbNormConstAggr(~, solver)
getProbSys(~, solver)

-a prob; a state prior over several rows is refused by name.

getProbSysAggr(~, solver)

-a prob; a state prior over several rows is refused by name.

getTranAvg(~, solver)

Store the transient tables of -s <solver> -a tran where the native path stores its own; @NetworkSolver/getTranAvg then wraps them into metricVal structs, so the wrapping has ONE implementation whichever engine produced the series.

-s ctmc -a tran is the transient MEANS, computed by the same analyzer that produces the occupancy and reported beside it since 2026-08-11. A state PRIOR over several rows is not refused either: solver_ctmc_transient_analyzer seeds analyzer_detail::init_state_distribution, the product of the declared per-node priors over the enumerated space, and integrates ONCE from that mixture – which is the reference’s weighted sum over the support, since every quantity it reports is linear in pi(t) and pi(t) in pi(0). See CPPLINE.assertSingleState for the arms that do still seed one state. The fluid port integrates the same ODE system over the same horizon, and the MAM port runs the same ldqbd Taylor-series analysis held to the same single-class precondition.

jmtGetProbAggr(~, solver, node, varargin)

-s jmt -a prob, which weighs ONE instrumented run: line-cli logs every station, resamples the traces onto a common grid and reports the time each station’s declared per-class counts are held for. That is the same estimator the MATLAB getter builds out of sampleAggr, and it is the C++ engine that ran the simulation, so the number is line-cli’s and not MATLAB’s. A state prior over several rows is refused by name (CPPLINE.assertSingleState).

jmtGetProbSysAggr(~, solver)
jmtSampleAggr(~, solver, varargin)

The trajectory is read back from the per-node CSV logs linkAndLog asks JMT to write, and line-cli’s -s jmt arm reports an AvgTable rather than writing them.

jmtSampleSysAggr(~, solver, varargin)

SAME REASON, AND IT IS NOT THE STATE. line-cli’s -s jmt arm neither writes the logs nor exports the logged copy’s Logger nodes, so the delegated runAnalyzer would simulate a model without them (and, on a four-class chain, fail outright). Pinning the inner solve to lang=’matlab’ would answer with a MATLAB trajectory under a lang that is an assertion about which engine produced it, so the limit is named here.

lnGetEnsembleAvg(~, solver)

ONE line-cli process solves the whole layered ensemble.

lnGetSensitivityTable(~, ~, varargin)
mamGetCdfRespT(~, solver)

-s mam -a cdf, the same MMAPPH1-family passage-time analysis solver_mam_passage_time runs.

mamGetProb(~, solver, node, state)
mamGetProbMarg(~, solver, ist, jobclass, state_m)
mamGetTranAvg(solver)

MAM keeps its own entry rather than the shared getTranAvg, since under any other lang its transient is the MATLAB ldqbd analysis.

mvaGetProbAggr(~, solver, ist)
mvaGetProbMarg(~, solver, ist, jobclass, state_m)
mvaGetProbSysAggr(~, solver)
ncGetCdfRespT(~, solver)

The sojourn law from line-cli (-s nc -a cdf), on the same logarithmic time grid the MATLAB getter uses. A model with NO FCFS station carries no tagged-job law at all, and the MATLAB getter then returns {} rather than a grid of empties. line-cli warns and sends no curve, which the shared reader turns into an (nstations x nclasses) cell of empties, so the shape is restated here: a caller testing isempty(RD) would otherwise read “no law” as “a law everywhere, all of it missing”.

ncGetProb(~, solver, node, varargin)

The state, when one is given, crosses as –node/–state and is decoded there with the port’s to_marginal, which is the reference’s own sn.state{ist} = state followed by State.toMarginal. Without one the model’s declared state stands, and that already travels in model.json.

ncGetProbAggr(~, solver, node, varargin)

ONLY FOR THE MODEL’S OWN STATE. -a prob reports every station’s marginal at the state model.json carries; a state_a named in the CALL would have to be written onto the node first, which would edit the caller’s model to ask a question about it, so it is refused by name.

ncGetProbMarg(~, solver, node)
ncGetProbSys(~, solver)
ncGetProbSysAggr(solver)
ncGetProbSysMarg(~, solver, nvec, engine)
sample(~, solver, node, numEvents)

The trajectory is the C++ engine’s; a markActivePassive rearrangement of the event cell applies to it unchanged.

sampleAggr(~, solver, node, numEvents)
sampleSys(~, solver, numEvents)

A chain given directly to SolverCTMC is not a Network, so linemodel_save has nothing to write for it; only the network case routes.

sampleSysAggr(~, solver, numEvents)
ssaGetCdfRespT(~, solver, varargin)
ssaGetProb(~, solver, node, varargin)

The Prob column of -a prob, the DETAILED marginal at the state the model carries.

ssaGetProbAggr(~, solver, node, varargin)

-a prob, the simulated occupancy of the state the model carries.

ssaGetProbSys(solver)
ssaGetProbSysAggr(solver)
class Copyable

Bases: handle

Copyable allows to perform deep-copy of objects via the copy() method.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

Method Summary
copy()

NEWOBJ = COPY(OBJ)

class CPPLINE

Bases: cppbridge.Transport, cppbridge.Options, cppbridge.Result, cppbridge.Analysis

CPPLINE MATLAB-to-C++ bridge (lang=’cpp’).

Static helpers that serialize a LINE model to the canonical model.json interchange (linemodel_save), run the C++ solver binary (line-cli) as a subprocess, and marshal the JSON answer back into MATLAB matrices. Mirrors PYLINE.m (lang=’python’) and the JLINE.m dispatch (lang=’java’), with the transport being subprocess + JSON rather than py.* or the JVM. It is the MATLAB twin of python/line_solver/solvers/cpp_dispatch.py and follows the same rules, flag for flag.

WHAT FALLS BACK AND WHAT DOES NOT. lang=’cpp’ is an assertion about what produced the numbers, so this bridge never substitutes another engine: a missing binary, a construct the C++ analyzer refuses, a non-zero exit or unparseable output are all errors. Silently answering from the MATLAB solver would hide exactly the class of defect cross-language comparison exists to find.

LayeredNetwork models reach the C++ layered solver through the .lqnx interchange (writeXML), which is the LQNS file format and covers every construct that reader carries, including fan-in/fan-out replication. A model whose think time sits on a non-reference task cannot be written as .lqnx at all, so it goes over model.json (linemodel_save) instead.

Copyright (c) 2012-2026, Imperial College London All rights reserved.

THIS CLASS IS A FACADE. Its static methods live in +cppbridge, one class per job (Transport, Options, Result, Analysis), all inherited here, so every CPPLINE.<method> call site resolves unchanged. Add a method to the part that does its job, never here.

Property Summary
CLI_PSTAR

The exponent line-cli smooths min(n_i, S_i) with under –method pnorm: FluidOptions::pstar in cpp/include/line/solvers/fluid/solver_fluid.h. The CLI exposes no flag for it, so it is the ONLY exponent this bridge can ask for, and pnormMethod refuses any other rather than answering at this one.

class IndexedTable

Bases: handle

INDEXEDTABLE Generic enhanced table wrapper with flexible object-based filtering

Wraps any MATLAB table to enable filtering using Station, Node, JobClass, and/or Chain objects, while maintaining full backward compatibility with standard table operations.

Supports all NetworkSolver result tables: - AvgTable (Station + JobClass) - AvgNodeTable (Node + JobClass) - AvgChainTable (Station + Chain) - AvgNodeChainTable (Node + Chain) - AvgSysTable (Chain only) - Any custom table with Station/Node/JobClass/Chain columns

Usage:

% Create from any MATLAB table t_native = solver.avgTable(); t = IndexedTable(t_native);

% Standard table operations (works as normal) rows = t.data(1:5, :) value = t.data.QLen(1)

% Object-based filtering (four equivalent syntaxes) rows = t(station_obj) % Direct indexing rows = t.filterBy(station_obj) % filterBy method rows = t.get(station_obj) % get alias (shorthand) rows = t.tget(station_obj) % tget alias

% Filter by class object rows = t(class_obj) rows = t.filterBy(class_obj) rows = t.get(class_obj) rows = t.tget(class_obj)

% Filter by both station/node and class/chain (any order) rows = t(station_obj, class_obj) rows = t.filterBy(station_obj, class_obj) rows = t.get(station_obj, class_obj) rows = t.tget(station_obj, class_obj)

Example (AvgTable):

solver = SolverJMT(model); solver.runAnalyzer(); avgTable = IndexedTable(solver.avgTable()); metrics = avgTable(queue, jobclass);

Example (AvgNodeTable):

nodeTable = IndexedTable(solver.avgNodeTable()); metrics = nodeTable(sink, jobclass);

Example (AvgChainTable):

chainTable = IndexedTable(solver.avgChainTable()); metrics = chainTable(queue, chain);

Example (AvgSysTable):

sysTable = IndexedTable(solver.avgSysTable()); metrics = sysTable(chain);

Constructor Summary
IndexedTable(table_data)

INDEXEDTABLE(table_data) Create wrapper around any MATLAB table

Parameters:

table_data – A MATLAB table object with object-filterable columns

Property Summary
data

Underlying MATLAB table

Method Summary
disp()

DISP Display the table

filterBy(varargin)

FILTERBY Filter table by Station/Node and/or JobClass/Chain objects

Intelligently filters based on table structure and object types

Usage:

rows = t.filterBy(station_obj) rows = t.filterBy(class_obj) rows = t.filterBy(station_obj, class_obj)

Parameters:

varargin – 1 or 2 arguments - Station/Node/JobClass/Chain objects

Returns:

result – Filtered MATLAB table

get(varargin)

GET Alias for filterBy - convenient shorthand

getTable()

GETTABLE Return the native MATLAB table

head(n)

HEAD Get first n rows

height()

HEIGHT Get number of rows

numArgumentsFromSubscript(s, indexingContext)

NUMARGUMENTSFROMSUBSCRIPT Specify number of outputs for indexing This tells MATLAB to always expect exactly 1 output from subsref operations, which is appropriate for table data access.

numel(varargin)

NUMEL Support numel() function When called with extra arguments (during indexing like obj(a,b)), return 1 to tell MATLAB we expect a single output from subsref. When called directly as numel(obj), return actual element count.

size(varargin)

SIZE Support size() function

subsref(s)

SUBSREF Support both standard indexing and object-based filtering

Intelligently detects object types and filters appropriate columns

summary()

SUMMARY Display table summary

table2array()

TABLE2ARRAY Convert IndexedTable to array Delegates to the underlying MATLAB table

tail(n)

TAIL Get last n rows

tget(varargin)

TGET Alias for filterBy - backward compatibility

varfun(func, obj, varargin)

VARFUN Apply function to table variables Delegates to the underlying MATLAB table

width()

WIDTH Get number of columns