Projections
Projections transform RDF graph structures into simplified forms useful for visualization, LPG-style analysis, or downstream consumption. They bridge the gap between the semantic richness of RDF named graphs and the operational needs of visualization tools, NetworkX pipelines, and JSON APIs.
Two Modes
Graph-to-Graph (CONSTRUCT)
Stays in RDF. Expressed as SPARQL CONSTRUCT queries. Results can be stored as named graphs in the holarchy, traversed by portals, and queried via SPARQL. Use this mode when the output will be consumed by another RDF-aware system or when you want to store a simplified view inside the holarchy.
from holonic.projections import build_construct, CONSTRUCT_STRIP_TYPES
# Build and execute a CONSTRUCT
query = build_construct(CONSTRUCT_STRIP_TYPES, graph_iri="urn:holon:x/interior")
result_graph = ds.construct(query)
Graph-to-Structure (Pythonic)
Exits RDF into Python dicts, ProjectedGraph instances, or other structures. Use this mode for the “last mile” to visualization, JSON export, or LPG tools like NetworkX.
from holonic.projections import project_to_lpg
lpg = project_to_lpg(graph,
collapse_types=True,
collapse_literals=True,
resolve_blanks=True,
resolve_lists=True,
)
# lpg.nodes: dict[str, ProjectedNode]
# lpg.edges: list[ProjectedEdge]
# lpg.to_dict(): JSON-serializable
What Gets Collapsed
Type Collapse
rdf:type triples become a types: list[str] attribute on the node rather than edges to type nodes. This is what every graph visualization tool wants — the type is metadata about the node, not a relationship to display.
# RDF:
ex:alice rdf:type ex:Person .
# LPG projection:
Node(iri="urn:ex:alice", types=["urn:ex:Person"], ...)
Literal Collapse
Literal-valued triples become entries in the node’s attributes dict. Multi-valued properties accumulate into lists.
# RDF:
ex:alice ex:age 30 .
ex:alice ex:name "Alice" .
# LPG projection:
Node(iri="urn:ex:alice", attributes={"urn:ex:age": 30, "urn:ex:name": "Alice"})
Blank Node Resolution
Blank nodes that serve as structured values (addresses, sensor suites, configurations) are inlined as nested dicts on their parent node.
# RDF:
ex:alice ex:address _:addr .
_:addr ex:city "Vancouver" .
_:addr ex:street "123 Main St" .
# LPG projection:
Node(iri="urn:ex:alice", attributes={
"urn:ex:address": {"urn:ex:city": "Vancouver", "urn:ex:street": "123 Main St"}
})
RDF List Resolution
rdf:first/rdf:rest chains are resolved into Python lists.
# RDF:
ex:alice ex:skills _:list .
_:list rdf:first "Python" ; rdf:rest _:l2 .
_:l2 rdf:first "SPARQL" ; rdf:rest rdf:nil .
# LPG projection:
Node(iri="urn:ex:alice", attributes={"urn:ex:skills": ["Python", "SPARQL"]})
Reification Collapse
rdf:Statement instances are unfolded into direct edges. Non-structural properties on the statement (confidence, source, timestamp) become edge attributes — the LPG representation of properties on edges.
from holonic.projections import collapse_reification
lpg = collapse_reification(graph, preserve_metadata=True)
# Edge(source=alice, predicate=knows, target=bob,
# attributes={"confidence": 0.92, "source": "survey-2026"})
Built-in CONSTRUCT Templates
Template |
Description |
|---|---|
|
Remove all |
|
Keep only IRI objects (topology) |
|
Keep only literal objects |
|
Unfold |
|
Extract |
|
Extract |
All templates accept an optional graph_iri parameter for GRAPH scoping:
q = build_construct(CONSTRUCT_STRIP_TYPES, graph_iri="urn:holon:x/interior")
Projection Pipelines
Chain CONSTRUCT and Python transform steps into composable pipelines. Fluent API.
from holonic.projections import (
ProjectionPipeline,
CONSTRUCT_STRIP_TYPES,
strip_blank_nodes,
localize_predicates,
)
lpg = (
ProjectionPipeline("viz-prep")
.add_construct("strip_types", CONSTRUCT_STRIP_TYPES)
.add_transform("strip_blanks", strip_blank_nodes)
.add_transform("localize", localize_predicates)
.apply_to_lpg(source_graph)
)
Steps execute sequentially. Each step receives the output of the previous step. .apply() returns the final Graph; .apply_to_lpg() converts the final graph to a ProjectedGraph.
Client Integration
HolonicDataset provides three projection methods:
project_holon(holon_iri, store_as=..., **kwargs)
Merges all of a holon’s interior graphs and projects to LPG. Optionally stores the result as a projection named graph (cga:hasProjection).
lpg = ds.project_holon("urn:holon:air-picture",
store_as="urn:holon:air-picture/projection/viz",
collapse_types=True,
resolve_blanks=True,
)
project_holarchy(**kwargs)
Projects the holarchy structure itself — holons as nodes, portals and cga:memberOf as edges. Useful for visualizing the topology of the holarchy.
topo = ds.project_holarchy()
# topo.nodes: holons and portals
# topo.edges: membership and portal connections
apply_pipeline(holon_iri, pipeline, store_as=...)
Applies a ProjectionPipeline to a holon’s merged interiors.
result_graph = ds.apply_pipeline(
"urn:holon:sensor-fused",
my_pipeline,
store_as="urn:holon:sensor-fused/projection/filtered",
)
Utility Functions
Function |
Signature |
Description |
|---|---|---|
|
|
IRI → type list mapping |
|
|
Keep only instances of a class |
|
|
Remove all blank-node triples |
|
|
Replace full IRIs with local names |
API Reference
- holonic.projections.project_to_lpg(graph, *, collapse_types=True, collapse_literals=True, resolve_blanks=True, resolve_lists=True, include_predicates=None, exclude_predicates=None)[source]
Project an RDF graph into an LPG-style structure.
This is the primary “exit ramp” from RDF into a property-graph representation suitable for visualization, NetworkX, or LPG tools.
- Parameters:
graph (
Graph) – Source rdflib.Graph.collapse_types (
bool) – If True, rdf:type becomes a node attribute, not an edge.collapse_literals (
bool) – If True, literal-valued triples become node attributes.resolve_blanks (
bool) – If True, blank nodes are inlined as nested dicts on their parent node’s attributes.resolve_lists (
bool) – If True, RDF collections (rdf:first/rdf:rest chains) are resolved into Python lists.include_predicates (
set[str] |None) – If set, only these predicates appear as edges (whitelist).exclude_predicates (
set[str] |None) – If set, these predicates are excluded from edges (blacklist).
- Return type:
- holonic.projections.collapse_reification(graph, *, preserve_metadata=True)[source]
Collapse RDF reification into direct edges with metadata.
rdf:Statement instances become edges. Non-structural properties on the statement (e.g., prov:wasAttributedTo, dct:created) become edge attributes.
- Parameters:
- Return type:
- holonic.projections.build_construct(template, graph_iri=None)[source]
Instantiate a CONSTRUCT template with optional GRAPH scoping.
If
templatecontains the{graph_clause}placeholder, it is treated as a format string (escaped braces{{/}}get un-escaped) and the placeholder is substituted with either aGRAPH <iri>clause or an empty string.If the placeholder is absent, the template is returned as-is — letting callers pass raw CONSTRUCT queries through
add_constructwithout having to escape every brace forstr.format.
- class holonic.projections.ProjectionPipeline(name='projection')[source]
Bases:
objectA composable pipeline of projection steps.
Steps are applied sequentially. Each step takes the output of the previous step as input. Steps can be SPARQL CONSTRUCTs (staying in RDF) or Python functions (Graph→Graph).
The final output can optionally be converted to a ProjectedGraph via project_to_lpg().
> TODO: write metadata about the Pipeline (self-describing) w/n holon boundary (new opaque portal type?)
Example:
```python pipeline = ProjectionPipeline(“visualization”) pipeline.add_construct(“strip_types”, CONSTRUCT_STRIP_TYPES, graph_iri=…) pipeline.add_construct(“labels”, CONSTRUCT_LABELS_ONLY, graph_iri=…) pipeline.add_transform(“custom”, my_transform_fn)
result_graph = pipeline.apply(source_graph) lpg = pipeline.apply_to_lpg(source_graph) ```
- steps: list[ProjectionStep]
- add_construct(name, template, graph_iri=None)[source]
Add a CONSTRUCT-based step.
- Return type:
- Parameters:
- add_transform(name, fn)[source]
Add a Python transform step (Graph→Graph).
- Return type:
- Parameters:
- apply_to_graph(source, backend=None)[source]
Apply all steps sequentially, returning the final Graph.
Alias for
apply()that matches the terminal-method naming used in SPEC R7.3 alongsideapply_to_lpg().
- Parameters:
name (str)
- class holonic.projections.ProjectedGraph(nodes=<factory>, edges=<factory>)[source]
Bases:
objectAn LPG-style projection of an RDF graph.
Nodes have types and literal attributes collapsed onto them. Edges carry only object-property relationships. Blank nodes are resolved into inline structures where possible.
- Parameters:
nodes (dict[str, ProjectedNode])
edges (list[ProjectedEdge])
- nodes: dict[str, ProjectedNode]
- edges: list[ProjectedEdge]
- class holonic.projections.ProjectedNode(iri, types=<factory>, attributes=<factory>, label=None)[source]
Bases:
objectA node in a projected graph with collapsed attributes.