LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
line::ldes Namespace Reference

Namespaces

namespace  engine
namespace  ssj

Classes

struct  LdesCacheMetrics
 Per-cache hit/miss/latency, as the cacheMetrics block carries them. More...
struct  LdesOptions
 The knobs of one LDES run. More...
struct  LdesResult
 One ldes-result document, parsed. More...
struct  LdesRunner
 One runner: the argv prefix that runs the engine, and the name of its image. More...
struct  LdesStateQuery
 One constraint of a joint-state query: a station and the per-class job counts it is required to hold. More...

Functions

template<class T>
LdesResult ldes_engine_solve_one (const qn::NetworkStruct< T > &sn, const LdesOptions &o)
 Simulate sn in process and return the same record the subprocess client parses out of an ldes-result document.
template<class T>
LdesResult ldes_engine_solve (const qn::NetworkStruct< T > &sn, const LdesOptions &o)
 Simulate sn, over independent REPLICATIONS when options.replications > 1.
template<class T>
engine::LnResult ldes_ln_engine_solve (const lqn::LqnStruct< T > &lsn, const LdesOptions &o)
 Simulate a layered model in process.
std::vector< std::string > list_valid_methods ()
 Port of SolverLDES.listValidMethods.
const std::string & ldes_engine_dir ()
 The directory holding the engine, or empty when there is none.
bool ldes_is_available ()
 True when this machine can run the engine at all, by either image.
std::vector< std::string > ldes_flags (const LdesOptions &o, const std::vector< std::string > &extra)
 The engine flags of one run, after solve <model> -o <result>.
std::vector< LdesRunnerldes_runners (const std::string &doc, const std::vector< std::string > &flags)
 The runners to try, in order (see the file header for why there are two and when the order flips).
detail::Json ldes_solve_rest (const std::string &base_url, const std::string &doc, const std::vector< std::string > &flags, double timeout)
 Solves through an LDES REST server and returns its result document.
LdesResult parse_ldes_result (const detail::Json &d)
 Parses one ldes-result document.
LdesResult solver_ldes_text (const std::string &doc, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >())
 Runs one LDES simulation on a model.json DOCUMENT and parses its result.
LdesResult solver_ldes_file (const std::string &path, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >())
 The same, reading the document from a file.
double ldes_prob_from_histogram (const LdesResult &r, std::size_t nclasses, const std::vector< LdesStateQuery > &query)
 Residence-time probability of an aggregate joint state, from a parsed result.
Matrix< double > ldes_cdf_respt (const LdesResult &r, std::size_t station, std::size_t job_class)
 Port of getCdfRespT: the EMPIRICAL response time CDF of one (station, class) pair, built from the per-job samples the engine exports under --respt-samples.
double ldes_prob_aggr (const std::string &doc, const LdesOptions &o, std::size_t station, const std::vector< double > &counts, std::size_t nclasses)
 Port of getProbAggr: the marginal probability of a per-class job count at one station.
double ldes_prob_sys_aggr (const std::string &doc, const LdesOptions &o, const Matrix< double > &target)
 Port of getProbSysAggr: the joint probability of a whole aggregate state.
template<class T>
LdesResult solver_ldes (const qn::NetworkStruct< T > &sn, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >())
 The same, for a model built through the C++ API.

Function Documentation

◆ ldes_cdf_respt()

Matrix< double > line::ldes::ldes_cdf_respt ( const LdesResult & r,
std::size_t station,
std::size_t job_class )
inline

Port of getCdfRespT: the EMPIRICAL response time CDF of one (station, class) pair, built from the per-job samples the engine exports under --respt-samples.

The returned matrix is (n x 2) with columns [F(t), t], NOT [t, F(t)]. That order is the convention every getCdfRespT follows across the codebases (MATLAB @SolverLDES/getCdfRespT.m, Python SolverJMT.getCdfRespT), and it is the REVERSE of the transient getTranCdfRespT, which is a different method with a different contract. Swapping them yields a CDF that reads as a time axis and vice versa, with no error anywhere.

A pair the run observed nothing at returns an EMPTY matrix rather than a fabricated law: an analytical fallback carrying the right mean says nothing about the tail, which is the whole reason to ask a simulator for a CDF. Repeated observations are collapsed, keeping the largest CDF value at each distinct time, or the ecdf is multivalued and an interpolating consumer reads a quantile off whichever duplicate it happens to hit.

Definition at line 775 of file solver_ldes.h.

References ldes_cdf_respt(), and line::ldes::LdesResult::respTimeSamples.

Referenced by ldes_cdf_respt().

◆ ldes_engine_dir()

const std::string & line::ldes::ldes_engine_dir ( )
inline

The directory holding the engine, or empty when there is none.

Definition at line 239 of file ldes_probe.h.

References ldes_engine_dir().

Referenced by ldes_engine_dir(), ldes_is_available(), and ldes_runners().

◆ ldes_engine_solve()

template<class T>
LdesResult line::ldes::ldes_engine_solve ( const qn::NetworkStruct< T > & sn,
const LdesOptions & o )

Simulate sn, over independent REPLICATIONS when options.replications > 1.

Each replication is one path with its OWN seed, seed + r, exactly as Solver_ldes_analyzer_parallel derives them. That is what makes the paths independent while keeping the whole run reproducible; sharing a seed would make every replication identical and collapse the interval to zero, and drawing them randomly would make the run irreproducible.

