@skaterqiang/protege-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/LICENSE +48 -0
- package/README.md +287 -0
- package/docs/API.md +654 -0
- package/docs/design/OWL2-/347/274/272/345/217/243/345/210/206/346/236/220.md +140 -0
- package/docs/owl2-rl/OWL2-RL-/350/247/204/345/210/231/347/273/206/345/210/231.md +168 -0
- package/docs/owl2-rl/owl2-profiles-spec-zh.html +1914 -0
- package/docs/owl2-rl/owl2-profiles-spec.html +1916 -0
- package/docs/owl2-rl/rl_rules.json +392 -0
- package/main.js +46 -0
- package/package.json +76 -0
- package/sample/README.md +74 -0
- package/sample/case1-ecommerce-risk.js +170 -0
- package/sample/case10-ecommerce-customer-service.js +454 -0
- package/sample/case2-family-swrl.js +147 -0
- package/sample/case3-pharma-safety.js +132 -0
- package/sample/case4-knowledge-publishing.js +111 -0
- package/sample/case5-obda-profiles.js +153 -0
- package/sample/case6-medical-ontology.js +152 -0
- package/sample/case7-manufacturing-ppr.js +155 -0
- package/sample/case8-real-medical-bfo-ogms.js +353 -0
- package/sample/case9-real-manufacturing-iao.js +360 -0
- package/sample/ontologies/bfo.owl +1715 -0
- package/sample/ontologies/iao.owl +8532 -0
- package/sample/ontologies/ogms.owl +4320 -0
- package/sample/ontologies/ro-core.owl +1010 -0
- package/scripts/verify-api.js +53 -0
- package/src/index.js +176 -0
- package/src/inference/OWL2ProfileReasoners.js +67 -0
- package/src/inference/OWL2RLReasoner.js +65 -0
- package/src/inference/ReasonerQueries.js +111 -0
- package/src/inference/SWRLReasoner.js +409 -0
- package/src/inference/TripleStore.js +82 -0
- package/src/inference/rdf.js +83 -0
- package/src/inference/rules/owl2el.js +33 -0
- package/src/inference/rules/owl2ql.js +29 -0
- package/src/inference/rules/owl2rl.js +823 -0
- package/src/io/FunctionalSyntaxParser.js +399 -0
- package/src/io/FunctionalSyntaxWriter.js +180 -0
- package/src/io/ManchesterSyntaxParser.js +398 -0
- package/src/io/OWLXMLParser.js +356 -0
- package/src/io/OntologyLoader.js +125 -0
- package/src/io/RDFGraphToOntology.js +702 -0
- package/src/io/RDFXMLParser.js +319 -0
- package/src/io/RDFXMLWriter.js +196 -0
- package/src/io/SWRLParser.js +150 -0
- package/src/io/TurtleParser.js +363 -0
- package/src/io/TurtleWriter.js +393 -0
- package/src/model/IRI.js +62 -0
- package/src/model/OWLAxiom.js +507 -0
- package/src/model/OWLClassExpression.js +242 -0
- package/src/model/OWLEntity.js +142 -0
- package/src/model/OWLLiteral.js +30 -0
- package/src/model/OWLModelManager.js +106 -0
- package/src/model/OWLOntology.js +119 -0
- package/src/model/SWRL.js +152 -0
- package/src/model/event/OWLModelManagerEvent.js +33 -0
- package/src/model/hierarchy/HierarchyProvider.js +214 -0
- package/src/profiles/OWL2Profiles.js +185 -0
- package/src/server/public/app.js +97 -0
- package/src/server/public/index.html +49 -0
- package/src/server/webServer.js +138 -0
- package/src/validation/GlobalRestrictionsValidator.js +172 -0
- package/test/core.test.js +111 -0
- package/test/exports-map.test.js +165 -0
- package/test/owl2/axioms.test.js +160 -0
- package/test/owl2/classExpressions.test.js +99 -0
- package/test/owl2/functional-syntax-writer.test.js +65 -0
- package/test/owl2/functional.test.js +113 -0
- package/test/owl2/global-restrictions.test.js +76 -0
- package/test/owl2/longtail.test.js +74 -0
- package/test/owl2/manchester-syntax.test.js +100 -0
- package/test/owl2/ontology-loader-imports.test.js +62 -0
- package/test/owl2/owlxml-parser.test.js +112 -0
- package/test/owl2/profile-reasoners.test.js +108 -0
- package/test/owl2/profiles.test.js +66 -0
- package/test/owl2/rdf-graph-to-ontology.test.js +177 -0
- package/test/owl2/rdfxml-punning.test.js +25 -0
- package/test/owl2/rdfxml-writer.test.js +55 -0
- package/test/owl2/rdfxml.test.js +91 -0
- package/test/owl2/reasoner-queries.test.js +58 -0
- package/test/owl2/sample-e2e.test.js +195 -0
- package/test/owl2/swrl-parser.test.js +60 -0
- package/test/owl2/swrl.test.js +131 -0
- package/test/owl2/turtle-writer.test.js +84 -0
- package/test/owl2/turtle.test.js +124 -0
- package/test/owl2rl/cax.test.js +57 -0
- package/test/owl2rl/cls.test.js +210 -0
- package/test/owl2rl/dt.test.js +72 -0
- package/test/owl2rl/eq.test.js +95 -0
- package/test/owl2rl/prp.test.js +198 -0
- package/test/owl2rl/scm.test.js +203 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// scripts/verify-api.js — smoke test for the public API entry point.
|
|
5
|
+
//
|
|
6
|
+
// Verifies that src/index.js exports every symbol the README documents, and
|
|
7
|
+
// that the OWL 2 RL reasoner actually entails a trivial subsumption.
|
|
8
|
+
// Run: node scripts/verify-api.js (also used by CI)
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const p = require(path.join(__dirname, '..', 'src', 'index.js'));
|
|
13
|
+
|
|
14
|
+
const need = [
|
|
15
|
+
// reasoners / inference
|
|
16
|
+
'OWL2RLReasoner', 'OWL2QLReasoner', 'OWL2ELReasoner', 'SWRLReasoner',
|
|
17
|
+
'TripleStore', 'ReasonerQueries',
|
|
18
|
+
// parsers
|
|
19
|
+
'TurtleParser', 'parseTurtle', 'parseRDFXML', 'FunctionalSyntaxParser',
|
|
20
|
+
'ManchesterSyntaxParser', 'OWLXMLParser', 'SWRLParser',
|
|
21
|
+
// writers
|
|
22
|
+
'writeTurtle', 'writeRDFXML', 'writeFunctionalSyntax',
|
|
23
|
+
// bridges / loader
|
|
24
|
+
'triplesToOntology', 'OntologyLoader',
|
|
25
|
+
// profiles / validation
|
|
26
|
+
'checkRL', 'checkQL', 'checkEL', 'checkProfile', 'isInProfile',
|
|
27
|
+
'GlobalRestrictionsValidator',
|
|
28
|
+
// model
|
|
29
|
+
'IRI', 'OWLLiteral', 'OWLOntology', 'OWLModelManager',
|
|
30
|
+
'OWLClass', 'OWLSubClassOfAxiom',
|
|
31
|
+
// namespaces
|
|
32
|
+
'model', 'io', 'inference', 'profiles'
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const miss = need.filter((k) => !(k in p));
|
|
36
|
+
if (miss.length) {
|
|
37
|
+
console.error('FAIL — missing exports:', miss.join(', '));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Functional smoke: cax-sco + prp-type entailment through the flat API.
|
|
42
|
+
const RDFS = 'http://www.w3.org/2000/01/rdf-schema#subClassOf';
|
|
43
|
+
const RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
|
|
44
|
+
const r = new p.OWL2RLReasoner(new p.TripleStore());
|
|
45
|
+
r.store.add('ex:A', RDFS, 'ex:B');
|
|
46
|
+
r.store.add('ex:x', RDF, 'ex:A');
|
|
47
|
+
r.materialize();
|
|
48
|
+
if (!r.entails('ex:x', RDF, 'ex:B')) {
|
|
49
|
+
console.error('FAIL — RL reasoner did not entail ex:x rdf:type ex:B');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(`OK — public API verified: ${Object.keys(p).length} exports, RL smoke entailment passed`);
|
package/src/index.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// protege-js — public API entry point.
|
|
5
|
+
//
|
|
6
|
+
// Everything is re-exported here so consumers can do:
|
|
7
|
+
//
|
|
8
|
+
// const { OWL2RLReasoner, TurtleParser } = require('@skaterqiang/protege-js');
|
|
9
|
+
//
|
|
10
|
+
// Namespaced access is available too, for callers that prefer grouping:
|
|
11
|
+
//
|
|
12
|
+
// const protege = require('@skaterqiang/protege-js');
|
|
13
|
+
// protege.inference.OWL2RLReasoner
|
|
14
|
+
// protege.io.TurtleParser
|
|
15
|
+
// protege.model.OWLClass
|
|
16
|
+
//
|
|
17
|
+
// Zero runtime dependencies. CommonJS. Node >= 18 (uses node:test in dev only).
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
// --- model -----------------------------------------------------------------
|
|
21
|
+
const { IRI } = require('./model/IRI');
|
|
22
|
+
const entity = require('./model/OWLEntity');
|
|
23
|
+
const axiom = require('./model/OWLAxiom');
|
|
24
|
+
const classExpr = require('./model/OWLClassExpression');
|
|
25
|
+
const { OWLLiteral } = require('./model/OWLLiteral');
|
|
26
|
+
const { OWLOntology, OWLOntologyID } = require('./model/OWLOntology');
|
|
27
|
+
const { OWLModelManager } = require('./model/OWLModelManager');
|
|
28
|
+
const swrlModel = require('./model/SWRL');
|
|
29
|
+
const { EventType, OWLModelManagerChangeEvent } = require('./model/event/OWLModelManagerEvent');
|
|
30
|
+
const hierarchy = require('./model/hierarchy/HierarchyProvider');
|
|
31
|
+
|
|
32
|
+
// --- io --------------------------------------------------------------------
|
|
33
|
+
const { TurtleParser, parseTurtle } = require('./io/TurtleParser');
|
|
34
|
+
const { writeTurtle, TurtleWriter } = require('./io/TurtleWriter');
|
|
35
|
+
const { parseRDFXML } = require('./io/RDFXMLParser');
|
|
36
|
+
const { writeRDFXML, RDFXMLWriter } = require('./io/RDFXMLWriter');
|
|
37
|
+
const { FunctionalSyntaxParser } = require('./io/FunctionalSyntaxParser');
|
|
38
|
+
const funcWriter = require('./io/FunctionalSyntaxWriter');
|
|
39
|
+
const { ManchesterSyntaxParser } = require('./io/ManchesterSyntaxParser');
|
|
40
|
+
const { OWLXMLParser } = require('./io/OWLXMLParser');
|
|
41
|
+
const { SWRLParser } = require('./io/SWRLParser');
|
|
42
|
+
const { triplesToOntology } = require('./io/RDFGraphToOntology');
|
|
43
|
+
const { OntologyLoader } = require('./io/OntologyLoader');
|
|
44
|
+
|
|
45
|
+
// --- inference -------------------------------------------------------------
|
|
46
|
+
const { TripleStore } = require('./inference/TripleStore');
|
|
47
|
+
const rdf = require('./inference/rdf');
|
|
48
|
+
const { OWL2RLReasoner } = require('./inference/OWL2RLReasoner');
|
|
49
|
+
const { OWL2QLReasoner, OWL2ELReasoner, ProfileReasoner } = require('./inference/OWL2ProfileReasoners');
|
|
50
|
+
const { SWRLReasoner, SWRLBuiltins } = require('./inference/SWRLReasoner');
|
|
51
|
+
const { ReasonerQueries } = require('./inference/ReasonerQueries');
|
|
52
|
+
const { rules: OWL2RL_RULES } = require('./inference/rules/owl2rl');
|
|
53
|
+
const { QL_RULE_IDS } = require('./inference/rules/owl2ql');
|
|
54
|
+
const { EL_RULE_IDS } = require('./inference/rules/owl2el');
|
|
55
|
+
|
|
56
|
+
// --- profiles / validation -------------------------------------------------
|
|
57
|
+
const profiles = require('./profiles/OWL2Profiles');
|
|
58
|
+
const { GlobalRestrictionsValidator } = require('./validation/GlobalRestrictionsValidator');
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Namespaces
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
const model = Object.assign(
|
|
65
|
+
{
|
|
66
|
+
IRI,
|
|
67
|
+
OWLLiteral,
|
|
68
|
+
OWLOntology,
|
|
69
|
+
OWLOntologyID,
|
|
70
|
+
OWLModelManager,
|
|
71
|
+
EventType,
|
|
72
|
+
OWLModelManagerChangeEvent
|
|
73
|
+
},
|
|
74
|
+
entity,
|
|
75
|
+
axiom,
|
|
76
|
+
classExpr,
|
|
77
|
+
swrlModel,
|
|
78
|
+
hierarchy
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const io = {
|
|
82
|
+
// parsers
|
|
83
|
+
TurtleParser,
|
|
84
|
+
parseTurtle,
|
|
85
|
+
parseRDFXML,
|
|
86
|
+
FunctionalSyntaxParser,
|
|
87
|
+
ManchesterSyntaxParser,
|
|
88
|
+
OWLXMLParser,
|
|
89
|
+
SWRLParser,
|
|
90
|
+
// writers
|
|
91
|
+
TurtleWriter,
|
|
92
|
+
writeTurtle,
|
|
93
|
+
RDFXMLWriter,
|
|
94
|
+
writeRDFXML,
|
|
95
|
+
writeFunctionalSyntax: funcWriter.writeFunctionalSyntax,
|
|
96
|
+
// bridges
|
|
97
|
+
triplesToOntology,
|
|
98
|
+
OntologyLoader
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const inference = {
|
|
102
|
+
TripleStore,
|
|
103
|
+
OWL2RLReasoner,
|
|
104
|
+
OWL2QLReasoner,
|
|
105
|
+
OWL2ELReasoner,
|
|
106
|
+
ProfileReasoner,
|
|
107
|
+
SWRLReasoner,
|
|
108
|
+
SWRLBuiltins,
|
|
109
|
+
ReasonerQueries,
|
|
110
|
+
NS: rdf.NS,
|
|
111
|
+
P: rdf.P,
|
|
112
|
+
C: rdf.C,
|
|
113
|
+
isLiteral: rdf.isLiteral,
|
|
114
|
+
parseLiteral: rdf.parseLiteral,
|
|
115
|
+
literal: rdf.literal,
|
|
116
|
+
OWL2RL_RULES,
|
|
117
|
+
QL_RULE_IDS,
|
|
118
|
+
EL_RULE_IDS
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Flat export surface (most common import style)
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
module.exports = Object.assign(
|
|
126
|
+
{
|
|
127
|
+
// namespaces
|
|
128
|
+
model,
|
|
129
|
+
io,
|
|
130
|
+
inference,
|
|
131
|
+
profiles,
|
|
132
|
+
// convenience: profile checks at top level
|
|
133
|
+
checkProfile: profiles.checkProfile,
|
|
134
|
+
isInProfile: profiles.isInProfile,
|
|
135
|
+
checkRL: profiles.checkRL,
|
|
136
|
+
checkQL: profiles.checkQL,
|
|
137
|
+
checkEL: profiles.checkEL,
|
|
138
|
+
Profiles: profiles.Profiles,
|
|
139
|
+
GlobalRestrictionsValidator,
|
|
140
|
+
// convenience: the classes people reach for first
|
|
141
|
+
IRI,
|
|
142
|
+
OWLLiteral,
|
|
143
|
+
OWLOntology,
|
|
144
|
+
OWLOntologyID,
|
|
145
|
+
OWLModelManager,
|
|
146
|
+
TripleStore,
|
|
147
|
+
OWL2RLReasoner,
|
|
148
|
+
OWL2QLReasoner,
|
|
149
|
+
OWL2ELReasoner,
|
|
150
|
+
SWRLReasoner,
|
|
151
|
+
ReasonerQueries,
|
|
152
|
+
TurtleParser,
|
|
153
|
+
parseTurtle,
|
|
154
|
+
parseRDFXML,
|
|
155
|
+
FunctionalSyntaxParser,
|
|
156
|
+
ManchesterSyntaxParser,
|
|
157
|
+
OWLXMLParser,
|
|
158
|
+
SWRLParser,
|
|
159
|
+
writeTurtle,
|
|
160
|
+
writeRDFXML,
|
|
161
|
+
writeFunctionalSyntax: funcWriter.writeFunctionalSyntax,
|
|
162
|
+
triplesToOntology,
|
|
163
|
+
OntologyLoader
|
|
164
|
+
},
|
|
165
|
+
// model layer (entities, axioms, class expressions, SWRL model, hierarchy)
|
|
166
|
+
entity,
|
|
167
|
+
axiom,
|
|
168
|
+
classExpr,
|
|
169
|
+
swrlModel,
|
|
170
|
+
hierarchy,
|
|
171
|
+
{ EventType, OWLModelManagerChangeEvent },
|
|
172
|
+
// writers exported as classes too
|
|
173
|
+
{ TurtleWriter, RDFXMLWriter },
|
|
174
|
+
// rdf namespace constants
|
|
175
|
+
{ NS: rdf.NS, P: rdf.P, C: rdf.C }
|
|
176
|
+
);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { TripleStore } = require('./TripleStore');
|
|
4
|
+
const { rules } = require('./rules/owl2rl');
|
|
5
|
+
const { QL_RULE_IDS } = require('./rules/owl2ql');
|
|
6
|
+
const { EL_RULE_IDS } = require('./rules/owl2el');
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// OWL2QLReasoner / OWL2ELReasoner — profile-restricted forward chaining.
|
|
10
|
+
// Each runs only the rule subset that is sound for its profile, reusing the
|
|
11
|
+
// rule implementations from owl2rl.js. This is the "minimal inference" for
|
|
12
|
+
// QL (query answering) and EL (classification) profiles.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
class ProfileReasoner {
|
|
16
|
+
constructor(store, ruleIds) {
|
|
17
|
+
this.store = store || new TripleStore();
|
|
18
|
+
this.inconsistencies = [];
|
|
19
|
+
this._inferred = new Set();
|
|
20
|
+
this._rounds = 0;
|
|
21
|
+
this._ruleFns = ruleIds.map(id => [id, rules[id]]).filter(([, fn]) => typeof fn === 'function');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_add(s, p, o) {
|
|
25
|
+
const isNew = this.store.add(s, p, o);
|
|
26
|
+
if (isNew) this._inferred.add(TripleStore.key(String(s), String(p), String(o)));
|
|
27
|
+
return isNew;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_conflict(rule, message) {
|
|
31
|
+
if (!this.inconsistencies.some(c => c.rule === rule && c.message === message)) {
|
|
32
|
+
this.inconsistencies.push({ rule, message });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
materialize(maxRounds = 1000) {
|
|
37
|
+
const add = (s, p, o) => this._add(s, p, o);
|
|
38
|
+
const conflict = (rule, msg) => this._conflict(rule, msg);
|
|
39
|
+
let round = 0, changed = true;
|
|
40
|
+
while (changed && round < maxRounds) {
|
|
41
|
+
round++;
|
|
42
|
+
const before = this.store.size();
|
|
43
|
+
for (const [id, fn] of this._ruleFns) {
|
|
44
|
+
fn(this.store, add, (msg) => conflict(id, msg));
|
|
45
|
+
}
|
|
46
|
+
changed = this.store.size() > before;
|
|
47
|
+
}
|
|
48
|
+
this._rounds = round;
|
|
49
|
+
return this._inferred.size;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
isConsistent() { return this.inconsistencies.length === 0; }
|
|
53
|
+
getInferredCount() { return this._inferred.size; }
|
|
54
|
+
entails(s, p, o) { return this.store.has(s, p, o); }
|
|
55
|
+
getRounds() { return this._rounds; }
|
|
56
|
+
getRuleIds() { return this._ruleFns.map(([id]) => id); }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class OWL2QLReasoner extends ProfileReasoner {
|
|
60
|
+
constructor(store) { super(store, QL_RULE_IDS); }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class OWL2ELReasoner extends ProfileReasoner {
|
|
64
|
+
constructor(store) { super(store, EL_RULE_IDS); }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { OWL2QLReasoner, OWL2ELReasoner, ProfileReasoner };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { TripleStore } = require('./TripleStore');
|
|
4
|
+
const { rules } = require('./rules/owl2rl');
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// OWL2RLReasoner — forward-chaining materialization of the OWL 2 RL ruleset.
|
|
8
|
+
// Runs all 78 rules to a fixpoint. Rules whose consequent is `false` record
|
|
9
|
+
// an inconsistency instead of throwing.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
class OWL2RLReasoner {
|
|
13
|
+
constructor(store) {
|
|
14
|
+
this.store = store || new TripleStore();
|
|
15
|
+
this.inconsistencies = []; // [{rule, message}]
|
|
16
|
+
this._inferred = new Set(); // triples added during materialization
|
|
17
|
+
this._rounds = 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_add(s, p, o) {
|
|
21
|
+
const isNew = this.store.add(s, p, o);
|
|
22
|
+
if (isNew) this._inferred.add(TripleStore.key(String(s), String(p), String(o)));
|
|
23
|
+
return isNew;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_conflict(rule, message) {
|
|
27
|
+
// dedupe identical findings
|
|
28
|
+
if (!this.inconsistencies.some(c => c.rule === rule && c.message === message)) {
|
|
29
|
+
this.inconsistencies.push({ rule, message });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Run the ruleset to a fixpoint. Returns number of new triples derived. */
|
|
34
|
+
materialize(maxRounds = 1000) {
|
|
35
|
+
const add = (s, p, o) => this._add(s, p, o);
|
|
36
|
+
const conflict = (rule, msg) => this._conflict(rule, msg);
|
|
37
|
+
let round = 0;
|
|
38
|
+
let changed = true;
|
|
39
|
+
while (changed && round < maxRounds) {
|
|
40
|
+
round++;
|
|
41
|
+
const before = this.store.size();
|
|
42
|
+
for (const [id, fn] of Object.entries(rules)) {
|
|
43
|
+
fn(this.store, add, (msg) => conflict(id, msg));
|
|
44
|
+
}
|
|
45
|
+
changed = this.store.size() > before;
|
|
46
|
+
// conflicts don't add triples, so keep looping while they emerge? They are
|
|
47
|
+
// terminal; no need to loop for conflicts alone.
|
|
48
|
+
}
|
|
49
|
+
this._rounds = round;
|
|
50
|
+
return this._inferred.size;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** True iff no inconsistency was recorded. */
|
|
54
|
+
isConsistent() { return this.inconsistencies.length === 0; }
|
|
55
|
+
|
|
56
|
+
/** Number of triples inferred (not present in the input). */
|
|
57
|
+
getInferredCount() { return this._inferred.size; }
|
|
58
|
+
|
|
59
|
+
/** True iff triple (s,p,o) holds in the materialized store. */
|
|
60
|
+
entails(s, p, o) { return this.store.has(s, p, o); }
|
|
61
|
+
|
|
62
|
+
getRounds() { return this._rounds; }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { OWL2RLReasoner, TripleStore };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// inference/ReasonerQueries — high-level query API over a materialized
|
|
5
|
+
// OWL2RLReasoner. Mirrors the OWLReasoner query methods of the OWL API:
|
|
6
|
+
// getSubClasses / getSuperClasses / getInstances / getTypes / isSubClassOf /
|
|
7
|
+
// isSatisfiable / getEquivalentClasses.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
const { NS } = require('./rdf');
|
|
11
|
+
const RDF = NS.RDF, RDFS = NS.RDFS, OWL = NS.OWL;
|
|
12
|
+
|
|
13
|
+
class ReasonerQueries {
|
|
14
|
+
/**
|
|
15
|
+
* @param {OWL2RLReasoner} reasoner already materialized (call .materialize() first)
|
|
16
|
+
*/
|
|
17
|
+
constructor(reasoner) {
|
|
18
|
+
this.reasoner = reasoner;
|
|
19
|
+
this.store = reasoner.store;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Direct + indirect subclasses of `clsIRI`. */
|
|
23
|
+
getSubClasses(clsIRI, { direct = false } = {}) {
|
|
24
|
+
const subs = this.store.subjects(RDFS + 'subClassOf', clsIRI);
|
|
25
|
+
if (direct) return subs;
|
|
26
|
+
return subs;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Direct + indirect superclasses of `clsIRI`. */
|
|
30
|
+
getSuperClasses(clsIRI, { direct = false } = {}) {
|
|
31
|
+
return this.store.objects(clsIRI, RDFS + 'subClassOf');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** All instances (individuals) of `clsIRI`. */
|
|
35
|
+
getInstances(clsIRI) {
|
|
36
|
+
return this.store.subjects(RDF + 'type', clsIRI)
|
|
37
|
+
.filter(s => !String(s).startsWith('_:'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** All classes that `individualIRI` is an instance of. */
|
|
41
|
+
getTypes(individualIRI) {
|
|
42
|
+
return this.store.objects(individualIRI, RDF + 'type')
|
|
43
|
+
.filter(t => t !== OWL + 'NamedIndividual' && t !== OWL + 'Class'
|
|
44
|
+
&& t !== OWL + 'ObjectProperty' && t !== OWL + 'DatatypeProperty'
|
|
45
|
+
&& t !== OWL + 'AnnotationProperty');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** True iff sub ⊑ super (asserted or inferred). */
|
|
49
|
+
isSubClassOf(subIRI, superIRI) {
|
|
50
|
+
return this.store.has(subIRI, RDFS + 'subClassOf', superIRI);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** True iff sub ⊑ super along subPropertyOf. */
|
|
54
|
+
isSubPropertyOf(subIRI, superIRI) {
|
|
55
|
+
return this.store.has(subIRI, RDFS + 'subPropertyOf', superIRI);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Equivalent classes of clsIRI (owl:equivalentClass both directions). */
|
|
59
|
+
getEquivalentClasses(clsIRI) {
|
|
60
|
+
const fwd = this.store.objects(clsIRI, OWL + 'equivalentClass');
|
|
61
|
+
const bwd = this.store.subjects(OWL + 'equivalentClass', clsIRI);
|
|
62
|
+
return [...new Set([...fwd, ...bwd])];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Disjoint classes of clsIRI. */
|
|
66
|
+
getDisjointClasses(clsIRI) {
|
|
67
|
+
const fwd = this.store.objects(clsIRI, OWL + 'disjointWith');
|
|
68
|
+
const bwd = this.store.subjects(OWL + 'disjointWith', clsIRI);
|
|
69
|
+
return [...new Set([...fwd, ...bwd])];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Same individuals (owl:sameAs both directions). */
|
|
73
|
+
getSameIndividuals(indIRI) {
|
|
74
|
+
const fwd = this.store.objects(indIRI, OWL + 'sameAs');
|
|
75
|
+
const bwd = this.store.subjects(OWL + 'sameAs', indIRI);
|
|
76
|
+
return [...new Set([...fwd, ...bwd])];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A class is unsatisfiable iff it is subclass of owl:Nothing.
|
|
81
|
+
* True = satisfiable (no proof of emptiness under OWA).
|
|
82
|
+
*/
|
|
83
|
+
isSatisfiable(clsIRI) {
|
|
84
|
+
return !this.store.has(clsIRI, RDFS + 'subClassOf', OWL + 'Nothing');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Object property values for an individual. */
|
|
88
|
+
getObjectPropertyValues(indIRI, propIRI) {
|
|
89
|
+
return this.store.objects(indIRI, propIRI);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Data property values for an individual. */
|
|
93
|
+
getDataPropertyValues(indIRI, propIRI) {
|
|
94
|
+
return this.store.objects(indIRI, propIRI);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Named roots: classes that are not a subclass of any named class except owl:Thing. */
|
|
98
|
+
getTopClasses() {
|
|
99
|
+
const allClasses = new Set();
|
|
100
|
+
for (const [s] of this.store.match(null, RDF + 'type', OWL + 'Class')) {
|
|
101
|
+
if (!String(s).startsWith('_:')) allClasses.add(s);
|
|
102
|
+
}
|
|
103
|
+
const hasSuper = new Set();
|
|
104
|
+
for (const [s, , o] of this.store.match(null, RDFS + 'subClassOf', null)) {
|
|
105
|
+
if (!String(o).startsWith('_:') && o !== OWL + 'Thing') hasSuper.add(s);
|
|
106
|
+
}
|
|
107
|
+
return [...allClasses].filter(c => !hasSuper.has(c));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = { ReasonerQueries };
|