api.wfn

class WorkflowNet

Bases: Model

WorkflowNet - A Workflow Net (WF-net) model for performance analysis.

A WF-net (van der Aalst, 1997/98) is a Petri net with exactly one source place i (no incoming arcs), exactly one sink place o (no outgoing arcs), and every place and transition on a directed path from i to o. A case is one token put in i; it is complete when one token reaches o.

WorkflowNet does not have a solver of its own. It checks that the net is a WF-net and that it is sound, and then either analyses one case exactly or compiles the net into an ordinary Network:

validate() structural WF-net conditions checkSoundness(k) k-soundness on the reachability graph getCycleTime() exact phase-type law of the case duration of one

case without contention (any sound net, PH timing)

getCycleTimeAnalysis() moments, CDF, percentiles, expected firings per

transition and expected token-time per place

getResourceLoads(lambda) per-resource load of an open stream of cases toWorkflow() equivalent Workflow, for block-structured nets toNetwork(‘forkjoin’) fork-join queueing network, block-structured nets toNetwork(‘spn’) stochastic Petri net with a case-counter place,

any sound net

getCaseMetrics(solver) case throughput, WIP, cycle time and resource

utilisation from a solver of a compiled network

TIMING. A timed transition fires after a phase-type delay (Exp, Erlang, HyperExp, Coxian, Cox2, APH, PH) under race semantics with enabling memory: a transition disabled by a conflicting firing loses its progress. An immediate transition fires in zero time; among the enabled immediate transitions only those of the highest priority fire, chosen with probability proportional to their weights.

SEMANTICS OF THE TWO NETWORK TARGETS WITH MANY CASES. The fork-join target keeps case identity, since LINE joins only synchronise siblings of the same job. The SPN target uses uncoloured tokens, so an AND-join may pair tokens of different cases; its case-counter place gives the mean number of cases in the system and, by Little’s law, the exact mean cycle time under that semantics. Percentiles are not available from it.

Example

wfn = WorkflowNet(‘SimpleWFN’); i = wfn.addPlace(‘i’); p = wfn.addPlace(‘p’); o = wfn.addPlace(‘o’); t1 = wfn.addTransition(‘Task1’, Exp(1.0)); t2 = wfn.addTransition(‘Task2’, Exp(2.0)); wfn.addArc(i, t1); wfn.addArc(t1, p); wfn.addArc(p, t2); wfn.addArc(t2, o); [isSound, report] = wfn.checkSoundness(); ph = wfn.getCycleTime(); % hypoexponential, mean 1.5

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

Constructor Summary
WorkflowNet(name)

WorkflowNet Create a new WorkflowNet instance

SELF = WorkflowNet(NAME)

Property Summary
arcs

Cell array of WFNArc objects

arrivalProcess

Interarrival distribution of cases (open analysis), or []

closedCases

Number of concurrent cases (closed analysis), 0 = open

places

Cell array of WFNPlace objects

resources

Cell array of WFNResource objects

sinkPlace

Sink place o, set by validate() if not given

sourcePlace

Source place i, set by validate() if not given

stateCap

Largest reachability graph explored before giving up

transitions

Cell array of WFNTransition objects

Method Summary
addArc(source, target, weight)

ADDARC Add an arc from a place to a transition or vice versa

ARC = ADDARC(SELF, SOURCE, TARGET, WEIGHT)

SOURCE and TARGET are WFNPlace/WFNTransition objects of this net or their names; WEIGHT is a positive integer (default 1).

addPlace(name, initialMarking)

ADDPLACE Add a place to the workflow net

PLACE = ADDPLACE(SELF, NAME, INITIALMARKING)

INITIALMARKING is informational: the analyses always start from k tokens in the source place, as WF-net soundness does.

addTransition(name, distribution, timing, servers, priority, weight)

ADDTRANSITION Add a transition to the workflow net

TRANSITION = ADDTRANSITION(SELF, NAME, DISTRIBUTION, TIMING, SERVERS, PRIORITY, WEIGHT)