THE INTERVAL COMES FROM THE SPREAD BETWEEN REPLICATIONS, not from within a path, and it is the honest one: independent paths need no assumption about the correlation structure that batch means has to model. The cost is that replications - 1 degrees of freedom is few, so the t critical value is large and the interval wide – which is the estimate, not a defect of it.

A single replication yields NO interval rather than a zero-width one: with one path there is no spread to measure, and reporting zero would read as perfect precision.

Definition at line 4897 of file ldes_engine.h.

References line::ldes::LdesOptions::cimethod, line::ldes::LdesResult::CN, line::ldes::LdesOptions::confint, ldes_engine_solve(), ldes_engine_solve_one(), line::ldes::LdesResult::nclasses, line::ldes::LdesResult::nstations, line::ldes::LdesResult::QN, line::ldes::LdesResult::QNCI, line::ldes::LdesOptions::replications, line::ldes::LdesResult::RN, line::ldes::LdesResult::RNCI, line::ldes::LdesOptions::seed, line::ldes::LdesResult::TN, line::ldes::LdesResult::TNCI, line::ldes::LdesResult::total_simulated_events, line::ldes::LdesResult::UN, line::ldes::LdesResult::UNCI, and line::ldes::LdesResult::XN.

Referenced by ldes_engine_solve(), and main().

◆ ldes_engine_solve_one()

template<class T>
LdesResult line::ldes::ldes_engine_solve_one ( const qn::NetworkStruct< T > & sn,
const LdesOptions & o )

Simulate sn in process and return the same record the subprocess client parses out of an ldes-result document.

A metric the run did not measure stays EMPTY rather than becoming a matrix of zeros: zero is a measurement and absence is not.

Snap a sampled duration onto the slot lattice, or REFUSE it.

A non-lattice sample is an ERROR, not something to round: rounding silently changes the distribution, and a model whose service law is not lattice-valued is not a discrete-time model. The reference refuses for the same reason. Only Geometric, Det with an integral slot count, and DMAP produce lattice values.

ONE STREAM PER (node, class), with the reference's own offsets.

Solver_ssj never shares a stream between two generators: it builds one MRG32k3a and one java.util.Random per (node, class) and seeds them from seed + offset, with

arrival (source s, class k)   stream (s*K + k)*10,  random the same + 2000
service (svc j, class k)      both ((numSources + j)*K + k)*10 + 1000
routing                       ROUTING_SEED_BASE = 900000

where numSources counts SOURCE nodes and j indexes the SERVICE nodes in order, neither of which is the station index. Sharing three run-wide streams instead, as this engine did, changes the interleaving of every draw: two stations that each consume their own stream in the reference would take turns from one here, so the paths diverge from the first event even with identical generators and identical quantiles.

The station-level draws that are not per-class – a polling switchover, a setup or delay-off, an SPN transition – sit on g_aux[station] in a band of their own, mirroring the reference's own (numSources + numServiceNodes + j) family without claiming to match it draw for draw.

< [class], at the (single) source

< [station][class]

< [station], the draws that carry no class

True when a fork's parameters actually vary: some branch is uncertain, some link carries a count other than the scalar, or some link draws its count. A fork that declares the matrices but leaves them uniform stays on the fixed path, so it keeps drawing nothing.

One draw from a jobs-per-link distribution, by inverse CDF on its pmf.

ROUTING IS RESOLVED OVER NODES, not over stations.

A Router, a ClassSwitch and a Logger appear here as destinations: the stochastic complement that removes them lives in rt, not in the NODE-level Peff this table is read off. They hold nothing, so deliver walks straight through them. A FORK and a JOIN are different again: they are stateful, they change the NUMBER of jobs in flight, and no stochastic complement can express that, so deliver replicates at a Fork and synchronizes at a Join.

The dispatcher each (node, class) declares, and its round-robin pointer.

The pointer is per (node, class) and NOT per destination: a round-robin dispatcher hands out its outgoing links in turn, and one counter per link would let two classes at the same node walk the cycle independently and both start at the head.

One forked parent awaiting its siblings.

required is fixed at FORK time, not at join time: the quorum is a fraction of the siblings THIS fork produced, and a Join shared by two forks of different fan-out would otherwise apply one fork's count to the other's siblings.

(class, instant) of every sibling this parent has parked at the Join.

Held PER PARENT, not per Join: the Join's queue length must fall by the siblings THIS synchronization releases, and a node-wide tally would clear the siblings of every other parent still waiting. It also dates each sibling's own wait, which is forkedJobJoinArrivalTimes.

Every per-visit response time, when either flag asked for them.

–trajectory counts as asking: the reference NESTS the same samples inside its transient block, and MATLAB's sample() and sampleSys() read them from there. Recording only under –respt-samples left those two returning nothing on the transient path.

G-NETWORK REMOVAL SIGNALS, resolved once per class.

A removal signal is a class like any other while it is in transit and stops being one the instant it reaches a station: it never joins, it removes jobs already there and it is annihilated. is_removal_signal is the whole gate, and everything the removal costs – the registry of jobs held at a Delay station, the extra draws – is paid only where it is true, so a model without signals runs exactly as before.

The jobs a Delay station currently holds, in arrival order.

A Delay keeps no buffer and no server: its occupants live only in the departure events already scheduled for them, and the event list has no handle to cancel one. The registry is that handle – a job removed by a signal is erased from it, and the departure that arrives later finds no entry and is discarded, which is the same tag trick the buffered stations use against a preempted departure.

SYNCHRONOUS CALLS, sn.syncreply: the class a caller expects its reply in.

