Skip to content

Commit e041d0d

Browse files
committed
feat(lance-graph-ontology): PR-bO-5 SKOS + DUL extension modules
Two additions on the Pattern-D substrate: bO-5 SKOS (OGIT::SKOS_V1, slot 14): - data/ontologies/skos/skos-core.rdf — canonical SKOS Core (32 classes + properties: Concept, ConceptScheme, Collection, OrderedCollection, broader/narrower/related/{broader,narrower}Transitive, in/topConcept, prefLabel/altLabel/hiddenLabel, notation, the *Match mapping family) - data/ontologies/skos/skos-xl.rdf — SKOS-XL eXtension for Labels (lifts labels to first-class IRIs via Label/literalForm/labelRelation) - data/ontologies/skos/skos-owl1dl.rdf — OWL-1-DL-conformant variant shipped for tools that require strict DL conformance (not used by the default hydration) - 27-edge whitelist (subClassOf + subPropertyOf + 23 SKOS semantic-relation / mapping / label predicates) — load-bearing for SKR03/SKR04 alignment - LICENSES/SKOS.txt (W3C document license) - 4 smoke tests verify Core classes, XL Label surface, and the *Match predicates resolve DUL extension modules merged into OGIT::DOLCE_V1: - data/ontologies/dul-extensions/conceptualization.owl — agent conceptualization patterns (knows / believes / assumes / adopts + InternalRepresentation class). Refines the dul:conceptualizes surface used by cognitive-shader agency cascades. - data/ontologies/dul-extensions/lmm-l2.owl — Lexical MetaModel L2. Adds NER surface (NamedEntity / Name / ConceptExpression / ContextualExpression / IndividualReference / MultipleReference / Gloss / hasSyntacticFunction / hasInstance / isInstanceOf). - New `hydrate_dolce_from_many` API that merges DUL.ttl + extensions into a single ContextBundle keyed by OGIT::DOLCE_V1.0 via OwlHydrator::hydrate_many. Extensions are loaded best-effort (skipped if missing) so the path works under either deployment. - 3 smoke tests verify the Conceptualization and LMM_L2 IRIs resolve under G=DOLCE_V1, plus the merged entity count grows past >=240. Format detection improvement: - OwlHydrator::detect_format now accepts bytes and content-sniffs the first 256 bytes for `<?xml` / `<rdf:RDF` when the extension is ambiguous (.owl files exist in both Turtle and RDF/XML form in the wild; the DUL extensions are RDF/XML-with-.owl). Existing routes by extension (.rdf / .ttl / .nt) are unchanged. All 92 lance-graph-ontology tests pass; cargo clippy clean.
1 parent ea9c368 commit e041d0d

16 files changed

Lines changed: 2151 additions & 21 deletions

File tree

LICENSES/DOLCE.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,30 @@ are: Endurant → Object, Perdurant → Event. The four upper categories
4444
in DUL are: Object, Event, Quality, Abstract (all direct sub-classes
4545
of Entity).
4646

47+
DUL extension modules
48+
---------------------
49+
data/ontologies/dul-extensions/ holds sibling design-pattern modules
50+
shipped from the same upstream Ontology Design Patterns catalogue under
51+
the same CC-BY 4.0 license. Both are RDF/XML rather than Turtle, so
52+
the OwlHydrator's `detect_format` content-sniffer routes them through
53+
oxrdfxml automatically.
54+
55+
- conceptualization.owl (~8 KB): patterns for agent-conceptualization
56+
relations — `knows` / `believes` / `assumes` / `adopts` plus the
57+
`InternalRepresentation` class. Refines the dul:conceptualizes
58+
property surface used by cognitive-shader agency cascades.
59+
60+
- lmm-l2.owl (~32 KB): Lexical MetaModel Level 2. Adds named-entity
61+
recognition surface (`NamedEntity` / `Name` / `ConceptExpression` /
62+
`ConceptReference` / `ContextualExpression` / `IndividualReference`
63+
/ `MultipleReference` / `Gloss`). Imports LMM_L1 and IOLite by URL;
64+
those imports are not followed by the hydrator (the IRIs are still
65+
interned as referenced names, but the imported triples are not).
66+
67+
Both extensions hydrate into the same OGIT::DOLCE_V1 bundle as DUL.ttl
68+
via OwlHydrator::hydrate_many, so downstream consumers see one
69+
transitive L1 surface rather than three separate G slots.
70+
4771
Attribution
4872
-----------
4973
DOLCE+DnS Ultralite is provided by the Laboratory for Applied Ontology