Parameters:
  • name - Transition name

  • distribution - Firing-time distribution (default Exp(1)) – Immediate distribution makes the transition immediate

  • timing - TimingStrategy.TIMED or TimingStrategy.IMMEDIATE

  • servers - Concurrent firings allowed (default Inf, i.e. no – contention unless a resource is assigned)

  • priority - Firing priority of an immediate transition (default 1)

  • weight - Firing weight of an immediate transition (default 1)

checkSoundness(k, stateCap)

CHECKSOUNDNESS Decide k-soundness on the reachability graph

[ISSOUND, REPORT] = CHECKSOUNDNESS(SELF, K, STATECAP)

From the marking with K tokens in the source (default 1) the net is k-sound when (1) the final marking, K tokens in the sink, is reachable from every reachable marking (option to complete), (2) no reachable marking puts K tokens in the sink together with other tokens (proper completion) and (3) every transition fires in some reachable marking (no dead transitions). The untimed net is explored, as soundness is a property of the net, not of its timing. A marking that strictly covers one of its ancestors proves the net unbounded, hence unsound. If STATECAP markings (default: the stateCap property) are explored first, the verdict is ‘unknown’, never ‘sound’.

REPORT fields: k, verdict (‘sound’,’unsound’,’unknown’), stateSpaceSize, deadlocks, noOptionToComplete, improperCompletion (cell arrays of markings, as row vectors over places), deadTransitions (names), unbounded, unboundedWitness, unboundedOrTooLarge, witnesses (readable one-line descriptions).

static formatReport(report)

FORMATREPORT One-line summary of a soundness report

static fromNetwork(model)

FROMNETWORK Create a WorkflowNet from a LINE Petri net

NET = FROMNETWORK(MODEL) copies the places, the transitions and their arcs of a single-class Network made of Place and Transition nodes. A transition must have exactly one mode and no inhibitor arcs or firing-rate dependence, since a WF-net transition has one firing rule. The result is validated.

static fromPNML(filename, netName)

FROMPNML Create a WorkflowNet from a PNML file

NET = FROMPNML(FILENAME, NETNAME) reads the net with pnml_load and converts it with fromNetwork; the source and sink are found from the structure. A file from another tool is untimed and reads with every transition exponential at rate 1 (see pnml_load).

getCaseMetrics(solver)

GETCASEMETRICS Case-level metrics from a solver of a compiled net

METRICS = GETCASEMETRICS(SELF, SOLVER), with SOLVER built on the network returned by the last toNetwork call, returns a struct:

target, mode - ‘forkjoin’/’spn’, ‘open’/’closed’ throughput - cases completed per unit time wip - mean number of cases in the system cycleTime - mean case cycle time (Little’s law) resources - table: resource, servers, utilisation tasks - table: task, mean response time at its

station (forkjoin) or tokens in service (spn)

places - table of mean tokens per place (spn only)

getCycleTime()

GETCYCLETIME Exact case cycle time of one case without contention

PH = GETCYCLETIME(SELF)

The reachability graph of the timed net from one token in the source becomes an absorbing CTMC whose absorbing state is the final marking; immediate transitions are folded out of it, so the case duration is exactly phase-type for any sound net with phase-type timing, series-parallel or not. Requires 1-soundness.

getCycleTimeAnalysis()

GETCYCLETIMEANALYSIS Full single-case analysis

AN = GETCYCLETIMEANALYSIS(SELF) returns a struct with fields

ph - PH law of the case duration mean, variance, scv, moments (first three raw moments) cdf - function handle t -> P(duration <= t) percentile - function handle q in (0,1) -> q-quantile zeroTimeProb - probability that a case completes in zero time firings - table of expected firings per transition placeTime - table of expected token-time per place numStates - number of tangible transient states

getIncidence()

GETINCIDENCE Input and output arc weights, places x transitions

getPlace(name)

GETPLACE Get a place by name

getResourceLoads(lambda)

GETRESOURCELOADS Per-resource load of an open stream of cases

LOADS = GETRESOURCELOADS(SELF, LAMBDA) returns a table with one row per resource and per task with a finite server count: the offered load LAMBDA * sum_t E[firings of t] * E[time of t] divided by the servers, using the expected firings of one case. The open system is stable only if every load is below 1. LAMBDA defaults to the rate of the arrival process.

getTransition(name)

GETTRANSITION Get a transition by name