A caller that expects one KEEPS ITS SERVER when it leaves for the callee and gets it back only when the matching REPLY class returns, which is the whole difference between a synchronous call and an ordinary hop. The pending calls are keyed by an identity that rides on the job, because the reply comes back from a station the caller never names.

One outstanding call: the slot it holds and the class that holds it.

Whether any (station, class) declares immediate feedback at all.

SPAWN ON COMPLETION, sn.classspawn: completing a job of class r injects a FRESH job of the mapped class at the same station.

It is an LQN phase-2 continuation: the work the server owes after the reply has gone back. The continuation is a new passage, so it starts its own response-time clock and is not the completing job under another name.

Push onto, and pop from, a station's waiting room.

FSP takes the linear branch: its key is recomputed against the residual work present at the station RIGHT NOW, over the waiting jobs and the ones in service alike, so no stamped ordering can stand in for it. Every other discipline has a per-job constant key and rides the heap.

Free, unblocked, unheld, and its pool accepts cls.

The free slot a job of class cls may enter, or s.nservers for none.

The POOL is chosen first, by the station's heterogeneous scheduling policy, and the slot is then the first free one inside it. That is the reference's order and it matters: picking the globally-first free slot would make every policy behave as ORDER.

Is any waiting job servable by slot sl?

Pop the job slot sl should serve next.

Off a pooled station this IS buffer_pop. On one it is the job the station's own order would have taken FIRST AMONG THOSE THE SLOT CAN SERVE, which is what a pool means: a server does not skip the queue, it skips the jobs it is not compatible with. The heap is drained and rebuilt because the discipline's order lives in the comparator, not in the sequence, so scanning the vector would answer in heap order rather than in service order.

The RATE multiplier a station applies to a class at its current population: load dependence times class dependence.

A sampled requirement is DIVIDED by this, so a multiplier above one is a faster station. lldscaling is indexed by the total population minus one, saturating at the table's end, which is the reference's convention and not a guard: the table is declared up to the station's capacity and a closed model can hold exactly that many.

A SHARING station does NOT take the load-dependent factor here. Its lldscaling is read instead as the effective SERVER COUNT in ps_shares, because c servers and a c-fold rate are the same thing only when the capacity is shared – applying both would scale it twice.

The effective server count of a SHARING station, which is where its load-dependence table is read. Transcribes getEffectivePSServerCount: with no jobs present the count is 1, not the table's first entry, so the very first arrival gets a whole server.

Integrate the work delivered at a state-dependent station since its last change, then re-time every completion it holds.

sd_advance must run BEFORE the population is written and sd_reschedule after, because the rate in force over the elapsed interval is the one the OLD population implied. The stale departures are neutralised by the tag, exactly as a preemption's are.

Whether station j can admit one more job of class r RIGHT NOW, counting the jobs already blocked in front of it.

THE CAPACITY TEST EXCLUDES THE BLOCKED JOBS, and reporting includes them. acc.qlen carries both, because the reference's effectiveQueueLength adds basBlockedAtDest/bbsBlockedAtDest for the statistics, while destinationHasCapacity reads getTotalCustomersAtStation – the jobs actually AT the station. Testing against the inclusive count DEADLOCKS a station of capacity one: the job blocked in front of it occupies the very slot it is waiting for, so the station can never make room and the upstream server is never released.

The blocking policy the DESTINATION declares for an arriving class.

Jobs parked by a WAITQ region: they have NOT entered the region, so they are outside every station's buffer, but they are still in the system and are charged to the station they were trying to enter.

Interrupt the job in slot and return it to the waiting room.

Its residual work and attained service are carried on the job, so a resume needs no side table; the tag is left stale, which is how the departure event already in the list is neutralised.

Advance a polling server one leg, or park it.

poll_serve starts the next job at the buffer the server stands at; poll_advance walks it to the next buffer, paying that leg's switchover. The walk stops after a full lap of zero-time legs that found no work, or the server would spin forever at the same simulated instant.

Reschedule the aggregate completion of a pass-and-swap station.

ONE CLOCK FOR THE WHOLE STATION, at the total rate mu(c) of its ordered list, and the position that completes is drawn from the per-position INCREMENTS. Scheduling one clock per job instead would need each job's marginal rate to be constant, which is exactly what an order-independent station does not have: every arrival and departure changes every other job's rate.

The position that completes, drawn from the per-position rate increments, and the job that actually departs after the swap chain.

Admit a job at station i, or drop it when the station is full.

from is the station the job comes from, or M for an arrival that enters from outside the network. IT IS NOT BOOKKEEPING: a hop BETWEEN TWO MEMBERS of the same finite capacity region does not cross the region boundary, so the job must neither be re-tested against the region's caps nor counted into it again. Without that test an intra-region hop adds one to the region on arrival and removes nothing on departure, so occupancy grows by one per hop until every arrival is refused and the run never completes another job.

Every index of tab, so JSQ can be told to consider all of them.

PROB: one draw against the static row.

The mass is NOT renormalized before the draw. The reference scales the uniform by the row total instead, so a row summing to one only up to rounding still lands, and the two engines consume the routing stream identically.

Eq. (10) of Krzesinski (1987) at the CURRENT marking: the split out of the entry centre into the branch entries, plus the residual that returns the customer to the departure centre.

A Source is reported as zero rather than as the infinite reservoir its encoding carries, which is the station_populations rule and the one the enumerating solvers use, so the sample path here has an exact counterpart in SolverNC rather than merely a similar one.

The candidate holding the FEWEST jobs, ties broken uniformly.

