Skip to content

ontocast.onto.graph_prune

Seed-free graph pruning shared by induced-subgraph retrieval and prompt condensing.

These pruners and predicate vocabularies were written for the vector-retrieval induced-subgraph builder in :mod:ontocast.tool.sparql, which is the only place in the pipeline that ever bounded how much ontology reached the LLM. The prompt condenser (:mod:ontocast.onto.ontology_condense) needs the same judgements about which triples carry the schema and which are scaffolding, so they live here rather than being duplicated: two copies of "what is safe to drop" would drift, and the drift would be invisible until an extraction quietly lost a term.

Everything here operates on a materialized graph with no seed list, no relevance scores and no triple store, which is what makes it reusable outside retrieval.

bfs_triple_rank(triple)

Sort key admitting defining triples before incidental ones, ties lexicographic.

Source code in ontocast/onto/graph_prune.py
def bfs_triple_rank(triple: tuple) -> tuple[int, str]:
    """Sort key admitting defining triples before incidental ones, ties lexicographic."""
    predicate = triple[1]
    for rank, predicates in enumerate(BFS_PREDICATE_PRIORITY):
        if predicate in predicates:
            return (rank, str(triple))
    return (len(BFS_PREDICATE_PRIORITY), str(triple))

count_meaningful_restriction_predicates(graph, bnode)

How many predicates of a restriction bnode actually constrain anything.

Source code in ontocast/onto/graph_prune.py
def count_meaningful_restriction_predicates(graph: Graph, bnode: BNode) -> int:
    """How many predicates of a restriction bnode actually constrain anything."""
    return sum(
        1
        for _, pred, _ in graph.triples((bnode, None, None))
        if pred in OWL_RESTRICTION_MEANINGFUL_PREDICATES
    )

prune_degenerate_restriction_bnodes(result)

Remove stub restriction blank nodes and subClassOf edges pointing to them.

Source code in ontocast/onto/graph_prune.py
def prune_degenerate_restriction_bnodes(result: Graph) -> int:
    """Remove stub restriction blank nodes and subClassOf edges pointing to them."""
    dropped = 0
    bnode_objects = sorted(
        {
            obj
            for _, _, obj in result.triples((None, RDFS.subClassOf, None))
            if isinstance(obj, BNode)
        },
        key=str,
    )
    for bnode in bnode_objects:
        if (
            count_meaningful_restriction_predicates(result, bnode)
            >= MIN_MEANINGFUL_RESTRICTION_PREDICATES
        ):
            continue
        remove_subclassof_to_bnode(result, bnode)
        remove_bnode_subgraph(result, bnode)
        dropped += 1
    return dropped

prune_orphaned_bnode_subjects(graph)

Remove blank-node subjects that no triple in the graph references as object.

Source code in ontocast/onto/graph_prune.py
def prune_orphaned_bnode_subjects(graph: Graph) -> None:
    """Remove blank-node subjects that no triple in the graph references as object."""
    bnode_as_object: set[BNode] = {o for _, _, o in graph if isinstance(o, BNode)}
    for triple in list(graph):
        subj, _, _ = triple
        if isinstance(subj, BNode) and subj not in bnode_as_object:
            graph.remove(triple)

remove_bnode_subgraph(graph, bnode)

Remove every triple asserted about a blank node.

Source code in ontocast/onto/graph_prune.py
def remove_bnode_subgraph(graph: Graph, bnode: BNode) -> None:
    """Remove every triple asserted about a blank node."""
    for triple in list(graph.triples((bnode, None, None))):
        graph.remove(triple)

remove_subclassof_to_bnode(graph, bnode)

Remove the class-axiom edges pointing at a blank node.

Source code in ontocast/onto/graph_prune.py
def remove_subclassof_to_bnode(graph: Graph, bnode: BNode) -> None:
    """Remove the class-axiom edges pointing at a blank node."""
    for triple in list(graph.triples((None, RDFS.subClassOf, bnode))):
        graph.remove(triple)
    for triple in list(graph.triples((None, OWL.equivalentClass, bnode))):
        graph.remove(triple)

strip_redundant_generic_types(graph)

Drop generic rdf:types when the subject has informative types or URI hierarchy.

Source code in ontocast/onto/graph_prune.py
def strip_redundant_generic_types(graph: Graph) -> None:
    """Drop generic rdf:types when the subject has informative types or URI hierarchy."""
    for subj, pred, obj in list(graph):
        if pred != RDF.type or obj not in GENERIC_INDIVIDUAL_TYPES:
            continue
        other_types = [
            term
            for _, _, term in graph.triples((subj, RDF.type, None))
            if term not in GENERIC_INDIVIDUAL_TYPES
        ]
        has_subclass_uri = any(
            isinstance(parent, URIRef)
            for _, _, parent in graph.triples((subj, RDFS.subClassOf, None))
        )
        if other_types or has_subclass_uri:
            graph.remove((subj, pred, obj))