isBlockStructured()

ISBLOCKSTRUCTURED True if the net reduces to a process tree

The net is reduced by five rules until nothing applies: fold a single-input single-output transition between two places into a place, fold a single-input single-output place between two transitions into a transition, merge parallel places (AND block), merge parallel transitions (XOR block, choices resolved by immediate transitions of equal priority, in proportion to their weights) and absorb a self-loop transition (geometric loop). It is block-structured if it reduces to one place.

savePNML(filename)

SAVEPNML Write the net to PNML with one token in the source

SAVEPNML(SELF, FILENAME) builds the plain Petri net (no case counter, no resources) and writes it with pnml_save, so that WorkflowNet.fromPNML reads it back with its timing.

setArrivalProcess(process, param)

SETARRIVALPROCESS Set the stream of cases for open analysis

SETARRIVALPROCESS(SELF, ‘poisson’, RATE) SETARRIVALPROCESS(SELF, ‘map’, MAPOBJ) SETARRIVALPROCESS(SELF, DIST) any interarrival distribution SETARRIVALPROCESS(SELF, ‘none’) clears it

setClosedCases(k)

SETCLOSEDCASES Set k concurrent cases (closed analysis); 0 = open

setResource(transition, name, servers, scheduling)

SETRESOURCE Assign a shared resource to a timed transition

RESOURCE = SETRESOURCE(SELF, TRANSITION, NAME, SERVERS, SCHEDULING)

A resource is shared by every task that names it and by every case. Calling it again with an existing NAME reuses that resource; SERVERS and SCHEDULING, when given, update it.

setSink(place)

SETSINK Declare the sink place (checked by validate)

setSource(place)

SETSOURCE Declare the source place (checked by validate)

setStateCap(cap)

SETSTATECAP Set the largest state space explored by the analyses

toNetwork(target, varargin)

TONETWORK Compile the workflow net to a LINE Network

NETWORK = TONETWORK(SELF, TARGET, ‘checkSoundness’, TF)

TARGET:
‘forkjoin’ - block-structured nets. AND-split -> Fork, AND-join

-> Join, XOR and loops -> probabilistic routing, a task -> Delay (no contention), a Queue of its own (finite servers) or the Queue of its resource. Tasks sharing a resource are told apart by class. Solvers: MVA (fork-join approximations), JMT, LDES.

‘spn’ - any sound net, as a stochastic Petri net with a

case-counter place. A task on a resource becomes an immediate acquire followed by the timed task, which releases the resource; waiting tasks take a free server in random order, in proportion to their weights, whatever the resource scheduling (the fork-join target honours it). Its transitions create and destroy tokens (an AND-split makes two of one), which SolverSSA and SolverLDES simulate; SolverCTMC and SolverJMT do not support that.

Open analysis (setArrivalProcess) gives a Source and a Sink; closed analysis (setClosedCases(k)) recirculates k cases through a zero-time restart. The soundness gate (k-soundness, k the number of closed cases or 1) is on by default; pass ‘checkSoundness’, false to skip it on nets too large to explore.

toWorkflow()

TOWORKFLOW Equivalent Workflow of a block-structured net

WORKFLOW = TOWORKFLOW(SELF) maps each timed transition to an activity with its distribution and each block to the Workflow precedence of the same meaning. Immediate transitions take no time and disappear; where a fork or join needs an anchor that the net does not provide, an Immediate activity named wfn_aux_<n> is added. Workflow.Loop executes its body a geometric number of times >= 1, so a loop with work on its return path is refused. Workflow models an Immediate activity at rate GlobalConstants.Immediate, so its toPH() agrees with getCycleTime() up to that rate, not to machine precision.

validate()

VALIDATE Check the structural WF-net conditions

VALIDATE(SELF) requires exactly one place without incoming arcs (the source i), exactly one place without outgoing arcs (the sink o), and every place and transition on a directed path from i to o, which is strong connectivity once the arc o -> i is added. A declared source or sink must be that place. On success sourcePlace and sinkPlace are set; on failure a LINE:input:Model error names the offending nodes.

class WFNTransition

Bases: handle

WFNTransition - A transition in a Workflow Net.