A destination that is not a service station holds nothing to compare, so it is skipped rather than counted as empty – a Sink would otherwise win every comparison and absorb the whole stream. When no candidate is a station the first one is taken, which is the reference's fallback.

One hop out of node inode in class r: the DISPATCHER picks the node, the class-switch row picks the class.

NEITHER STAGE DRAWS WHEN ITS OUTCOME IS DETERMINED. A single destination and a single arriving class each cost nothing, which is the reference's rule and not an optimization: a routing stream that advanced on a forced hop would put every subsequent draw out of step with the Java engine and make a seeded run of a tandem irreproducible across the two.

The class a job routed out of node under cls will ARRIVE the true destination station under, resolving through any ClassSwitch/Router/ Logger chain draw_node_route itself stops one hop short of.

draw_node_route(i, r).cls names the class on the FIRST edge out of a station, and when link() synthesized a ClassSwitch node for that edge (any route whose class differs from station to station), that first edge's class is the PRE-switch one – the switch happens INSIDE the synthesized node, one hop later. The synchronous-call park check below needs the class the job actually lands under, or a callee's own reply (its edge into the synthesized node still reads as the CALL class) is misread as a brand-new call: the reply's job.call is then overwritten with a fresh id, the caller's real call is never answered, and its server stays held while the buffer behind it grows without bound.

Consumes no extra draw when every hop is forced, which a link()- synthesized pass-through node always is (draw_node_route never spends a draw on a single-destination table), so the routing stream stays in step with a caller that already resolved through this same node.

Hand on the jobs blocked in front of j, oldest first, now that it has room.

OLDEST FIRST is the reference's rule and it matters: releasing the newest would starve a server that has been blocked longest, which no blocking discipline does and which shows up as an unbounded blocked time at one station while another cycles freely.

Let the jobs parked by region rg in, oldest first, now that it has room.

A waiter re-enters through admit, which re-tests every constraint: the region may have freed one slot while the waiter's own class cap or the linear constraints still bind, and admitting on the global count alone would breach them.

The jobs station i actually HOLDS, waiting and in service alike.

Remove ONE job from station i under policy, returning its class or K when the station holds nothing.

THE POLICY IS TWO-TIER, as in the reference: FCFS and LCFS rank the WAITING line by age and only reach into the servers once nobody waits, while RANDOM draws uniformly over waiting and in-service jobs together. A station whose occupants are all in service – Delay, PS, PAS – has no waiting line for the age tiers to rank, so there the three policies differ only in which of the equally-served jobs is taken.

The victim is drawn WITHOUT REGARD TO CLASS. That is Gelenbe's negative customer and what the Java engine and SolverMAM both do; sn.signaltarget narrows the eligible class only in the state-space solvers, whose state carries the per-class composition this one draws over directly.

A G-NETWORK REMOVAL SIGNAL arriving at station i, and annihilated there.

The signal NEVER JOINS the station and never continues along its routing chain: it removes a batch of the jobs it finds, and one that finds the station empty is simply lost. That is Gelenbe's semantics and the one State.afterEventStationSignal and the Java engine both implement; routing the signal onward instead would make a single signal fire once per downstream station, which is what the tandem regression measures.

A CATASTROPHE removes EVERY job held, in-service ones included, and ignores the batch-size law entirely – removing all of them is what the word means. A NEGATIVE signal draws its batch size from signalremdist (absent: exactly one) and CLIPS it at what the station holds, so an oversized batch drains the station instead of driving it negative.

The completion budget, spent by anything the model actually DOES.

Declared ahead of deliver because a service completion is not the only thing that spends it: a Petri net has no services of its own and counts a FIRING, and a cache-only model (Source -> Cache -> Sink) has none either and counts the cache's delivery to the Sink. Without the latter the loop at the bottom of this function never terminates on such a model, since the Source keeps the event queue non-empty forever. Solver_ssj spends the budget at the same point (deliverCacheCompletion calls checkEventCountStop on the sink branch), so this is the reference's rule and not a local convention.

Free every request parked while item was being fetched.

Declared ahead of deliver and assigned after it because the two are mutually recursive: a release routes each freed request onward, and a delivery to a cache is what triggers a release. Each freed request reads the now-cached item and completes at once, counted as a DELAYED HIT and leaving under its own hit class; its original t_sys is preserved so the response time still spans the wait it actually served.

Hand a job to a NODE, replicating at a Fork and synchronizing at a Join.

A FORK sends a sibling down EVERY outgoing edge, not one drawn from the routing probabilities: the edges of a fork are branches taken together, and drawing among them would turn a fork into a probabilistic split with the same picture and a completely different response time. tasksPerLink multiplies each branch.

A JOIN releases ONE job once its strategy is satisfied and DISCARDS the siblings that arrive afterwards – under a quorum they are the losers of the race, and counting them as completions would inflate the throughput of everything downstream.

A REPLY ANSWERS ITS CALL AND TRAVELS ON. It releases the server the caller has been holding, lets that station take its next waiting job, and then CONTINUES along its own routing – which is what separates it from a removal signal, annihilated where it lands.

A reply with no matching call is not an error: the call may have been answered already, or the class may be routed as a reply without any caller having parked. It routes on, having released nothing.

The marking as one flat vector over (place slot, class), p * K + r, which is what a mode's arcs are indexed by.

SUMMED OVER CLASSES UNTIL 2026-08-12, which made every arc colourless and let a token of one class satisfy another's pre-arc.

