Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .cursor/rules/specify-rules.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Auto-generated from all feature plans. Last updated: 2025-11-04
- In-memory only (Map keyed by Id v); gram files for round-trip via libs/gram parse/serialize (033-pattern-graph)
- Haskell (GHC 9.10.3) + `pattern-hs` ecosystem (`Pattern.Core`, `Subject.Core`) (034-graph-classifier)
- In-memory `Map` via `PatternGraph` (034-graph-classifier)
- Haskell (GHC 9.10.3) + `containers ^>=0.7` (Map, Set), `base >=4.17.0.0`, `subject` (GraphValue/Symbol), `pattern` (Pattern.Core, Pattern.Graph, Pattern.PatternGraph, Pattern.Graph.GraphClassifier) (035-graph-query)
- N/A — pure in-memory data structures (035-graph-query)

- (001-pattern-data-structure)

Expand All @@ -57,9 +59,9 @@ tests/
: Follow standard conventions

## Recent Changes
- 035-graph-query: Added Haskell (GHC 9.10.3) + `containers ^>=0.7` (Map, Set), `base >=4.17.0.0`, `subject` (GraphValue/Symbol), `pattern` (Pattern.Core, Pattern.Graph, Pattern.PatternGraph, Pattern.Graph.GraphClassifier)
- 034-graph-classifier: Added Haskell (GHC 9.10.3) + `pattern-hs` ecosystem (`Pattern.Core`, `Subject.Core`)
- 033-pattern-graph: Added Haskell (GHC 9.10.3 per CLAUDE.md; base >=4.17.0.0 from pattern.cabal) + pattern (Pattern.Core, Pattern.Reconcile), subject, containers, hashable, unordered-containers (from libs/pattern)
- 032-gram-annotation-syntax: Added Haskell (GHC 9.10.3) + megaparsec (parsing), hspec (testing)


<!-- MANUAL ADDITIONS START -->
Expand Down
4 changes: 4 additions & 0 deletions libs/pattern/pattern.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ library
Pattern.Core
Pattern.Graph
Pattern.Graph.GraphClassifier
Pattern.Graph.GraphQuery
Pattern.Graph.Algorithms
Pattern.PatternGraph
Pattern.Reconcile

Expand All @@ -49,6 +51,8 @@ test-suite pattern-test
Spec.Pattern.CoreSpec
Spec.Pattern.GraphSpec
Spec.Pattern.Graph.GraphClassifierSpec
Spec.Pattern.Graph.GraphQuerySpec
Spec.Pattern.Graph.AlgorithmsSpec
Spec.Pattern.PatternGraphProperties
Spec.Pattern.PatternGraphSpec
Spec.Pattern.Properties
Expand Down
17 changes: 16 additions & 1 deletion libs/pattern/src/Pattern.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
--
-- * @Pattern.Core@ - Core Pattern data type, construction functions, query functions,
-- predicate functions, and typeclass instances (Functor, Applicative, Comonad, etc.)
-- * @Pattern.Graph@ - Graph operations and transformations
-- * @Pattern.Graph@ - Low-level graph structural operations (GraphLens, nodes, relationships, incidentRels, etc.)
-- * @Pattern.Graph.GraphQuery@ - Portable graph query interface ('GraphQuery', 'TraversalWeight',
-- 'fromGraphLens', combinators)
-- * @Pattern.Graph.Algorithms@ - Graph algorithms operating on 'GraphQuery' (bfs, dfs,
-- shortestPath, connectedComponents, etc.)
-- * @Pattern.PatternGraph@ - Typed graph container with O(log n) lookups; 'fromPatternGraph'
-- * @Pattern.Reconcile@ - Pattern reconciliation for normalizing duplicate identities
--
-- == Usage
Expand All @@ -23,6 +28,12 @@
-- >>> value p
-- "test"
--
-- For graph algorithms, import the algorithm modules directly:
--
-- > import Pattern.PatternGraph (fromPatternGraph)
-- > import Pattern.Graph.GraphQuery (directed)
-- > import qualified Pattern.Graph.Algorithms as Alg
--
-- All public functions, types, and typeclass instances from Pattern.Core are
-- available through this module. See individual module documentation for
-- detailed information about specific functionality.
Expand All @@ -35,6 +46,7 @@
-- query functions, predicate functions, helper functions, and all typeclass instances)
-- * All public exports from @Pattern.Graph@ (graph operations)
-- * All public exports from @Pattern.Reconcile@ (reconciliation operations)
-- * All public exports from @Pattern.Graph.GraphQuery@ (portable graph query interface)
--
-- Internal implementation details and helper functions are not exported through
-- this module, ensuring a clean public API.
Expand All @@ -43,10 +55,13 @@ module Pattern
module Pattern.Core
-- * Graph Operations
, module Pattern.Graph
-- * Portable Graph Query Interface
, module Pattern.Graph.GraphQuery
-- * Reconciliation Operations
, module Pattern.Reconcile
) where

import Pattern.Core
import Pattern.Graph
import Pattern.Graph.GraphQuery
import Pattern.Reconcile
91 changes: 0 additions & 91 deletions libs/pattern/src/Pattern/Graph.hs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,10 @@ module Pattern.Graph
, neighbors
, incidentRels
, degree
-- * Graph Analysis Operations
, connectedComponents -- Requires Ord v
, bfs -- Requires Ord v
, findPath -- Requires Ord v
) where
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical backward compatibility violation: The functions bfs, findPath, and connectedComponents have been completely removed from Pattern.Graph, breaking existing code that depends on them.

