Skip to content

Algorithms ​

wity-system provides trait-enriched wrappers around graph-headless's traversal and pathfinding primitives, plus domain-level cascade and impact analysis. When types are registered, algorithms auto-read behavioral traits. Explicit options always override.

What the structure is: a flow network, or a plain map ​

The analyses read the types' traits as the system's structure says (formalism/structure.js, declared as data like an aspect's formalism):

structurefollowsbegins ata route costs
'flow-network' (the default — every system before 0.19)connection types with carriesFlow: true (all, when none is marked)asset types with role: 'source'defaultCost, or the connection's costField
'plain'every connectionnothing — cascade must be given sourceUidsone per step
js
sys.setStructure('plain');   // an org chart, a service map: nothing flows
sys.getStructure();          // 'plain'
sys.entryAssets();           // [] — the sources of a flow network; none here

The declarations are exported: FLOW_NETWORK, PLAIN, STRUCTURES (by id) and DEFAULT_STRUCTURE ('flow-network'); readStructure(structure, types) → { edgeTypes, sourceTypes, cost } is how the analyses read a system's types under one — edgeTypes null for all of them, cost null for one per step.

It is stored with the system only when it is not 'flow-network', so a flow network stores exactly as before. The type registry's getSourceTypes, getFlowTypes and buildCostFn are the flow network's reading of the traits.

Propagation ​

BFS traversal from a starting node, following edges in a given direction.

js
sys.propagate('V1');
// → [{ uid: 'H1', depth: 1, via: 'V1' }]

sys.propagate('H1', { direction: 'upstream' });
// → [{ uid: 'V1', depth: 1, via: 'H1' }, { uid: 'WTP', depth: 2, via: 'V1' }]

Options:

OptionDefaultDescription
direction'downstream''downstream', 'upstream', or 'both'
edgeTypesauto (flow types)Edge types to traverse. Auto-filtered to carriesFlow: true types when registry is populated
maxDepthInfinityMaximum traversal depth

Cascade ​

Cascade analysis determines which nodes are cut off from source nodes when specific nodes are disabled.

js
sys.cascade(['V1']);
// → { reachable: ['WTP'], cutOff: ['H1'], disabled: ['V1'] }

The algorithm:

  1. Source detection — auto-detects sources from asset types with role: 'source', or uses explicitly provided sourceUids
  2. Edge filtering — auto-filters to connection types with carriesFlow: true, or uses provided edgeTypes
  3. BFS from sources — skipping disabled nodes, marking everything reachable
  4. Cut-off classification — everything not reachable and not disabled is cut off
js
// Explicit source/edge overrides
sys.cascade(['V1'], {
    sourceUids: ['WTP', 'BACKUP'],
    edgeTypes: ['feeds'],
    direction: 'downstream',
});

Options:

OptionDefaultDescription
sourceUidsauto (source role)Override source node detection
edgeTypesauto (flow types)Edge types to consider for reachability
direction'downstream''downstream', 'upstream', or 'both'

Returns { reachable: string[], cutOff: string[], disabled: string[] }.

Impact ​

Full impact analysis — what is upstream and downstream of a node.

js
sys.impact('V1');
// → { downstream: [...], upstream: [...] }

sys.impact('V1', { maxDepth: 2 });
// → limited to 2 hops in each direction

Options:

OptionDefaultDescription
edgeTypesauto (flow types)Edge types to traverse
maxDepthInfinityMaximum traversal depth in each direction

Returns { downstream: Array, upstream: Array }, each array containing { uid, depth, via } entries.

Reachable ​

List all nodes reachable from a starting node.

js
sys.reachable('WTP');
// → ['V1', 'H1']

sys.reachable('H1', { direction: 'upstream' });
// → ['V1', 'WTP']

Options:

OptionDefaultDescription
direction'downstream''downstream', 'upstream', or 'both'
edgeTypesauto (flow types)Edge types to traverse

Shortest path ​

Dijkstra's shortest path with trait-driven cost functions.

js
sys.shortestPath('WTP', 'H1');
// → { path: ['WTP', 'V1', 'H1'], cost: 620 }

Cost resolution (when types are registered):

  1. If the edge's connection type defines costField and edge.data[costField] exists, use that value
  2. Otherwise use defaultCost from the connection type
  3. No type registered for the edge type → cost is 1
js
// Override with a custom cost function
sys.shortestPath('WTP', 'H1', { cost: (edge) => edge.data.latency ?? 1 });

Options:

OptionDefaultDescription
edgeTypesauto (flow types)Edge types to traverse
costauto (costField/defaultCost)Custom (edge) => number cost function

Returns { path: string[], cost: number } or null if no path exists.

All paths ​

DFS-based enumeration of all paths between two nodes.

js
sys.allPaths('WTP', 'H1', { maxDepth: 10 });
// → [{ path: ['WTP', 'V1', 'H1'], cost: 620 }]

Options:

OptionDefaultDescription
maxDepthnoneMaximum path length
edgeTypesauto (flow types)Edge types to traverse
costauto (costField/defaultCost)Custom cost function

Returns an array of { path: string[], cost: number }.

Pure function variants ​

cascade and impact are also exported as standalone pure functions that operate directly on a GraphAbstract, without trait enrichment:

js
import { cascade, impact } from 'wity-system';

const result = cascade(graph, ['V1'], {
    sourceUids: ['WTP'],
    edgeTypes: ['feeds'],
});

const analysis = impact(graph, 'V1', {
    edgeTypes: ['feeds'],
    maxDepth: 5,
});

Use the pure function variants when you need to run algorithms against a raw graph without a SystemGraph wrapper.