Fire every enabled IMMEDIATE mode, then arm the timed ones.

The immediate modes are exhausted FIRST and to a fixed point: a marking that enables one is not a marking the net rests in, so letting a timed mode fire from it would visit a state the model does not have. The guard bounds a net whose immediate modes form a cycle, which is a modelling error rather than something to simulate forever.

WARM START, --initsol: the placement a companion solver's steady state implies, as a STATION-MAJOR vector [st0_cl0, ..., stM-1_clK-1].

It REPLACES the reference-station placement of the closed classes; the open arrival streams are armed either way. A Petri net is exempt: its tokens live in Places and not in the service queues this vector describes, so a station-based placement would put nothing anywhere and then fail the conservation check.

CLASSES DESCEND WITHIN A STATION. Every initial job arrives at time 0, so under an order-preserving discipline the LAST one injected holds the server; ascending order puts the highest-index class in service, which is the mirror of what State.initDefault encodes and selects the other closed communicating class on a chain made reducible by non-overtaking routing – a different stationary distribution, not a different tie-break.

A TRANSIENT run is bounded by SIMULATED TIME, not by completions.

options.timespan = [t0, t1] sets the horizon and the completion budget is then ignored, exactly as the reference does: a transient trajectory is a function of time, so stopping on an event count would end it at a different instant on every path and make the series incomparable across seeds.

The EXACT joint-state residence time, keyed by the aggregate state row.

histogram_space plus histogram_time let a caller evaluate its own reward on the empirical distribution, INCLUDING a nonlinear one: E[r] = sum_s (t_s / sum t) r(state_s) is exact only because the residence time of each distinct state is kept, which a trajectory of means cannot reconstruct.

IMMEDIATE FEEDBACK: the completing job goes straight back into THE SAME SERVER under the destination class, holding the slot instead of re-queueing behind whoever is waiting.

That is the whole content of the feature and it is not the same model as a self-loop: a self-loop puts the job at the tail and lets the head of the queue in, while feedback lets one job hold the server for as many services as its routing keeps returning it. It also never leaves the station, so it exits no region, releases no blocked upstream server and is not a system completion.

A SYNCHRONOUS CALL PARKS THE SERVER. The completing job leaves for the callee and its slot stays held until the matching reply comes back: no waiter is promoted into it, and the time it spends held counts as BUSY, because the server is unavailable and reporting it idle would credit the station with capacity it does not have. That is the same rule the BAS branch below applies for the same reason.

A hop that ALREADY switched into the reply class is not a call: it is the answer, and parking on it would wait for a reply to a reply. That switch may not show on dcls yet – see resolve_final_cls – when the routing matrix moves this edge through a synthesized ClassSwitch node, so the check resolves through it rather than reading dcls directly.

Definition at line 682 of file ldes_engine.h.

References line::qn::Station< T >::ServerType::compatible, line::qn::Station< T >::ServerType::count, line::lang::Distrib< T >::disabled, line::lang::DROP, line::ldes::LdesOptions::events, line::lang::EXHAUSTIVE, line::qn::ForkParam< T >::fan_out_dist, line::qn::ForkParam< T >::fan_out_link, line::qn::ForkParam< T >::fan_out_prob, line::InputError::InputError(), line::lang::KLIMITED, ldes_engine_solve_one(), line::lang::LPS, line::lang::Distrib< T >::mean, line::qn::Station< T >::ServerType::name, line::lang::NONE, line::lang::QUEUE_LENGTH, line::lang::RENEGING, line::ldes::LdesOptions::samples, line::ldes::LdesOptions::seed, line::qn::Station< T >::ServerType::service, line::ldes::LdesOptions::slot_length, line::ldes::LdesOptions::slotted, and line::UnsupportedError::UnsupportedError().

Referenced by ldes_engine_solve(), and ldes_engine_solve_one().

◆ ldes_flags()

std::vector< std::string > line::ldes::ldes_flags ( const LdesOptions & o,
const std::vector< std::string > & extra )
inline

The engine flags of one run, after solve <model> -o <result>.

ONE MAPPING for the subprocess and the REST paths, as in both other clients: the server takes the same long-form flags verbatim, and deriving them twice is how the two transports start disagreeing. A knob is emitted only when it differs from the engine default, so a default run yields the minimal command line an older AOT image still parses – except -s and --seed, which are always emitted: the CLI's seed default is -1 (random), so a silent seed makes the run irreproducible and unlike the MATLAB and Python clients.

Definition at line 219 of file solver_ldes.h.

References line::ldes::LdesOptions::busy_period_orders, line::ldes::LdesOptions::busy_period_subnets, line::ldes::LdesOptions::cimethod, line::ldes::LdesOptions::ciminbatch, line::ldes::LdesOptions::ciminobs, line::ldes::LdesOptions::cnvgbatch, line::ldes::LdesOptions::cnvgchk, line::ldes::LdesOptions::cnvgon, line::ldes::LdesOptions::cnvgtol, line::ldes::LdesOptions::events, line::ldes::LdesOptions::has_timespan, line::ldes::LdesOptions::init_sol, ldes_flags(), line::ldes::LdesOptions::method, line::ldes::LdesOptions::mserbatch, line::ldes::LdesOptions::numthreads, line::ldes::LdesOptions::obmoverlap, line::ldes::LdesOptions::replications, line::ldes::LdesOptions::samples, line::ldes::LdesOptions::seed, line::ldes::LdesOptions::slot_length, line::ldes::LdesOptions::slotted, line::ldes::LdesOptions::spectral_low_freq_frac, line::ldes::LdesOptions::t0, line::ldes::LdesOptions::t1, line::ldes::LdesOptions::timeout, line::ldes::LdesOptions::tranfilter, and line::ldes::LdesOptions::warmupfrac.