WFNTransition represents a transition in a workflow net, which can consume tokens from input places and produce tokens to output places. A transition can be timed (with a service time distribution) or immediate (fires instantly when enabled).

Properties:

name - Transition name distribution - Service time distribution (for timed transitions) timing - Timing strategy (TIMED or IMMEDIATE) servers - Number of servers priority - Firing priority weight - Firing weight (for probabilistic choices) index - Internal index workflowNet - Parent WorkflowNet resource - Assigned WFNResource (if any) workflowActivity - Corresponding WorkflowActivity (if compiled to Workflow) networkTransition - Corresponding Transition in compiled Network (if any)

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

Constructor Summary
WFNTransition(workflowNet, name, distribution, timing, servers, priority, weight)

WFNTransition Create a new WFNTransition

SELF = WFNTransition(WORKFLOWNET, NAME, DISTRIBUTION, TIMING, SERVERS, PRIORITY, WEIGHT)

Property Summary
distribution
index
name
priority
servers
timing
weight
workflowNet
Method Summary
isImmediate()

ISIMMEDIATE Check if transition is immediate

TF = ISIMMEDIATE(SELF)

isTimed()

ISTIMED Check if transition is timed

TF = ISTIMED(SELF)

setDistribution(distribution)

SETDISTRIBUTION Set the service time distribution

SETDISTRIBUTION(SELF, DISTRIBUTION)

setPriority(priority)

SETPRIORITY Set the firing priority

SETPRIORITY(SELF, PRIORITY)

setServers(servers)

SETSERVERS Set the number of servers

SETSERVERS(SELF, SERVERS)

setTiming(timing)

SETTIMING Set the timing strategy

SETTIMING(SELF, TIMING)

setWeight(weight)

SETWEIGHT Set the firing weight

SETWEIGHT(SELF, WEIGHT)

class WFNResource

Bases: handle

WFNResource - A shared resource in a Workflow Net.

WFNResource represents a shared resource that can be assigned to transitions. When a transition requires a resource, it can only fire if the resource has available servers.

Properties:

name - Resource name servers - Number of servers scheduling - Scheduling strategy

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

Constructor Summary
WFNResource(name, servers, scheduling)

WFNResource Create a new WFNResource

SELF = WFNResource(NAME, SERVERS, SCHEDULING)

Property Summary
name
scheduling
servers
Method Summary
setName(name)

SETNAME Set the resource name

SETNAME(SELF, NAME)

setScheduling(scheduling)

SETSCHEDULING Set the scheduling strategy

SETSCHEDULING(SELF, SCHEDULING)

setServers(servers)

SETSERVERS Set the number of servers

SETSERVERS(SELF, SERVERS)

class WFNArc

Bases: handle

WFNArc - An arc in a Workflow Net.

WFNArc represents a directed arc between a place and a transition, or between a transition and a place. Arcs have a weight (multiplicity).

Properties:

source - Source node (WFNPlace or WFNTransition) target - Target node (WFNPlace or WFNTransition) weight - Arc weight/multiplicity workflowNet - Parent WorkflowNet

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

Constructor Summary
WFNArc(workflowNet, source, target, weight)

WFNArc Create a new WFNArc

SELF = WFNArc(WORKFLOWNET, SOURCE, TARGET, WEIGHT)

Property Summary
source
target
weight
workflowNet
Method Summary
setWeight(weight)

SETWEIGHT Set the arc weight

SETWEIGHT(SELF, WEIGHT)

class WFNPlace

Bases: handle

WFNPlace - A place in a Workflow Net.

WFNPlace represents a place in a workflow net, which can hold tokens that enable transitions to fire.

Properties:

name - Place name initialMarking - Initial token count index - Internal index workflowNet - Parent WorkflowNet networkPlace - Corresponding Place in compiled Network (if any)

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

Constructor Summary
WFNPlace(workflowNet, name, initialMarking)

WFNPlace Create a new WFNPlace

SELF = WFNPlace(WORKFLOWNET, NAME, INITIALMARKING)

Property Summary
index
initialMarking
name
workflowNet
Method Summary
setInitialMarking(marking)

SETINITIALMARKING Set the initial marking

SETINITIALMARKING(SELF, MARKING)