pattern
Safe HaskellSafe-Inferred
LanguageHaskell2010

Pattern.Graph.Transform

Description

Graph transformation operations over GraphView.

This module provides bulk transformation, filtering, folding, and iterative shape-aware algorithms for graphs represented as GraphView.

Overview

Transformations operate lazily over GraphView and are composed by function composition. Finalize a pipeline by calling materialize.

Elements are classified by shape (via classifyByShape) into five buckets: GNode, GRelationship, GWalk, GAnnotation, and GOther. The topoShapeSort function orders elements within a GraphView so that each element is processed after all elements it structurally depends on.

Example

pipeline :: PatternGraph Subject -> PatternGraph Subject
pipeline graph =
  Pattern.PatternGraph.materialize canonicalClassifier LastWriteWins
  . mapWithContext canonicalClassifier enrich
  . filterGraph canonicalClassifier isRelevant dissolve
  . mapAllGraph updateTimestamp
  . Pattern.PatternGraph.toGraphView canonicalClassifier
  $ graph
Synopsis

Graph construction from seeds

unfoldGraph :: (GraphValue v, Eq v, Mergeable v, HasIdentity v (Id v), Refinable v) => GraphClassifier extra v -> ReconciliationPolicy (MergeStrategy v) -> (a -> [Pattern v]) -> [a] -> PatternGraph extra v Source #

Build a PatternGraph by expanding seed values into patterns and merging.

Each seed is expanded into a list of 'Pattern v' via the provided function. All resulting patterns are merged into a single PatternGraph using the given reconciliation policy.

Example

rowToPatterns :: Row -> [Pattern Subject]
rowToPatterns row = [ personNode row, departmentNode row, worksInRel row ]

etlGraph = unfoldGraph canonicalClassifier LastWriteWins rowToPatterns rows

Bulk transformations

mapGraph Source #

Arguments

:: GraphClassifier extra v 
-> (Pattern v -> Pattern v)

nodes

-> (Pattern v -> Pattern v)

relationships

-> (Pattern v -> Pattern v)

walks

-> (Pattern v -> Pattern v)

annotations

-> (Pattern v -> Pattern v)

other / unrecognized

-> GraphView extra v 
-> GraphView extra v 

Map over view elements, applying a different function per GraphClass.

Each element in viewElements is transformed by the function corresponding to its category. The viewQuery is passed through unchanged.

mapAllGraph :: (Pattern v -> Pattern v) -> GraphView extra v -> GraphView extra v Source #

Apply a single function uniformly to every element in the view.

filterGraph :: GraphClassifier extra v -> (GraphClass extra -> Pattern v -> Bool) -> Substitution v -> GraphView extra v -> GraphView extra v Source #

Filter elements from a GraphView, repairing container gaps via Substitution.

Elements that do not satisfy the predicate are removed. Container structures (walks, annotations) whose internal elements are removed are repaired according to the Substitution strategy.

foldGraph :: Monoid m => (GraphClass extra -> Pattern v -> m) -> GraphView extra v -> m Source #

Reduce all view elements into a single Monoid accumulation.

Context-aware enrichment

mapWithContext :: GraphClassifier extra v -> (GraphQuery v -> Pattern v -> Pattern v) -> GraphView extra v -> GraphView extra v Source #

Map over view elements with access to the original snapshot GraphQuery.

The mapping function receives the unmodified query from the original view, ensuring deterministic snapshot semantics: later elements do not see mutations applied to earlier elements.

Iterative topology-aware algorithms

scopeDictFromGraphView :: GraphValue v => GraphView extra v -> ScopeDict (Id v) v Source #

Reify the generic scope answers for a GraphView snapshot.

This exposes the unified scope model without exporting the internal adapter type used to compute it.

Invariants:

  • allElements covers every classified element in the GraphView.
  • byIdentity is bounded to that snapshot.
  • containers and siblings are derived from direct containment only.
  • Duplicate graph identities are rejected when reifying generic scope answers, because ScopeQuery lookup is keyed only by 'Id v'.

Example:

let scope = scopeDictFromGraphView view
length (allElements scope)

paraGraph :: GraphValue v => (GraphQuery v -> Pattern v -> [r] -> r) -> GraphView extra v -> Map (Id v) r Source #

Single-pass structure-aware fold over a GraphView.

Elements are processed in containment order via topoShapeSort: each element receives already-computed results for its direct sub-elements.

Processing Order

Determined by topoShapeSort:

  1. Nodes (atomic — no sub-elements)
  2. Relationships (contain nodes)
  3. Walks (contain relationships)
  4. Annotations (contain any element type, including other annotations)
  5. Other (GOther — unconstrained sub-elements)

Within the Annotation and Other buckets, elements are additionally sorted so that referenced elements appear before the elements that reference them.

The subResults Contract

The [r] argument received by f contains the results of all direct sub-elements of the current element that have already been processed.

If a sub-element has not yet been processed — which can occur when a dependency cycle exists within a bucket — its result is omitted from subResults. The list will be shorter than elements p. Callers should treat subResults as a best-effort list, not a guaranteed complete list.

Example

-- Count the number of sub-results (i.e., direct dependencies) for each element
countDeps :: GraphValue v => GraphView extra v -> Map (Id v) Int
countDeps = paraGraph (\_ _ subResults -> length subResults)

paraGraphFixed :: (GraphValue v, Ord (Id v)) => (r -> r -> Bool) -> (GraphQuery v -> Pattern v -> [r] -> r) -> r -> GraphView extra v -> Map (Id v) r Source #

Iterate paraGraph rounds until the convergence predicate is satisfied.

Each round applies topoShapeSort to ensure correct within-bucket dependency ordering. The ordering is recomputed identically every round (the GraphView is immutable).

See paraGraph for the subResults contract, including cycle behaviour.

The convergence predicate conv old new should return True when the result is considered stable. A common example for floating-point algorithms:

\old new -> abs (old - new) < 0.0001

The initial value r0 is used for all elements in the first round.