Referenced by ldes_flags(), and solver_ldes_text().

◆ ldes_is_available()

bool line::ldes::ldes_is_available ( )
inline

True when this machine can run the engine at all, by either image.

Definition at line 245 of file ldes_probe.h.

References ldes_engine_dir(), and ldes_is_available().

Referenced by line::autosolver::auto_solver_is_available(), and ldes_is_available().

◆ ldes_ln_engine_solve()

template<class T>
engine::LnResult line::ldes::ldes_ln_engine_solve ( const lqn::LqnStruct< T > & lsn,
const LdesOptions & o )

Simulate a layered model in process.

The budget is ENTRY COMPLETIONS, mirroring the flat engine's service completions: a layered model has no single notion of "a job leaving", so the count of entry invocations that returned is what a horizon can be set on.

ONE STREAM PER ACTIVITY, as the flat engine now does per (node, class).

The layered reference indexes its generators by the LQN entity rather than by a station, so the offsets here are built the same way from the activity index: host demands in the service band (+1000) and think times in the arrival band, which is what those two are. Sharing one stream across every activity interleaves draws that the reference keeps apart, and the divergence is immediate rather than statistical.

REPLICATION. A processor or task declared with replication r is r identical copies of itself, and a copy is a server of its own: pooling them into one server of r times the capacity would let one queue absorb what r separate queues cannot. So the host state below is indexed by a SLOT – the processor plus its replica – while what the copies share by definition (servers per copy, scheduling) stays indexed by the element. The reported measures are summed over the copies, which keeps throughput conserved across a call and utilization a fraction of the total capacity.

Slot of the processor replica running replica trep of one of its tasks.

Replica of the callee reached by one call of replica crep of the caller. An unset fan-out is the smallest value consistent with repl(caller)*fanout = repl(callee)*fanin, and the caller reaches the block {(i*f+k) mod r}. A call is one indivisible unit of work, so it goes to one member of that block drawn uniformly: over many calls each member carries the 1/f share that LQN2QN splits the call mean into. This is deterministic pairing at f=1 and a uniform spread over every replica at f=r.

< job currently on a host slot

< task index of each slot

Jobs woken by a thread release, continued after the current walk returns. Waking them inside the walk would re-enter it on another job while this one still holds a reference into jobs.

How many times one execution of the caller makes call cidx.

floor(mean) calls plus one more with probability equal to the fraction, which is Solver_ssj_ln.sampleCallCount exactly. It is DETERMINISTIC at an integer mean, which is what calls-mean="3" asks for: three calls every time, not three on average.

Continue every job a thread release woke, and every job those wake.

Put job on its host replica, queueing when every server of it is taken.

Arm THREAD's cold start; the request that woke it waits in thr_queue.

Arm THREAD's idle countdown; when it expires the thread is OFF.

Give jid a thread of the task owning the entry on top of its stack.

Returns false when the job could not proceed: either every thread is taken, or the only thread available was powered off and is now warming up. Either way the job is parked in thr_queue and will be continued by whoever frees or finishes warming a thread. An infinite-thread task never blocks and holds no identified thread.

Free the thread the innermost entry of jid holds and wake the next job.

Continue jid from wherever it is: run the next activity of the innermost entry, issue its next call, or return to the caller.

One execution of act has finished.

ITS RESPONSE TIME CARRIES THE CALLS IT MADE, which is what separates it from the residence act_host_resid accumulates: on an activity that calls a server three times, the response is its own demand plus the three replies and the residence is its own demand alone. The occupancy is closed off through cur_q at the same instant, so QLen, RespT and Tput satisfy Little's law by construction rather than by three separate estimators happening to agree.

Definition at line 274 of file ldes_ln_engine.h.

References line::ldes::LdesOptions::events, line::lang::INF, line::InputError::InputError(), ldes_ln_engine_solve(), line::lang::NONE, line::lang::POST_SEQ, line::lang::PRE_SEQ, line::ldes::LdesOptions::samples, line::ldes::LdesOptions::seed, line::lang::SYNC, and line::UnsupportedError::UnsupportedError().

Referenced by ldes_ln_engine_solve().

◆ ldes_prob_aggr()

double line::ldes::ldes_prob_aggr ( const std::string & doc,
const LdesOptions & o,
std::size_t station,
const std::vector< double > & counts,
std::size_t nclasses )
inline

Port of getProbAggr: the marginal probability of a per-class job count at one station.

The run is a plain steady-state solve with --export-histogram added, which is what getAvgReward already does, so the estimate is over the same post-warmup path the mean metrics come from.

Parameters
docthe model.json document, forwarded byte for byte
othe run's knobs
station1-based station index
countsper-class job counts to test at that station
nclassesthe model's class count

Definition at line 813 of file solver_ldes.h.

References ldes_prob_aggr(), ldes_prob_from_histogram(), and solver_ldes_text().

Referenced by ldes_prob_aggr().

◆ ldes_prob_from_histogram()

double line::ldes::ldes_prob_from_histogram ( const LdesResult & r,
std::size_t nclasses,
const std::vector< LdesStateQuery > & query )
inline

Residence-time probability of an aggregate joint state, from a parsed result.

