# Radial Sugiyama vs Go Snapshot Pipeline

This note delimits the algorithmic intersection between the Rust pipeline in `radial_sugiyama/` and the Go snapshot/export path in:

- `backend_go/graph_export.go`
- `backend_go/graph_snapshot.go`

The goal is not to describe integration mechanics yet, but to mark where the two implementations solve the same problem, where they only touch indirectly, and where they are solving different problems.

## Scope

The Rust pipeline is a hierarchy-specific layout pipeline:

1. import ontology hierarchy from Turtle
2. optionally filter to a rooted descendant subtree
3. validate DAG structure
4. assign hierarchy levels
5. insert dummy nodes for long edges
6. reduce crossings
7. assign coordinates
8. project to radial space
9. generate routed edge artifacts
10. export SVG

The Go path is a snapshot/materialization pipeline:

1. query predicates and edges from SPARQL
2. accumulate nodes and edges
3. build a graph response
4. run a lightweight hierarchy layering + radial placement
5. attach labels
6. return JSON to the frontend

Because of that, the true intersection is narrow in `graph_export.go` and broader in the layout section of `graph_snapshot.go`.

## Legend

- `Direct overlap`: both sides implement essentially the same algorithmic concern
- `Adjacent overlap`: one side prepares or consumes the same kind of structure, but the algorithm differs materially
- `No overlap`: the stage exists only on one side

## Intersection with `graph_export.go`

`graph_export.go` overlaps with the Rust pipeline only at graph materialization time.

| Algorithmic stage | Rust pipeline | `graph_export.go` | Intersection | Notes |
| --- | --- | --- | --- | --- |
| Node identity and deduplication | `ttl.rs` maps class IRIs to stable node indices | `graphAccumulator.getOrAddNode` maps SPARQL terms to stable node IDs | Direct overlap | Both build a unique node set from repeated source records. |
| Edge materialization | `ttl.rs` emits `superclass -> subclass` edges and deduplicates repeats | `graphAccumulator.addBindings` emits `source -> target` edges from SPARQL bindings | Adjacent overlap | Both convert raw triples/bindings into an in-memory graph, but Rust is specialized to `rdfs:subClassOf` while Go is predicate-agnostic. |
| Literal / blank-node filtering | `ttl.rs` ignores blank/literal hierarchy endpoints | `getOrAddNode` skips literals and optionally keeps blank nodes | Adjacent overlap | Similar sanitation step, but not identical semantics. |
| Predicate preservation | Rust discards all predicates except `rdfs:subClassOf` | Go preserves predicate IDs through `PredicateDict` | No overlap | This is Go-only in the compared files. |
| Graph limits / capacity management | Rust does not enforce snapshot-style node and edge caps here | Go enforces `nodeLimit` and preallocates with edge hints | No overlap | This is an operational concern of the Go snapshot path. |

### Boundary for `graph_export.go`

The clean algorithmic seam is:

- Go owns generic SPARQL binding ingestion and generic graph materialization.
- Rust owns hierarchy-specialized interpretation once a hierarchy graph has already been isolated.

That means `graph_export.go` is not competing with the Rust layout pipeline. It is only producing the kind of node/edge structure that Rust would eventually need as input.

## Intersection with `graph_snapshot.go`

`graph_snapshot.go` intersects with the Rust pipeline in two different regions:

1. graph acquisition and hierarchy preparation
2. lightweight layout assignment

## Stage-by-stage comparison

| Algorithmic stage | Rust pipeline | `graph_snapshot.go` | Intersection | Notes |
| --- | --- | --- | --- | --- |
| Source acquisition | `graph_from_ttl_path` parses Turtle directly | `fetchGraphSnapshot` queries SPARQL in batches | Adjacent overlap | Both acquire a graph, but from different upstream sources. |
| Hierarchy graph extraction | Rust keeps only `rdfs:subClassOf` during import | Go accepts a `graph_query_id` and accumulates whatever that query returns | Adjacent overlap | The overlap is meaningful only when the Go query is hierarchy-like. |
| Rooted subtree filtering | `filter_graph_to_descendants` keeps one configured root and its descendants | No equivalent in these two Go files | No overlap | This is currently Rust-only. |
| Cycle detection / DAG validation | `compute_hierarchy_levels` rejects cyclic graphs | `levelSynchronousKahnLayers` returns a `CycleError` if not all nodes are processed | Direct overlap | Both need a DAG to continue with hierarchy layout. |
| Level assignment | Rust computes longest-path hierarchy levels | Go computes level-synchronous Kahn layers | Direct overlap | Same problem, different algorithm. Both assign ring depth from DAG structure. |
| Per-level ordering | Rust later optimizes order for crossings | Go sorts each layer lexicographically by IRI | Adjacent overlap | Both define an order inside a level, but Go is a simple deterministic ordering while Rust is layout-driven. |
| Radial node placement | Rust projects coordinates to rings after Sugiyama coordinate assignment | Go uses `radialPositionsFromLayers` to place each layer on a ring | Direct overlap | Same output shape, very different sophistication. |
| Coordinate shifting / scaling controls | Rust has configurable radius, spacing, borders, and positive-coordinate shifting | Go uses a fixed `maxR = 5000.0` radial envelope | Adjacent overlap | Both map levels to 2D coordinates, but only Rust exposes tuned geometry controls. |
| Label enrichment | Rust keeps node labels as imported IRIs | Go fetches `rdfs:label` after layout | Adjacent overlap | Both carry node naming, but the enrichment algorithm is currently Go-only. |
| Response packaging | Rust writes SVG and layout artifacts | Go returns `GraphResponse` JSON plus metadata | No overlap | Same graph, different downstream consumers. |

## Rust-only algorithms with no counterpart in the compared Go files

These parts of the Rust pipeline do not currently intersect with `graph_export.go` or `graph_snapshot.go`:

- rooted descendant filtering
- dummy-node insertion for long edges
- crossing reduction / sifting
- coordinate assignment before radial projection
- adaptive / packed / distributed ring projection modes
- routed edge generation
- layout artifact generation
- SVG rendering and export

These are the parts that make the Rust pipeline a true Sugiyama-style layout engine rather than a simple radial snapshot placer.

## Go-only algorithms with no counterpart in the Rust pipeline

These parts of the compared Go files do not currently exist in Rust:

- predicate dictionary construction from SPARQL results
- batched SPARQL edge fetching with memory management
- snapshot limits and backend metadata packaging
- `rdfs:label` lookup through SPARQL
- generic graph export over arbitrary predicate sets

These are acquisition and serving concerns rather than layout concerns.

## Algorithmic ownership boundary

If the future integration wants a clean division of responsibility, the strongest ownership boundary is:

### Go-owned stages

- query execution against AnzoGraph / SPARQL
- predicate-aware graph accumulation
- generic graph snapshot materialization
- label fetching and API response orchestration

### Rust-owned stages

- hierarchy-specific filtering
- hierarchy-level assignment
- Sugiyama expansion with dummy nodes
- crossing minimization
- coordinate assignment
- radial projection and route generation
- layout artifact production

## Most important practical conclusion

At algorithm granularity, the Rust pipeline intersects only lightly with `graph_export.go`, but it intersects substantially with the hierarchy-layout portion of `graph_snapshot.go`.

The main replacement candidates in a future integration are therefore not the generic export/materialization routines in `graph_export.go`, but these hierarchy-layout steps currently performed by `graph_snapshot.go`:

1. DAG validation / cycle detection
2. layer assignment
3. per-layer ordering
4. radial coordinate generation

Everything after that depends on how much of the Rust layout artifact model the future integration wants to expose to the frontend.