According to FR-011 in the specification (spec.md:115): "The existing bfs, findPath, and connectedComponents functions on GraphLens MUST be retained as backward-compatible wrappers that delegate to the new Algorithms module with undirected as the default TraversalWeight."

The research document (research.md:66-83) also explicitly describes the backward compatibility strategy with example wrapper implementations.

Task T060 states these functions "remain in Pattern.Graph" but they have been removed. This is a breaking API change that will cause compilation failures for any existing code using these functions.

Copilot uses AI. Check for mistakes.

import Pattern.Core (Pattern(..))
import Data.Maybe (mapMaybe)
import qualified Data.Set as Set

import Pattern.Graph.GraphClassifier (GraphValue(..))

Expand Down Expand Up @@ -400,90 +395,4 @@ incidentRels lens node =
degree :: GraphValue v => GraphLens v -> Pattern v -> Int
degree lens node = length (incidentRels lens node)

-- * Graph Analysis Operations

-- | Find all connected components in the graph.
--
-- A connected component is a set of nodes that are reachable from
-- each other via relationships. Returns a list of lists, where each
-- inner list represents a component.
--
-- == Time Complexity
-- O(n + r) where n is number of nodes and r is number of relationships
--
-- == Example
--
-- >>> let lens = GraphLens pattern isAtomic
-- >>> connectedComponents lens
-- [[pattern "A", pattern "B", pattern "C"], [pattern "D", pattern "E"]]
connectedComponents :: GraphValue v => GraphLens v -> [[Pattern v]]
connectedComponents lens = findComponents lens (nodes lens) Set.empty []

findComponents :: GraphValue v => GraphLens v -> [Pattern v] -> Set.Set (Id v) -> [[Pattern v]] -> [[Pattern v]]
findComponents _ [] _ acc = reverse acc
findComponents lens (n:ns) visited acc =
if Set.member (identify (value n)) visited
then findComponents lens ns visited acc
else
let component = bfs lens n
newVisited = Set.union visited (Set.fromList (map (identify . value) component))
newAcc = component : acc
in findComponents lens ns newVisited newAcc

-- | Perform breadth-first search from a starting node.
--
-- Returns all nodes reachable from the starting node via relationships.
--
-- == Time Complexity
-- O(n + r) where n is number of nodes and r is number of relationships
--
-- == Example
--
-- >>> let lens = GraphLens pattern isAtomic
-- >>> bfs lens (point "Alice")
-- [point "Alice", point "Bob", pattern "Charlie"]
bfs :: GraphValue v => GraphLens v -> Pattern v -> [Pattern v]
bfs lens start = bfsHelper lens Set.empty [start] []

bfsHelper :: GraphValue v => GraphLens v -> Set.Set (Id v) -> [Pattern v] -> [Pattern v] -> [Pattern v]
bfsHelper _ _ [] acc = reverse acc
bfsHelper lens visited (n:queue) acc
| Set.member (identify (value n)) visited = bfsHelper lens visited queue acc
| otherwise =
let newVisited = Set.insert (identify (value n)) visited
newAcc = n : acc
nodeNeighbors = Pattern.Graph.neighbors lens n
newQueue = queue ++ filter (\nb -> not (Set.member (identify (value nb)) newVisited)) nodeNeighbors
in bfsHelper lens newVisited newQueue newAcc

-- | Find a path between two nodes if one exists.
--
-- Returns Just [nodes] if a path exists, Nothing otherwise.
-- The path is a sequence of nodes connecting start to end.
--
-- == Time Complexity
-- O(n + r) where n is number of nodes and r is number of relationships
--
-- == Example
--
-- >>> let lens = GraphLens pattern isAtomic
-- >>> findPath lens (point "Alice") (pattern "Charlie")
-- Just [point "Alice", point "Bob", pattern "Charlie"]
findPath :: GraphValue v => GraphLens v -> Pattern v -> Pattern v -> Maybe [Pattern v]
findPath lens start end
| identify (value start) == identify (value end) = Just [start]
| otherwise = findPathHelper lens Set.empty [(start, [start])] end

findPathHelper :: GraphValue v => GraphLens v -> Set.Set (Id v) -> [(Pattern v, [Pattern v])] -> Pattern v -> Maybe [Pattern v]
findPathHelper _ _ [] _ = Nothing
findPathHelper lens visited ((n, path):queue) targetNode
| identify (value n) == identify (value targetNode) = Just (reverse path)
| Set.member (identify (value n)) visited = findPathHelper lens visited queue targetNode
| otherwise =
let newVisited = Set.insert (identify (value n)) visited
nodeNeighbors = Pattern.Graph.neighbors lens n
newPaths = map (\neighbor -> (neighbor, neighbor:path)) nodeNeighbors
unvisitedPaths = filter (\(neighbor, _) -> not (Set.member (identify (value neighbor)) newVisited)) newPaths
newQueue = queue ++ unvisitedPaths
in findPathHelper lens newVisited newQueue targetNode

Loading
Loading