WHY THE HISTOGRAM AND NOT THE TRAJECTORY. stateHistogram is the exact residence time of every integer joint state the run visited, keyed on the state itself (Solver_ssj.updateRewardStats accumulates it per marking), so P(state) = t(state) / sum(t) is an unbiased estimate that is exact on the sampled path. The transient QNt series is a sequence of INTERVAL MEANS of the queue length; comparing one against an integer state matches only where a bucket mean happens to land on an integer, which is why the three clients that estimated a probability that way reported a near-zero number for a state the chain occupies most of the time (BUG-96).

The layout is the aggregated station-major, class-minor one ctmc_state_space_aggr builds: column (i-1)*nclasses + k is the number of class-k jobs at station i. THE RESOLUTION IS PER CLASS, not per phase: the engine records the integer queue lengths, so a phase-resolved query cannot be answered from this document and the caller must aggregate first.

Parameters
ra result produced with --export-histogram
nclassesthe model's class count, which fixes the column stride
querythe stations to constrain, and to what
Returns
the residence-time fraction, 0 when the state was never visited
Exceptions
NumericErrorwhen the run carried no histogram (the flag was omitted)

Definition at line 714 of file solver_ldes.h.

References line::Matrix< T >::cols(), line::ldes::LdesResult::histogram_space, line::ldes::LdesResult::histogram_time, line::InputError::InputError(), ldes_prob_from_histogram(), line::NumericError::NumericError(), and line::Matrix< T >::rows().

Referenced by ldes_prob_aggr(), ldes_prob_from_histogram(), and ldes_prob_sys_aggr().

◆ ldes_prob_sys_aggr()

double line::ldes::ldes_prob_sys_aggr ( const std::string & doc,
const LdesOptions & o,
const Matrix< double > & target )
inline

Port of getProbSysAggr: the joint probability of a whole aggregate state.

Parameters
target(nstations x nclasses) job counts, EVERY station constrained

Definition at line 829 of file solver_ldes.h.

References line::Matrix< T >::cols(), line::InputError::InputError(), ldes_prob_from_histogram(), ldes_prob_sys_aggr(), line::Matrix< T >::rows(), and solver_ldes_text().

Referenced by ldes_prob_sys_aggr().

◆ ldes_runners()

std::vector< LdesRunner > line::ldes::ldes_runners ( const std::string & doc,
const std::vector< std::string > & flags )
inline

The runners to try, in order (see the file header for why there are two and when the order flips).

Parameters
docthe model document, scanned for features the AOT image predates
flagsthe resolved flags, scanned for the same reason
Exceptions
UnsupportedErrorwhen no engine can run on this host

Definition at line 340 of file solver_ldes.h.

References line::ldes::LdesRunner::argv, line::ldes::LdesRunner::engine, ldes_engine_dir(), ldes_runners(), and line::UnsupportedError::UnsupportedError().

Referenced by ldes_runners(), and solver_ldes_text().

◆ ldes_solve_rest()

detail::Json line::ldes::ldes_solve_rest ( const std::string & base_url,
const std::string & doc,
const std::vector< std::string > & flags,
double timeout )
inline

Solves through an LDES REST server and returns its result document.

The payload is the model text plus the same flag vector the subprocess would have been given, so a fixed seed gives the same numbers on both transports.

Definition at line 382 of file solver_ldes.h.

References line::http::Response::body, ldes_solve_rest(), line::NumericError::NumericError(), line::http::post_json(), and line::http::Response::status.

Referenced by ldes_solve_rest(), and solver_ldes_text().

◆ list_valid_methods()

std::vector< std::string > line::ldes::list_valid_methods ( )
inline

Port of SolverLDES.listValidMethods.