LICENSES/SKOS.txt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
SKOS (Simple Knowledge Organization System)
2+
===========================================
3+
4+
Source
5+
------
6+
http://www.w3.org/2004/02/skos/core
7+
http://www.w3.org/2008/05/skos-xl
8+
W3C Recommendation: https://www.w3.org/TR/skos-reference/
9+
10+
Authors
11+
-------
12+
Alistair Miles, Sean Bechhofer (eds.); W3C Semantic Web Deployment
13+
Working Group. Contributors: Dave Beckett, Nikki Rogers, and others.
14+
15+
License
16+
-------
17+
This document is licensed under the W3C Document License.
18+
https://www.w3.org/Consortium/Legal/2015/doc-license
19+
20+
The associated OWL ontology is published under the W3C Software and
21+
Document License (2015) and is in the public domain. See:
22+
https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
23+
24+
Usage in this repository
25+
------------------------
26+
data/ontologies/skos/skos-core.rdf is the canonical SKOS Core vocabulary
27+
(~32 named classes and properties).
28+
29+
data/ontologies/skos/skos-xl.rdf is the SKOS eXtension for Labels (SKOS-XL),
30+
which lifts labels from literals to first-class IRIs (`skosxl:Label`
31+
with `skosxl:literalForm`, plus `skosxl:prefLabel` / `altLabel` /
32+
`hiddenLabel` / `labelRelation`).
33+
34+
data/ontologies/skos/skos-owl1dl.rdf is an OWL-1-DL-conformant subset
35+
of SKOS Core (axioms that violate OWL-1-DL are removed for tools that
36+
require strict DL conformance). The default hydration uses Core + XL
37+
(not the OWL-1-DL variant) since the cognitive-shader cascade is
38+
schema-agnostic at the parser layer.
39+
40+
Both Core and XL hydrate together as the L2 universal knowledge-
41+
organization slot OGIT::SKOS_V1, merged into a single ContextBundle
42+
via OwlHydrator::hydrate_many.
43+
44+
Declares inherits_from: Some(OGIT::DOLCE_V1.0). Alignment:
45+
skos:Concept ⊑ dul:Concept, skos:Collection ⊑ dul:Collection.
46+
47+
Citation
48+
--------
49+
Miles, A. and Bechhofer, S., eds. (2009). "SKOS Simple Knowledge
50+
Organization System Reference." W3C Recommendation 18 August 2009.

