graph

package
v0.2.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ComputeCouplingMetrics

func ComputeCouplingMetrics(g DirectedGraph, config CouplingConfig) (map[string]*CouplingMetrics, error)

ComputeCouplingMetrics computes Robert Martin's coupling metrics for all nodes in the directed graph.

func StronglyConnectedComponents added in v0.2.2

func StronglyConnectedComponents(ctx context.Context, g DirectedGraph) ([][]string, error)

StronglyConnectedComponents returns every strongly connected component of g, including single-node components, using Tarjan's algorithm. Components are returned in reverse topological order: a component appears before any component it depends on.

The pass checks ctx once per visited node and returns ctx.Err() as soon as the context is cancelled.

Types

type ChainFinder added in v0.2.2

type ChainFinder struct {
	// contains filtered or unexported fields
}

ChainFinder answers longest-dependency-chain queries over a directed graph.

Finding the longest simple path in a graph that may contain cycles is NP-hard, so a ChainFinder walks the condensation instead: strongly connected components are collapsed to single vertices and the heaviest chains through the resulting DAG are memoized in one linear pass, weighting each component by how many nodes it holds. Each query then expands a component chain into a concrete route through the members of every component it crosses, so every chain returned is a real simple path in the graph.

A chain is maximal in dependency layers: no other simple path crosses more strongly connected components. Within a component the route is not guaranteed maximal — the final component is walked greedily through as many members as it can reach, while components the chain passes through are crossed by the shortest route between the edges that enter and leave them. Recovering the longest route through a cycle is the NP-hard problem again.

Chains are ranked by weight (the node count of the components they cross) and, between equally heavy chains, by the lexicographic order of the components they cross, each component named by its smallest member. That order depends only on the graph, not on traversal, so repeated queries over the same graph return the same chains.

func NewChainFinder added in v0.2.2

func NewChainFinder(ctx context.Context, g DirectedGraph) (*ChainFinder, error)

NewChainFinder builds the condensation of g and memoizes the best chain reachable from every component. Construction is linear in the graph size; each subsequent query is linear in the length of the chain it returns.

The condensation and the memoizing pass check ctx as they go and return ctx.Err() as soon as the context is cancelled.

func (*ChainFinder) LongestChain added in v0.2.2

func (f *ChainFinder) LongestChain() []string

LongestChain returns the longest chain anywhere in the graph.

func (*ChainFinder) LongestChainFrom added in v0.2.2

func (f *ChainFinder) LongestChainFrom(nodeID string) []string

LongestChainFrom returns the longest chain that starts at nodeID, or nil if the node is not in the graph.

func (*ChainFinder) LongestChains added in v0.2.3

func (f *ChainFinder) LongestChains(ctx context.Context, limit int) ([][]string, error)

LongestChains returns the limit best-ranked chains in the graph, best first. Chains may start anywhere, so a reported chain can be the tail of another. Every chain contains at least one edge: a node that depends on nothing is not a chain, so a graph without edges has none. A limit of zero or less returns nil.

The ranking pass is linear in the graph size times limit and checks ctx as it goes, returning ctx.Err() as soon as the context is cancelled.

type CouplingConfig

type CouplingConfig struct {
	// AbstractnessFunc computes the abstractness (0.0–1.0) for a given node.
	// pyscn: ratio of public names matching abstract patterns.
	// jscan: export ratio.
	// If nil, Abstractness defaults to 0.0 for all nodes.
	AbstractnessFunc func(nodeID string) (float64, error)
}

CouplingConfig configures coupling metric computation.

type CouplingMetrics

type CouplingMetrics struct {
	NodeID       string
	Ca           int     // Afferent coupling (incoming edges)
	Ce           int     // Efferent coupling (outgoing edges)
	Instability  float64 // Ce / (Ca + Ce), 0 = maximally stable
	Abstractness float64 // Provided by language-specific callback
	Distance     float64 // |Abstractness + Instability - 1|
}

CouplingMetrics holds Robert Martin's package coupling metrics for a node.

type CycleDetector

type CycleDetector struct {
	// contains filtered or unexported fields
}

CycleDetector finds strongly connected components using Tarjan's algorithm.

func NewCycleDetector

func NewCycleDetector() *CycleDetector

NewCycleDetector creates a new CycleDetector.

func (*CycleDetector) DetectCycles

func (d *CycleDetector) DetectCycles(g DirectedGraph) *CycleResult

DetectCycles finds all cycles (SCCs with size > 1) in the directed graph.

type CycleResult

type CycleResult struct {
	// Cycles contains all strongly connected components with more than one node.
	Cycles [][]string
	// HasCycles is true if any cycle was found.
	HasCycles bool
	// AffectedNodes contains all nodes that participate in at least one cycle.
	AffectedNodes map[string]bool
}

CycleResult holds the result of cycle detection via Tarjan's SCC algorithm.

type DirectedGraph

type DirectedGraph interface {
	// NodeIDs returns all node identifiers in the graph.
	NodeIDs() []string
	// Successors returns the IDs of nodes that this node has edges to.
	Successors(nodeID string) []string
	// Predecessors returns the IDs of nodes that have edges to this node.
	Predecessors(nodeID string) []string
	// NodeCount returns the number of nodes in the graph.
	NodeCount() int
	// HasNode returns true if the node exists in the graph.
	HasNode(nodeID string) bool
}

DirectedGraph provides read-only access to a directed graph. pyscn's DependencyGraph (map[string]*ModuleNode) and jscan's domain.DependencyGraph (method-based) can both implement this.

type MapGraph

type MapGraph struct {
	// contains filtered or unexported fields
}

MapGraph is a simple directed graph implementation backed by maps. Useful for testing and as a default implementation.

func NewMapGraph

func NewMapGraph() *MapGraph

NewMapGraph creates a new empty MapGraph.

func (*MapGraph) AddEdge

func (g *MapGraph) AddEdge(from, to string)

AddEdge adds a directed edge from → to. Both nodes are created if absent.

func (*MapGraph) AddNode

func (g *MapGraph) AddNode(id string)

AddNode adds a node to the graph. No-op if already present.

func (*MapGraph) HasNode

func (g *MapGraph) HasNode(nodeID string) bool

HasNode returns true if the node exists.

func (*MapGraph) NodeCount

func (g *MapGraph) NodeCount() int

NodeCount returns the number of nodes.

func (*MapGraph) NodeIDs

func (g *MapGraph) NodeIDs() []string

NodeIDs returns all node identifiers sorted lexicographically.

func (*MapGraph) Predecessors

func (g *MapGraph) Predecessors(nodeID string) []string

Predecessors returns the IDs of nodes that have an edge to nodeID.

func (*MapGraph) Successors

func (g *MapGraph) Successors(nodeID string) []string

Successors returns the IDs of nodes reachable by one edge from nodeID.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL