architecture/network/topology
Topology utilities.
Provides:
- computeTopoOrder: Kahn-style topological sorting with graceful fallback when cycles detected.
- hasPath: depth-first reachability query (used to prevent cycle introduction when acyclicity enforced).
- topology contract helpers: public intent accessors that keep semantic API state aligned with low-level runtime flags.
Design Notes:
- We deliberately tolerate cycles by falling back to raw node ordering instead of throwing; this allows callers performing interim structural mutations to proceed (e.g. during evolve phases) while signaling that the fast acyclic optimizations should not be used.
- Input nodes are seeded into the queue immediately regardless of in-degree to keep them early in the ordering even if an unusual inbound edge was added (defensive redundancy).
- Self loops are ignored for in-degree accounting and queue progression (they neither unlock new nodes nor should they block ordering completion).
architecture/network/topology/network.topology.utils.types.ts
IN_DEGREE_DECREMENT
Unit decrement/increment used for in-degree tally updates.
INPUT_NODE_TYPE
Input node-type discriminator used for queue seeding.
PathSearchContext
Mutable context used while running iterative DFS reachability checks.
TopologyBuildContext
Mutable context used while building Kahn topological order.
TopologyNetwork
Network instance type used by topology helpers.
TopologyNetworkProps
Internal topology state view carried across helper groups.
TopologyNode
Node instance type used by topology helpers.
ZERO_COUNT
Zero baseline used for degree counts and empty-size checks.
architecture/network/topology/network.topology.utils.ts
computeTopoOrder
computeTopoOrder(): void
Compute a topological ordering (Kahn's algorithm) for the current directed acyclic graph. If cycles are detected (order shorter than node count) we fall back to raw node order to avoid breaking callers. In non-acyclic mode we simply clear cached order to signal use of sequential node array.
createMLP
createMLP(
inputCount: number,
hiddenCounts: number[],
outputCount: number,
): default
Build a strictly layered and fully connected MLP network.
Parameters:
this- Network constructor.inputCount- Number of input nodes.hiddenCounts- Hidden-layer node counts.outputCount- Number of output nodes.
Returns: Newly created MLP network.
getTopologyIntent
getTopologyIntent(): NetworkTopologyIntent
Read the public topology intent preserved on a network instance.
This accessor keeps the semantic contract visible to callers even though the lower-level runtime ultimately enforces acyclicity through booleans and cache invalidation.
Parameters:
this- Target network instance.
Returns: Current topology intent.
hasPath
hasPath(
from: default,
to: default,
): boolean
Depth-first reachability test (avoids infinite loops via visited set).
rebuildConnections
rebuildConnections(
networkInstance: default,
): void
Rebuild the canonical connection array from per-node outgoing lists.
Parameters:
networkInstance- Target network.
setEnforceAcyclic
setEnforceAcyclic(
flag: boolean,
): void
Toggle low-level acyclic enforcement while preserving a coherent public contract.
This exists for backward compatibility with callers that still use the legacy
boolean API instead of the semantic topologyIntent field.
Parameters:
this- Target network instance.flag- Whether to enforce acyclic connectivity.
Returns: Nothing.
setTopologyIntent
setTopologyIntent(
topologyIntent: NetworkTopologyIntent,
): void
Set the public topology intent and synchronize low-level runtime flags.
Updating the semantic contract also updates acyclic enforcement and marks the topological cache dirty so later activation paths rebuild consistent state.
Parameters:
this- Target network instance.topologyIntent- Desired topology intent.
Returns: Nothing.
architecture/network/topology/network.topology.loop.utils.ts
appendTopoNode
appendTopoNode(
topoOrder: default[],
node: default,
): void
Append one node to topological order output.
Parameters:
topoOrder- Accumulated topological order.node- Node to append.
Returns: Void.
decrementNodeInDegree
decrementNodeInDegree(
buildContext: TopologyBuildContext,
node: default,
): number
Decrement node in-degree and return remaining value.
Parameters:
buildContext- Mutable build context.node- Target node.
Returns: Remaining in-degree after decrement.
getInDegree
getInDegree(
buildContext: TopologyBuildContext,
node: default,
): number
Read in-degree for a node with zero fallback.
Parameters:
buildContext- Mutable build context.node- Candidate node.
Returns: In-degree value.
isInputNode
isInputNode(
node: default,
): boolean
Test whether a node is an input node.
Parameters:
node- Candidate node.
Returns: True when node type is input.
isQueueSeedNode
isQueueSeedNode(
node: default,
buildContext: TopologyBuildContext,
): boolean
Determine whether a node belongs in the initial queue.
Parameters:
node- Candidate node.buildContext- Mutable build context.
Returns: True when node is input-type or has zero in-degree.
isSelfConnection
isSelfConnection(
from: default,
to: default,
): boolean
Test whether a connection is a self-loop.
Parameters:
from- Source node.to- Target node.
Returns: True when source and target are the same node.
processKahnQueue
processKahnQueue(
buildContext: TopologyBuildContext,
): void
Process queue until all available nodes are emitted.
Parameters:
buildContext- Mutable build context.
Returns: Void.
relaxOutgoingEdges
relaxOutgoingEdges(
buildContext: TopologyBuildContext,
currentNode: default,
): void
Relax outgoing edges for one processed node.
Parameters:
buildContext- Mutable build context.currentNode- Processed node.
Returns: Void.
seedProcessingQueue
seedProcessingQueue(
buildContext: TopologyBuildContext,
): void
Seed Kahn queue with input nodes and zero in-degree nodes.
Parameters:
buildContext- Mutable build context.
Returns: Void.
takeNextQueueNode
takeNextQueueNode(
processingQueue: default[],
): default
Shift and return the next queue node.
Parameters:
processingQueue- Queue of pending nodes.
Returns: Next node.
architecture/network/topology/network.topology.path.utils.ts
createPathSearchContext
createPathSearchContext(
from: default,
to: default,
): PathSearchContext
Create DFS search context.
Parameters:
from- Origin node.to- Target node.
Returns: Initialized path-search context.
hasVisitedNode
hasVisitedNode(
visitedNodes: Set<default>,
node: default,
): boolean
Test whether a node has already been visited.
Parameters:
visitedNodes- Visited-node set.node- Candidate node.
Returns: True when node is already visited.
isSameNode
isSameNode(
leftNode: default,
rightNode: default,
): boolean
Compare node identity.
Parameters:
leftNode- Left node.rightNode- Right node.
Returns: True when references are identical.
isSelfConnection
isSelfConnection(
from: default,
to: default,
): boolean
Test whether a connection is a self-loop.
Parameters:
from- Source node.to- Target node.
Returns: True when source and target are the same node.
markVisited
markVisited(
visitedNodes: Set<default>,
node: default,
): void
Mark a node as visited.
Parameters:
visitedNodes- Visited-node set.node- Node to mark.
Returns: Void.
pushOutgoingTargets
pushOutgoingTargets(
nodesToVisitStack: default[],
currentNode: default,
): void
Push non-self outgoing targets to DFS stack.
Parameters:
nodesToVisitStack- DFS stack.currentNode- Current expanded node.
Returns: Void.
takeNextStackNode
takeNextStackNode(
nodesToVisitStack: default[],
): default
Pop and return next DFS stack node.
Parameters:
nodesToVisitStack- DFS stack.
Returns: Next node to process.
traversePathSearch
traversePathSearch(
searchContext: PathSearchContext,
): boolean
Traverse DFS search stack and test reachability.
Parameters:
searchContext- Mutable search context.
Returns: True when target node is reachable.
architecture/network/topology/network.topology.setup.utils.ts
applyIncomingEdgeCounts
applyIncomingEdgeCounts(
buildContext: TopologyBuildContext,
): void
Apply in-degree increments from non-self connections.
Parameters:
buildContext- Mutable build context.
Returns: Void.
asTopologyProps
asTopologyProps(
network: default,
): TopologyNetworkProps
Cast network to internal topology props view.
Parameters:
network- Network instance.
Returns: Internal topology props view.
clearCachedTopoOrder
clearCachedTopoOrder(
internalTopologyProps: TopologyNetworkProps,
): void
Clear cached topological order state.
Parameters:
internalTopologyProps- Internal topology props view.
Returns: Void.
createTopologyBuildContext
createTopologyBuildContext(
network: default,
internalTopologyProps: TopologyNetworkProps,
): TopologyBuildContext
Create mutable build context for Kahn traversal.
Parameters:
network- Network instance.internalTopologyProps- Internal topology props view.
Returns: Initialized build context.
finalizeTopoOrder
finalizeTopoOrder(
buildContext: TopologyBuildContext,
): void
Finalize cached order, falling back to raw node order on cycle detection.
Parameters:
buildContext- Mutable build context.
Returns: Void.
incrementNodeInDegree
incrementNodeInDegree(
buildContext: TopologyBuildContext,
node: default,
): void
Increment in-degree for a node in the tally map.
Parameters:
buildContext- Mutable build context.node- Target node.
Returns: Void.
initializeAllNodeInDegreeCounts
initializeAllNodeInDegreeCounts(
buildContext: TopologyBuildContext,
): void
Initialize all nodes with zero in-degree.
Parameters:
buildContext- Mutable build context.
Returns: Void.
isSelfConnection
isSelfConnection(
from: default,
to: default,
): boolean
Test whether a connection is a self-loop.
Parameters:
from- Source node.to- Target node.
Returns: True when source and target are the same node.
resolveFinalOrder
resolveFinalOrder(
buildContext: TopologyBuildContext,
): default[]
Resolve final topological order with cycle fallback.
Parameters:
buildContext- Mutable build context.
Returns: Fully valid topological order or raw node order fallback.
shouldUseRawNodeOrder
shouldUseRawNodeOrder(
internalTopologyProps: TopologyNetworkProps,
): boolean
Determine whether topological order should be bypassed.
Parameters:
internalTopologyProps- Internal topology props view.
Returns: True when acyclic mode is disabled.
architecture/network/topology/network.topology.factory.utils.ts
addOutgoingConnectionsToSet
addOutgoingConnectionsToSet(
outgoingConnections: default[],
allConnections: Set<default>,
): void
Add all outgoing connections to a deduplication set.
Parameters:
outgoingConnections- Outgoing connections from one node.allConnections- Deduplication set for network connections.
assignNetworkNodes
assignNetworkNodes(
networkInstance: default,
mlpNodeLayers: MlpNodeLayers,
): void
Assign ordered nodes to the network instance.
Parameters:
networkInstance- Target network.mlpNodeLayers- Grouped node layers for this MLP.
collectUniqueOutgoingConnections
collectUniqueOutgoingConnections(
networkInstance: default,
): Set<default>
Collect unique outgoing connections across all network nodes.
Parameters:
networkInstance- Target network.
Returns: Set of unique outgoing connections.
connectLayerPair
connectLayerPair(
sourceLayer: default[],
targetLayer: default[],
): void
Fully connect every source node to every target node.
Parameters:
sourceLayer- Source layer.targetLayer- Target layer.
connectMlpLayers
connectMlpLayers(
mlpNodeLayers: MlpNodeLayers,
): void
Fully connect each adjacent layer in MLP order.
Parameters:
mlpNodeLayers- Grouped node layers for this MLP.
convertConnectionSetToArray
convertConnectionSetToArray(
uniqueConnections: Set<default>,
): default[]
Convert a connection set into the canonical array format.
Parameters:
uniqueConnections- Unique network connections.
Returns: Array of network connections.
createHiddenLayers
createHiddenLayers(
hiddenCounts: number[],
): default[][]
Create all hidden layers for an MLP topology.
Parameters:
hiddenCounts- Hidden-layer node counts.
Returns: Hidden layers in forward order.
createMLP
createMLP(
inputCount: number,
hiddenCounts: number[],
outputCount: number,
): default
Build a strictly layered and fully connected MLP network.
Parameters:
this- Network constructor.inputCount- Number of input nodes.hiddenCounts- Hidden-layer node counts.outputCount- Number of output nodes.
Returns: Newly created MLP network.
createMlpNodeLayers
createMlpNodeLayers(
inputCount: number,
hiddenCounts: number[],
outputCount: number,
): MlpNodeLayers
Build input, hidden, and output node layers for an MLP topology.
Parameters:
inputCount- Number of input nodes.hiddenCounts- Hidden-layer node counts.outputCount- Number of output nodes.
Returns: Grouped node layers for MLP assembly.
createNodesOfType
createNodesOfType(
nodeCount: number,
nodeType: "input" | "output" | "hidden",
): default[]
Create all nodes for a single fixed node type.
Parameters:
nodeCount- Number of nodes to create.nodeType- Node type identifier.
Returns: Node list of the requested type.
createOrderedNodeList
createOrderedNodeList(
mlpNodeLayers: MlpNodeLayers,
): default[]
Build the canonical ordered node list used by the network.
Parameters:
mlpNodeLayers- Grouped node layers for this MLP.
Returns: Ordered node list: input, hidden, then output.
flattenNodeLayers
flattenNodeLayers(
nodeLayers: default[][],
): default[]
Flatten layered node collections into a single ordered list.
Parameters:
nodeLayers- Layered node collections.
Returns: Flattened node list.
instantiateNetwork
instantiateNetwork(
networkFactory: NetworkConstructor,
inputCount: number,
outputCount: number,
): default
Instantiate a new network using the runtime constructor.
Parameters:
networkFactory- Network constructor function.inputCount- Number of input nodes.outputCount- Number of output nodes.
Returns: Newly instantiated network.
markTopologyDirty
markTopologyDirty(
networkInstance: default,
): void
Mark a network topology as dirty after structural edits.
Parameters:
networkInstance- Network instance to mark.
rebuildConnections
rebuildConnections(
networkInstance: default,
): void
Rebuild the canonical connection array from per-node outgoing lists.
Parameters:
networkInstance- Target network.
architecture/network/topology/network.topology.contract.utils.ts
getTopologyIntent
getTopologyIntent(): NetworkTopologyIntent
Read the public topology intent preserved on a network instance.
This accessor keeps the semantic contract visible to callers even though the lower-level runtime ultimately enforces acyclicity through booleans and cache invalidation.
Parameters:
this- Target network instance.
Returns: Current topology intent.
setEnforceAcyclic
setEnforceAcyclic(
flag: boolean,
): void
Toggle low-level acyclic enforcement while preserving a coherent public contract.
This exists for backward compatibility with callers that still use the legacy
boolean API instead of the semantic topologyIntent field.
Parameters:
this- Target network instance.flag- Whether to enforce acyclic connectivity.
Returns: Nothing.
setTopologyIntent
setTopologyIntent(
topologyIntent: NetworkTopologyIntent,
): void
Set the public topology intent and synchronize low-level runtime flags.
Updating the semantic contract also updates acyclic enforcement and marks the topological cache dirty so later activation paths rebuild consistent state.
Parameters:
this- Target network instance.topologyIntent- Desired topology intent.
Returns: Nothing.
architecture/network/topology/network.topology.architecture.utils.ts
createArchitectureDescriptor
createArchitectureDescriptor(
hiddenLayerSizes: number[],
hasCycles: boolean,
source: NetworkArchitectureSource,
totalNodes: number,
totalConnections: number,
): NetworkArchitectureDescriptor
Creates the final immutable descriptor shape used by telemetry and UI code.
Keeping descriptor assembly in one place ensures every resolution strategy returns the same payload contract and avoids accidental field drift.
Parameters:
hiddenLayerSizes- - Hidden-layer widths.hasCycles- - Whether cycles were detected.source- - Descriptor provenance.totalNodes- - Node count.totalConnections- - Connection count.
Returns: Descriptor object.
Example:
const descriptor = createArchitectureDescriptor([6, 3], false, 'graph-topology', 14, 25);
// descriptor.totalNodes === 14
createDirectedEdgeList
createDirectedEdgeList(
runtimeConnections: RuntimeConnectionLike[],
nodeByIndex: Map<number, RuntimeNodeLike>,
): { fromIndex: number; toIndex: number; }[]
Produces a validated list of enabled directed edges.
Invalid references, disabled connections, and self-loops are removed so the remaining edge list can be consumed safely by cycle and depth algorithms.
Parameters:
runtimeConnections- - Runtime connections.nodeByIndex- - Indexed nodes.
Returns: Valid directed edges.
Example:
const edges = createDirectedEdgeList(runtimeConnections, nodeByIndex);
// edges -> [{ fromIndex: 0, toIndex: 3 }, ...]
createNodeIndexMap
createNodeIndexMap(
runtimeNodes: RuntimeNodeLike[],
): Map<number, RuntimeNodeLike>
Builds a node lookup table keyed by stable index.
Runtime objects may omit index; in that case the current array position is
used as a deterministic fallback to keep downstream graph logic total.
Parameters:
runtimeNodes- - Runtime nodes.
Returns: Node map keyed by stable node index.
Example:
const nodeByIndex = createNodeIndexMap(nodes);
// nodeByIndex.get(0) -> first node or node with explicit index 0
describeArchitecture
describeArchitecture(
network: default,
): NetworkArchitectureDescriptor
Describes network architecture for diagnostics, telemetry, and UI rendering.
This function prefers factual sources over heuristics so downstream tooling can rely on the descriptor while still receiving useful output for partially specified runtime graphs.
Resolution priority is intentionally explicit:
- node
layermetadata (factual when present) - graph-derived feed-forward depth layering (factual for acyclic graphs)
- hidden-node count fallback (heuristic inference)
Parameters:
network- - Runtime network instance.
Returns: Stable architecture descriptor.
Example:
const descriptor = describeArchitecture(network);
// descriptor.hiddenLayerSizes -> [8, 4]
// descriptor.source -> 'layer-metadata' | 'graph-topology' | 'inferred'
isHiddenNode
isHiddenNode(
runtimeNode: RuntimeNodeLike,
): boolean
Identifies whether a runtime node should be treated as hidden for topology reconstruction and fallback inference.
Parameters:
runtimeNode- - Candidate node.
Returns: True when node type is hidden.
Example:
if (isHiddenNode(node)) {
// Include in hidden-layer counting
}
isHydratedDescriptorCompatible
isHydratedDescriptorCompatible(
network: default,
hydratedDescriptor: NetworkArchitectureDescriptor | undefined,
): boolean
Check whether hydrated descriptor metadata still matches the current graph shape.
Parameters:
network- Runtime network instance.hydratedDescriptor- Optional hydrated descriptor candidate.
Returns: True when hydrated descriptor can safely stand in for the inferred result.
resolveArchitectureDescriptor
resolveArchitectureDescriptor(
network: default,
): NetworkArchitectureDescriptor
Resolve the public architecture descriptor, preferring live graph facts and falling back to hydrated serialization metadata only when the live result is still purely inferred.
This helper keeps the descriptor ownership story in one chapter: topology owns the live analysis while serialization can optionally hydrate a cached descriptor that remains safe to reuse when the runtime graph shape matches.
Parameters:
network- Runtime network instance.
Returns: Public architecture descriptor for telemetry and UI consumers.
resolveCycleStateAndTopoOrder
resolveCycleStateAndTopoOrder(
nodeByIndex: Map<number, RuntimeNodeLike>,
directedEdges: { fromIndex: number; toIndex: number; }[],
): { topologicalOrder: number[]; hasCycles: boolean; }
Resolves cycle presence and, when possible, returns a topological order using Kahn's algorithm.
A complete topological ordering implies an acyclic graph. If some nodes remain unprocessed, at least one cycle exists.
Parameters:
nodeByIndex- - Indexed nodes.directedEdges- - Directed edges.
Returns: Topological order and cycle status.
Example:
const { topologicalOrder, hasCycles } = resolveCycleStateAndTopoOrder(nodeByIndex, edges);
resolveHiddenCountsByDepth
resolveHiddenCountsByDepth(
nodeByIndex: Map<number, RuntimeNodeLike>,
depthByNodeIndex: Map<number, number>,
): Map<number, number>
Aggregates hidden-node counts per derived depth.
This is the final transformation before emitting architecture widths: hidden nodes are grouped by depth and counted in insertion-safe maps.
Parameters:
nodeByIndex- - Indexed nodes.depthByNodeIndex- - Derived depths.
Returns: Hidden-node counts by depth.
Example:
const hiddenCountsByDepth = resolveHiddenCountsByDepth(nodeByIndex, depthByNodeIndex);
resolveHiddenLayerSizesFromGraphTopology
resolveHiddenLayerSizesFromGraphTopology(
runtimeNodes: RuntimeNodeLike[],
runtimeConnections: RuntimeConnectionLike[],
): { hiddenLayerSizes: number[]; hasCycles: boolean; }
Derives hidden-layer widths from graph topology when no explicit layer metadata is available.
The method computes a topological depth model for acyclic graphs; cyclic graphs are flagged and intentionally return no width inference because depth is not well-defined in recurrent loops.
Parameters:
runtimeNodes- - Runtime nodes.runtimeConnections- - Runtime connections.
Returns: Hidden-layer widths derived from acyclic topology and cycle flag.
Example:
const { hiddenLayerSizes, hasCycles } = resolveHiddenLayerSizesFromGraphTopology(nodes, edges);
resolveHiddenLayerSizesFromLayerMetadata
resolveHiddenLayerSizesFromLayerMetadata(
runtimeNodes: RuntimeNodeLike[],
): number[]
Resolves hidden-layer widths from explicit node.layer metadata.
This is treated as the most trustworthy source because layer assignment is usually produced by architecture-aware builders and does not depend on topological reconstruction.
Parameters:
runtimeNodes- - Runtime nodes.
Returns: Hidden-layer widths from explicit node.layer metadata.
Example:
// Hidden nodes in layers 1, 1, and 2 -> [2, 1]
const sizes = resolveHiddenLayerSizesFromLayerMetadata(nodes);
resolveNodeDepthByIndex
resolveNodeDepthByIndex(
nodeByIndex: Map<number, RuntimeNodeLike>,
directedEdges: { fromIndex: number; toIndex: number; }[],
topologicalOrder: number[],
): Map<number, number>
Computes node depth (feed-forward distance from inputs) for an acyclic graph.
Depth assignment is parent-driven: each node depth is one plus the maximum resolved parent depth. Nodes with no resolved parents are skipped.
Parameters:
nodeByIndex- - Indexed nodes.directedEdges- - Directed edges.topologicalOrder- - Acyclic topological order.
Returns: Derived depth by node index.
Example:
const depthByNodeIndex = resolveNodeDepthByIndex(nodeByIndex, edges, topologicalOrder);