This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
swift build # Build the library
swift test # Run all tests
swift test --filter GraphsTests.DijkstraTests # Run a specific test suite
swift test --filter "GraphsTests.DijkstraTests/findsShortestPath" # Run a single testTests use Swift Testing framework (@Test macro, #expect() assertions), not XCTest.
Two Package.swift variants exist: Package.swift (Swift 5.9+) and Package@swift-6.0.swift (Swift 6 with strict concurrency via .swiftLanguageMode(.v6)).
Configured via .swift-format. Key rules: 4-space indentation, 160 char line length, all public declarations require doc comments (///), no access level on extension declarations, use early exits.
This is a protocol-oriented graph algorithms library. The architecture has four layers:
Composable protocols that graph types conform to — each adds a capability:
Graph— base protocol withVertexDescriptorandEdgeDescriptorassociated typesIncidenceGraph— outgoing edges from a vertex (edges(from:))BidirectionalGraph— adds incoming edgesVertexListGraph/EdgeListGraph— enumerate all vertices/edgesAdjacencyGraph— direct neighbor accessPropertyGraph— vertex and edge properties via subscript (graph[vertex],graph[edge])MutableGraph/MutablePropertyGraph— mutation capabilitiesEdgeLookupGraph— O(1) edge existence checkBinaryIncidenceGraph— tree structures (left/right children)BipartiteGraph— two-partitioned vertex sets
Concrete types conforming to the protocol hierarchy:
AdjacencyList— primary sparse graph implementation, generic over storage backendsAdjacencyMatrix— dense graph with O(1) edge lookupGridGraph— 2D coordinate-based graph for pathfindingInlineGraph— data embedded directly in vertex/edge typesLazyGraph— edges computed on-demand
Storage backends in Storage/ (CSR, COO, ordered, binary, cached bidirectional edges) are pluggable via generic parameters on AdjacencyList.
Each algorithm category defines a protocol (e.g., ShortestPathAlgorithm, TraversalAlgorithm) with associated types for the graph, result, and visitor. Algorithms constrain on specific graph protocols (e.g., shortest path requires IncidenceGraph). The library is inspired by the Boost Graph Library (BGL) and translates BGL's C++ concepts into Swift protocols.
Algorithm protocol families: ShortestPathAlgorithm, TraversalAlgorithm, SearchAlgorithm, ColoringAlgorithm, ConnectedComponentsAlgorithm, MinimumSpanningTreeAlgorithm, MaxFlowAlgorithm, CentralityAlgorithm, HamiltonianPathAlgorithm, EulerianPathAlgorithm, StronglyConnectedComponentsAlgorithm, MatchingAlgorithm, TopologicalSortAlgorithm, CliqueDetectionAlgorithm, CommunityDetectionAlgorithm, IsomorphismAlgorithm, RandomGraphAlgorithm.
Each subdirectory contains one or more implementations of an algorithm protocol. Algorithms are external to graphs (strategy pattern) — they are separate types selected at the call site, not methods baked into graph types.
-
Define the algorithm struct conforming to the appropriate protocol:
struct MyAlgorithm<G: IncidenceGraph, W: Numeric & Comparable>: ShortestPathAlgorithm { typealias Graph = G typealias Weight = W typealias Visitor = DijkstraVisitor<G> let weight: CostDefinition<G, W> }
-
Implement required methods — the algorithm's core logic, calling
visitorhooks at appropriate points. -
Add a static factory method on the protocol using a constrained extension:
extension ShortestPathAlgorithm where Self == MyAlgorithm<some IncidenceGraph, some Numeric & Comparable> { static func myAlgorithm<G, W>(weight: CostDefinition<G, W>) -> MyAlgorithm<G, W> { ... } }
-
Use it — it works automatically with all compatible graphs:
graph.shortestPath(from: a, to: b, using: .myAlgorithm(weight: .property(\.weight)))
CostDefinition<Graph, Cost>— extracts edge weights. Built-in factories:.property(\.weight),.uniform(1.0), or a custom closure.Heuristic<Graph, Cost>— estimates distance for A*/best-first. Built-ins:.euclidean,.manhattanDistance,.chebyshevDistance.
Every algorithm protocol includes an optional Visitor associated type. Visitors observe execution without modifying the algorithm — hooks include events like discoverVertex, examineEdge, edgeRelaxed, treeEdge, backEdge, finishVertex, etc. Visitors are:
- Struct-based with optional closure properties (e.g.,
DFSVisitor<Graph>,DijkstraVisitor<Graph>). - Composable — chain multiple visitors via
.withVisitor()on an algorithm; all receive events. - Zero-cost when absent — passing
nil(the default) incurs no overhead.
Key difference between TraversalAlgorithm and SearchAlgorithm: traversal returns a complete TraversalResult, while search returns a lazy Sequence that supports early termination via break.
- Performance annotations:
@inlinableand@usableFromInlineare used extensively on hot paths. - Property maps:
PropertyMap<Key, Value>andMutablePropertyMapabstract property access across different graph types. Custom properties are defined as types conforming toVertexProperty/EdgePropertywith adefaultValue, then exposed as computed properties onVertexPropertyValues/EdgePropertyValues. - Graph views (
GraphDefinitions/Views/): Lazy transformations like filtered, reversed, mapped, and undirected wrappers over existing graphs — they don't copy data. - Pluggable storage:
AdjacencyListis generic overVertexStore,EdgeStore,VertexPropertyMap, andEdgePropertyMap. Storage types compose via wrappers (e.g.,OrderedEdgeStorage().cacheInOutEdges()adds incoming edge tracking). - Serialization (
Sources/Graphs/Serialization/): DOT, GraphML, and JSON format support.
Tests are in Tests/GraphsTests/ organized by category: Core/, DataStructures/, Algorithms/, Properties/, Serialization/. Test suites are structs with @Test-annotated methods. Test names follow GraphsTests.<SuiteName>/<methodName>().