Two names, and only one engine behind them: 'parallel' asks the engine for INDEPENDENT REPLICATIONS and the mean over them, which the client turns into --replications (the caller's count when set, 8 otherwise). It is not a second simulator, which is why the list is this short in all four codebases.

Definition at line 52 of file ldes_options.h.

References list_valid_methods().

Referenced by line::autosolver::auto_family_methods(), list_valid_methods(), and line::NetworkSolver::list_valid_methods().

◆ parse_ldes_result()

LdesResult line::ldes::parse_ldes_result ( const detail::Json & d)
inline

Parses one ldes-result document.

format is CHECKED, not assumed: the runner falls through on failure, and a runner that wrote some other JSON to the output path would otherwise be read as a result full of absent metrics rather than as the failure it is.

Definition at line 426 of file solver_ldes.h.

References line::ldes::LdesResult::AN, line::ldes::LdesResult::ANCI, line::ldes::LdesResult::ANfcr, line::ldes::LdesResult::avgOrbitSize, line::ldes::LdesResult::avgRenegingWaitTime, line::ldes::LdesResult::balkedCustomers, line::ldes::LdesResult::balkingProbability, line::ldes::LdesResult::busy_periods, line::ldes::LdesResult::cache_metrics, line::ldes::LdesResult::class_names, line::ldes::LdesResult::CN, line::ldes::LdesResult::converged, line::ldes::LdesResult::convergence_batches, line::ldes::LdesResult::BusyPeriodTarget::count, line::ldes::LdesCacheMetrics::delayed, line::ldes::LdesResult::DropRateJoin, line::ldes::LdesResult::DropRateNfcr, line::ldes::LdesResult::histogram_space, line::ldes::LdesResult::histogram_time, line::ldes::LdesCacheMetrics::hit, line::ldes::LdesCacheMetrics::hitList, line::ldes::LdesCacheMetrics::itemProb, line::ldes::LdesResult::BusyPeriodTarget::job_class, line::ldes::LdesCacheMetrics::latency, line::ldes::LdesCacheMetrics::listCost, line::ldes::LdesResult::BusyPeriodTarget::mean, line::ldes::LdesResult::MemOccNfcr, line::ldes::LdesResult::method, line::ldes::LdesCacheMetrics::miss, line::ldes::LdesResult::BusyPeriodTarget::name, line::ldes::LdesResult::nchains, line::ldes::LdesResult::nclasses, line::ldes::LdesResult::nregions, line::ldes::LdesResult::nstations, line::NumericError::NumericError(), parse_ldes_result(), line::ldes::LdesResult::QN, line::ldes::LdesResult::QNCI, line::ldes::LdesResult::QNfcr, line::ldes::LdesResult::QNRelPrec, line::ldes::LdesResult::QNSamples, line::ldes::LdesResult::QNt, line::ldes::LdesResult::renegedCustomers, line::ldes::LdesResult::renegingRate, line::ldes::LdesResult::respTimeSamples, line::ldes::LdesResult::retrialDropped, line::ldes::LdesResult::retriedCustomers, line::ldes::LdesResult::RN, line::ldes::LdesResult::RNCI, line::ldes::LdesResult::RNfcr, line::ldes::LdesResult::RNRelPrec, line::ldes::LdesResult::RNSamples, line::ldes::LdesResult::runtime, line::Matrix< T >::size(), line::ldes::LdesResult::station_names, line::ldes::LdesResult::BusyPeriodTarget::stations, line::ldes::LdesResult::stopping_reason, line::ldes::LdesResult::t, line::ldes::LdesResult::TN, line::ldes::LdesResult::TNCI, line::ldes::LdesResult::TNfcr, line::ldes::LdesResult::TNRelPrec, line::ldes::LdesResult::TNSamples, line::ldes::LdesResult::TNt, line::ldes::LdesResult::total_simulated_events, line::ldes::LdesResult::traj_space, line::ldes::LdesResult::traj_time, line::ldes::LdesResult::UN, line::ldes::LdesResult::UNCI, line::ldes::LdesResult::UNfcr, line::ldes::LdesResult::UNRelPrec, line::ldes::LdesResult::UNSamples, line::ldes::LdesResult::UNt, line::ldes::LdesResult::WeightNfcr, line::ldes::LdesResult::WN, line::ldes::LdesResult::WNCI, line::ldes::LdesResult::WNfcr, and line::ldes::LdesResult::XN.

Referenced by parse_ldes_result(), and solver_ldes_text().

◆ solver_ldes()

template<class T>
LdesResult line::ldes::solver_ldes ( const qn::NetworkStruct< T > & sn,
const LdesOptions & o,
const std::vector< std::string > & extra_flags = std::vector<std::string>() )

The same, for a model built through the C++ API.

The struct is serialized with io::network_json_envelope, the writer whose output the reference readers consume, and the document then takes the same path as any other. WHAT THE STRUCT CANNOT CARRY DOES NOT CROSS: a reward built from a lambda and any construct outside the writer's schema are dropped by the writer, with its own warning, which is why a caller holding a model.json should pass the DOCUMENT rather than a struct parsed from it – the round trip can only lose.

Definition at line 858 of file solver_ldes.h.

References line::io::network_json_envelope(), solver_ldes(), and solver_ldes_text().

Referenced by line::NetworkSolver::avg_table(), and solver_ldes().

◆ solver_ldes_file()

LdesResult line::ldes::solver_ldes_file ( const std::string & path,
const LdesOptions & o,
const std::vector< std::string > & extra_flags = std::vector<std::string>() )
inline

The same, reading the document from a file.

Definition at line 669 of file solver_ldes.h.

References solver_ldes_file(), and solver_ldes_text().

Referenced by solver_ldes_file().

◆ solver_ldes_text()

LdesResult line::ldes::solver_ldes_text ( const std::string & doc,
const LdesOptions & o,
const std::vector< std::string > & extra_flags = std::vector<std::string>() )
inline

Runs one LDES simulation on a model.json DOCUMENT and parses its result.

The document is written into a private scratch directory, the engine is run there, and the directory is removed on both the success and the failure path (TempDir's destructor), because a wrapper that leaks one directory per failed solve leaks silently.

THE RUNNERS ARE TRIED IN ORDER and the LAST failure is reported in full, merged stdout and stderr, because the engine states why it refused there and an exit code alone is not a diagnosis.

Parameters
docthe model.json text, forwarded byte for byte
othe run's knobs
extra_flagsflags an analysis adds (–trajectory, –export-histogram, –respt-samples)
Returns
the parsed result, carrying which image produced it

Definition at line 606 of file solver_ldes.h.

References line::util::capture(), line::ldes::LdesResult::engine, line::util::ProcResult::exitCode, line::util::TempDir::file(), ldes_flags(), ldes_runners(), ldes_solve_rest(), line::NumericError::NumericError(), line::util::ProcResult::out, parse_ldes_result(), line::ldes::LdesOptions::rest_url, solver_ldes_text(), line::util::LineConsole::step(), line::ldes::LdesResult::stopping_reason, line::ldes::LdesResult::timed_out, line::util::ProcResult::timedOut, line::ldes::LdesOptions::timeout, line::util::trim(), and line::ldes::LdesOptions::verbose.

Referenced by ldes_prob_aggr(), ldes_prob_sys_aggr(), solver_ldes(), solver_ldes_file(), and solver_ldes_text().