crates/lance-graph-contract/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const CANONICAL_SLOTS: &[(&str, u32)] = &[
3232
("PROVO", 11),
3333
("QUDT", 12),
3434
("SCHEMAORG", 13),
35+
("SKOS", 14),
3536
// L3 finance / business ontologies (PR-bO-6 / bO-7).
3637
// FIBO Foundations and FIBO Business Entities.
3738
("FIBOFND", 20),

crates/lance-graph-ontology/src/hydrators/dolce.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,21 @@ use std::path::{Path, PathBuf};
2727

2828
use lance_graph_contract::manifest::OGIT;
2929

30-
use super::owl::{HydrateErr, MetaStructureHydrator, OwlHydrator};
30+
use super::owl::{HydrateErr, OwlHydrator};
3131
use crate::registry::OntologyRegistry;
3232

3333
/// Where the DUL TTL artifact lives, relative to the workspace root.
3434
const DUL_TTL_RELATIVE_PATH: &str = "data/ontologies/dul.ttl";
3535

36+
/// DUL extension modules: small sibling ontologies that refine the DUL
37+
/// upper-category surface. Each shares the `dul:` namespace and is
38+
/// hydrated into the same `OGIT::DOLCE_V1` bundle so downstream consumers
39+
/// see one transitive L1 surface.
40+
const DUL_EXTENSION_RELATIVE_PATHS: &[&str] = &[
41+
"data/ontologies/dul-extensions/conceptualization.owl",
42+
"data/ontologies/dul-extensions/lmm-l2.owl",
43+
];
44+
3645
/// Edge-IRI whitelist for the DOLCE+DUL cascade.
3746
///
3847
/// These are the predicates the cognitive shader's cascade will follow at the
@@ -70,22 +79,42 @@ const DOLCE_EDGE_WHITELIST: &[&str] = &[
7079
///
7180
/// Registers a [`super::owl::ContextBundle`] at `OGIT::DOLCE_V1.0` with
7281
/// `inherits_from: None` (the L1 root, the only hydrator that declares no
73-
/// parent). Reads `data/ontologies/dul.ttl` resolved against the workspace
74-
/// root (the `CARGO_MANIFEST_DIR` of `lance-graph-ontology` is
75-
/// `crates/lance-graph-ontology`, so we walk up two levels).
82+
/// parent). Hydrates the canonical `data/ontologies/dul.ttl` plus the
83+
/// available DUL extension modules under `data/ontologies/dul-extensions/`
84+
/// (Conceptualization patterns, LMM_L2 lexical metamodel) into one bundle.
7685
///
7786
/// After hydration the cascade whitelist is registered via
7887
/// [`OntologyRegistry::register_edge_types`].
7988
pub fn hydrate_dolce(registry: &OntologyRegistry) -> Result<u32, HydrateErr> {
80-
let ttl_path = dul_ttl_path();
81-
hydrate_dolce_from(&ttl_path, registry)
89+
let dul = dul_ttl_path();
90+
let extensions: Vec<PathBuf> =
91+
DUL_EXTENSION_RELATIVE_PATHS.iter().map(extension_path).collect();
92+
let mut paths: Vec<&Path> = Vec::with_capacity(1 + extensions.len());
93+
paths.push(&dul);
94+
for ext in &extensions {
95+
if ext.exists() {
96+
paths.push(ext.as_path());
97+
}
98+
}
99+
hydrate_dolce_from_many(&paths, registry)
82100
}
83101

84-
/// Test-friendly variant: hydrate DOLCE from an explicit path. Used by tests
85-
/// that ship their own fixture or want to exercise the failure path.
102+
/// Test-friendly variant: hydrate DOLCE from an explicit single path
103+
/// (typically a test fixture). Use [`hydrate_dolce_from_many`] when you want
104+
/// to mix the canonical DUL.ttl with extension modules.
86105
pub fn hydrate_dolce_from(
87106
ttl_path: &Path,
88107
registry: &OntologyRegistry,
108+
) -> Result<u32, HydrateErr> {
109+
hydrate_dolce_from_many(&[ttl_path], registry)
110+
}
111+
112+
/// Multi-file variant: hydrate DOLCE+DUL+extensions from explicit paths.
113+
/// Used by [`hydrate_dolce`] to merge the canonical DUL with sibling
114+
/// extension modules, and by tests that need to compose a custom bundle.
115+
pub fn hydrate_dolce_from_many(
116+
paths: &[&Path],
117+
registry: &OntologyRegistry,
89118
) -> Result<u32, HydrateErr> {
90119
let hydrator = OwlHydrator {
91120
g: OGIT::DOLCE_V1.0,
@@ -94,7 +123,7 @@ pub fn hydrate_dolce_from(
94123
inherits_from: None,
95124
starting_entity_id: 100,
96125
};
97-
hydrator.hydrate(ttl_path, registry)?;
126+
hydrator.hydrate_many(paths, registry)?;
98127
registry
99128
.register_edge_types(OGIT::DOLCE_V1.0, DOLCE_EDGE_WHITELIST)
100129
.map_err(|reason| HydrateErr::Registry {
@@ -113,6 +142,13 @@ fn dul_ttl_path() -> PathBuf {
113142
.join(DUL_TTL_RELATIVE_PATH)
114143
}
115144

145+
fn extension_path(rel: &&str) -> PathBuf {
146+
Path::new(env!("CARGO_MANIFEST_DIR"))
147+
.join("..")
148+
.join("..")
149+
.join(rel)
150+
}
151+
116152
#[cfg(test)]
117153
mod tests {
118154
use super::*;

crates/lance-graph-ontology/src/hydrators/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ pub mod owltime;
2525
pub mod provo;
2626
pub mod qudt;
2727
pub mod schemaorg;
28+
pub mod skos;
2829

29-
pub use dolce::{hydrate_dolce, hydrate_dolce_from};
30+
pub use dolce::{hydrate_dolce, hydrate_dolce_from, hydrate_dolce_from_many};
3031
pub use fibo::{hydrate_fibo_be, hydrate_fibo_be_from, hydrate_fibo_fnd, hydrate_fibo_fnd_from};
3132
pub use owl::{ContextBundle, EntityId, HydrateErr, MetaStructureHydrator, OntologySlot, OwlHydrator};
3233
pub use owltime::{hydrate_owltime, hydrate_owltime_from};
3334
pub use provo::{hydrate_provo, hydrate_provo_from};
3435
pub use qudt::{hydrate_qudt, hydrate_qudt_from};
3536
pub use schemaorg::{hydrate_schemaorg, hydrate_schemaorg_from};
37+
pub use skos::{hydrate_skos, hydrate_skos_from};

crates/lance-graph-ontology/src/hydrators/owl.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,35 @@ enum Format {
142142
RdfXml,
143143
}
144144

145-
/// Detect serialization by extension. `.rdf` → RDF/XML, everything else
146-
/// (`.ttl`, `.owl`, `.nt`, …) → Turtle. FIBO is the canonical RDF/XML user;
147-
/// DOLCE+DUL / OWL-Time / PROV-O / QUDT / schema.org are all Turtle.
148-
fn detect_format(path: &Path) -> Format {
145+
/// Detect serialization by extension first, then content sniff as a fallback.
146+
/// `.rdf` → RDF/XML. `.ttl` / `.nt` / `.n3` → Turtle. For ambiguous `.owl`
147+
/// files (which exist in both Turtle and RDF/XML form in the wild) and
148+
/// unknown extensions, sniff the first ~200 bytes for an XML preamble or
149+
/// `<rdf:RDF>` opening tag.
150+
fn detect_format(path: &Path, bytes: &[u8]) -> Format {
149151
let ext = path
150152
.extension()
151153
.and_then(|s| s.to_str())
152154
.unwrap_or("")
153155
.to_ascii_lowercase();
154156
match ext.as_str() {
155-
"rdf" | "rdfxml" => Format::RdfXml,
156-
_ => Format::Turtle,
157+
"rdf" | "rdfxml" | "owx" => Format::RdfXml,
158+
"ttl" | "nt" | "n3" | "trig" => Format::Turtle,
159+
_ => {
160+
// `.owl` and unknown extensions: sniff the head.
161+
let head = &bytes[..bytes.len().min(256)];
162+
// Strip leading whitespace / BOM.
163+
let trimmed = head
164+
.iter()
165+
.position(|b| !b.is_ascii_whitespace() && *b != 0xEF && *b != 0xBB && *b != 0xBF)
166+
.map(|i| &head[i..])
167+
.unwrap_or(head);
168+
if trimmed.starts_with(b"<?xml") || trimmed.starts_with(b"<rdf:RDF") {
169+
Format::RdfXml
170+
} else {
171+
Format::Turtle
172+
}
173+
}
157174
}
158175
}
159176

@@ -198,7 +215,7 @@ impl OwlHydrator {
198215
// to oxrdfxml; everything else (.ttl, .nt, .n3) is treated as
199216
// Turtle and routed to oxttl. This keeps the hydrator API
200217
// single-method and the bO-* series free of per-format glue.
201-
match detect_format(ttl_path) {
218+
match detect_format(ttl_path, &bytes) {
202219
Format::RdfXml => {
203220
let parser = RdfXmlParser::new().for_slice(&bytes);
204221
for triple in parser {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//! SKOS (Simple Knowledge Organization System, `http://www.w3.org/2004/02/skos/core#`)
2+
//! hydration glue.
3+
//!
4+
//! L2 universal ontology for thesauri, classification schemes, subject-heading
5+
//! lists, taxonomies, and other controlled vocabularies. Defines `Concept`,
6+
//! `ConceptScheme`, `Collection`, `OrderedCollection`, plus the canonical
7+
//! semantic-relation surface (`broader`/`narrower`/`related` plus their
8+
//! transitive variants and the `*Match` mapping siblings).
9+
//!
10+
//! Multi-file hydration: SKOS Core + SKOS-XL (eXtension for Labels — adds
11+
//! `skosxl:Label` so labels can be first-class resources with their own
12+
//! IRIs rather than literals) hydrate into one bundle keyed by
13+
//! `OGIT::SKOS_V1.0`. SKOS-XL declares `owl:imports` against Core so the
14+
//! merged bundle gives consumers both surfaces in a single G slot.
15+
//!
16+
//! Declares `inherits_from: Some(OGIT::DOLCE_V1.0)`. Alignment with DUL:
17+
//! `skos:Concept ⊑ dul:Concept`, `skos:Collection ⊑ dul:Collection`.
18+
//! Downstream consumers assert these axioms when they need cross-G
19+
//! traversal between SKR-style classification trees and DOLCE
20+
//! upper-category cascade.
21+
22+
use std::path::{Path, PathBuf};
23+
24+
use lance_graph_contract::manifest::OGIT;
25+
26+
use super::owl::{HydrateErr, OwlHydrator};
27+
use crate::registry::OntologyRegistry;
28+
29+
const SKOS_CORE_RELATIVE_PATH: &str = "data/ontologies/skos/skos-core.rdf";
30+
const SKOS_XL_RELATIVE_PATH: &str = "data/ontologies/skos/skos-xl.rdf";
31+
32+
const SKOS_EDGE_WHITELIST: &[&str] = &[
33+
"http://www.w3.org/2000/01/rdf-schema#subClassOf",
34+
"http://www.w3.org/2000/01/rdf-schema#subPropertyOf",
35+
"http://www.w3.org/2002/07/owl#equivalentClass",
36+
// Hierarchical (intra-scheme) relations
37+
"http://www.w3.org/2004/02/skos/core#broader",
38+
"http://www.w3.org/2004/02/skos/core#narrower",
39+
"http://www.w3.org/2004/02/skos/core#broaderTransitive",
40+
"http://www.w3.org/2004/02/skos/core#narrowerTransitive",
41+
"http://www.w3.org/2004/02/skos/core#related",
42+
"http://www.w3.org/2004/02/skos/core#semanticRelation",
43+
// Scheme membership
44+
"http://www.w3.org/2004/02/skos/core#inScheme",
45+
"http://www.w3.org/2004/02/skos/core#hasTopConcept",
46+
"http://www.w3.org/2004/02/skos/core#topConceptOf",
47+
"http://www.w3.org/2004/02/skos/core#member",
48+
"http://www.w3.org/2004/02/skos/core#memberList",
49+
// Cross-scheme mapping (load-bearing for SKR03/SKR04 alignment)
50+
"http://www.w3.org/2004/02/skos/core#mappingRelation",
51+
"http://www.w3.org/2004/02/skos/core#broadMatch",
52+
"http://www.w3.org/2004/02/skos/core#narrowMatch",
53+
"http://www.w3.org/2004/02/skos/core#relatedMatch",
54+
"http://www.w3.org/2004/02/skos/core#exactMatch",
55+
"http://www.w3.org/2004/02/skos/core#closeMatch",
56+
// Lexical labels (Core + XL)
57+
"http://www.w3.org/2004/02/skos/core#prefLabel",
58+
"http://www.w3.org/2004/02/skos/core#altLabel",
59+
"http://www.w3.org/2004/02/skos/core#hiddenLabel",
60+
"http://www.w3.org/2004/02/skos/core#notation",
61+
"http://www.w3.org/2008/05/skos-xl#literalForm",
62+
"http://www.w3.org/2008/05/skos-xl#prefLabel",
63+
"http://www.w3.org/2008/05/skos-xl#altLabel",
64+
"http://www.w3.org/2008/05/skos-xl#hiddenLabel",
65+
"http://www.w3.org/2008/05/skos-xl#labelRelation",
66+
];
67+
68+
/// Hydrate SKOS as `OGIT::SKOS_V1` (L2 universal knowledge-organization).
69+
///
70+
/// Hydrates both Core and XL artifacts into a single bundle.
71+
pub fn hydrate_skos(registry: &OntologyRegistry) -> Result<u32, HydrateErr> {
72+
let core = skos_core_path();
73+
let xl = skos_xl_path();
74+
hydrate_skos_from(&[&core, &xl], registry)
75+
}
76+
77+
/// Test-friendly variant: hydrate SKOS from explicit paths. Accepts a slice
78+
/// so callers can opt into Core-only, or substitute the `skos-owl1dl.rdf`
79+
/// OWL-DL-conformant variant (also shipped under `data/ontologies/skos/`).
80+
pub fn hydrate_skos_from(
81+
ttl_paths: &[&Path],
82+
registry: &OntologyRegistry,
83+
) -> Result<u32, HydrateErr> {
84+
let hydrator = OwlHydrator {
85+
g: OGIT::SKOS_V1.0,
86+
version: OGIT::SKOS_V1.1,
87+
domain_name: "skos".to_string(),
88+
inherits_from: Some(OGIT::DOLCE_V1.0),
89+
starting_entity_id: 100,
90+
};
91+
hydrator.hydrate_many(ttl_paths, registry)?;
92+
registry
93+
.register_edge_types(OGIT::SKOS_V1.0, SKOS_EDGE_WHITELIST)
94+
.map_err(|reason| HydrateErr::Registry {
95+
g: OGIT::SKOS_V1.0,
96+
reason,
97+
})?;
98+
Ok(OGIT::SKOS_V1.0)
99+
}
100+
101+
fn skos_core_path() -> PathBuf {
102+
Path::new(env!("CARGO_MANIFEST_DIR"))
103+
.join("..")
104+
.join("..")
105+
.join(SKOS_CORE_RELATIVE_PATH)
106+
}
107+
108+
fn skos_xl_path() -> PathBuf {
109+
Path::new(env!("CARGO_MANIFEST_DIR"))
110+
.join("..")
111+
.join("..")
112+
.join(SKOS_XL_RELATIVE_PATH)
113+
}

crates/lance-graph-ontology/src/lib.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@ pub mod lance_cache;
5454
pub use bridge::{BridgeError, NamespaceBridge};
5555
pub use error::Error;
5656
pub use hydrators::{
57-
hydrate_dolce, hydrate_dolce_from, hydrate_fibo_be, hydrate_fibo_be_from, hydrate_fibo_fnd,
58-
hydrate_fibo_fnd_from, hydrate_owltime, hydrate_owltime_from, hydrate_provo,
59-
hydrate_provo_from, hydrate_qudt, hydrate_qudt_from, hydrate_schemaorg, hydrate_schemaorg_from,
60-
ContextBundle, EntityId, HydrateErr, MetaStructureHydrator, OntologySlot, OwlHydrator,
57+
hydrate_dolce, hydrate_dolce_from, hydrate_dolce_from_many, hydrate_fibo_be,
58+
hydrate_fibo_be_from, hydrate_fibo_fnd, hydrate_fibo_fnd_from, hydrate_owltime,
59+
hydrate_owltime_from, hydrate_provo, hydrate_provo_from, hydrate_qudt, hydrate_qudt_from,
60+
hydrate_schemaorg, hydrate_schemaorg_from, hydrate_skos, hydrate_skos_from, ContextBundle,
61+
EntityId, HydrateErr, MetaStructureHydrator, OntologySlot, OwlHydrator,
6162
};
6263
pub use namespace::{NamespaceId, OgitUri, SchemaPtr};
6364
pub use proposal::{

0 commit comments

Comments
 (0)