@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.
Files changed (92) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/LICENSE +48 -0
  3. package/README.md +287 -0
  4. package/docs/API.md +654 -0
  5. package/docs/design/OWL2-/347/274/272/345/217/243/345/210/206/346/236/220.md +140 -0
  6. package/docs/owl2-rl/OWL2-RL-/350/247/204/345/210/231/347/273/206/345/210/231.md +168 -0
  7. package/docs/owl2-rl/owl2-profiles-spec-zh.html +1914 -0
  8. package/docs/owl2-rl/owl2-profiles-spec.html +1916 -0
  9. package/docs/owl2-rl/rl_rules.json +392 -0
  10. package/main.js +46 -0
  11. package/package.json +76 -0
  12. package/sample/README.md +74 -0
  13. package/sample/case1-ecommerce-risk.js +170 -0
  14. package/sample/case10-ecommerce-customer-service.js +454 -0
  15. package/sample/case2-family-swrl.js +147 -0
  16. package/sample/case3-pharma-safety.js +132 -0
  17. package/sample/case4-knowledge-publishing.js +111 -0
  18. package/sample/case5-obda-profiles.js +153 -0
  19. package/sample/case6-medical-ontology.js +152 -0
  20. package/sample/case7-manufacturing-ppr.js +155 -0
  21. package/sample/case8-real-medical-bfo-ogms.js +353 -0
  22. package/sample/case9-real-manufacturing-iao.js +360 -0
  23. package/sample/ontologies/bfo.owl +1715 -0
  24. package/sample/ontologies/iao.owl +8532 -0
  25. package/sample/ontologies/ogms.owl +4320 -0
  26. package/sample/ontologies/ro-core.owl +1010 -0
  27. package/scripts/verify-api.js +53 -0
  28. package/src/index.js +176 -0
  29. package/src/inference/OWL2ProfileReasoners.js +67 -0
  30. package/src/inference/OWL2RLReasoner.js +65 -0
  31. package/src/inference/ReasonerQueries.js +111 -0
  32. package/src/inference/SWRLReasoner.js +409 -0
  33. package/src/inference/TripleStore.js +82 -0
  34. package/src/inference/rdf.js +83 -0
  35. package/src/inference/rules/owl2el.js +33 -0
  36. package/src/inference/rules/owl2ql.js +29 -0
  37. package/src/inference/rules/owl2rl.js +823 -0
  38. package/src/io/FunctionalSyntaxParser.js +399 -0
  39. package/src/io/FunctionalSyntaxWriter.js +180 -0
  40. package/src/io/ManchesterSyntaxParser.js +398 -0
  41. package/src/io/OWLXMLParser.js +356 -0
  42. package/src/io/OntologyLoader.js +125 -0
  43. package/src/io/RDFGraphToOntology.js +702 -0
  44. package/src/io/RDFXMLParser.js +319 -0
  45. package/src/io/RDFXMLWriter.js +196 -0
  46. package/src/io/SWRLParser.js +150 -0
  47. package/src/io/TurtleParser.js +363 -0
  48. package/src/io/TurtleWriter.js +393 -0
  49. package/src/model/IRI.js +62 -0
  50. package/src/model/OWLAxiom.js +507 -0
  51. package/src/model/OWLClassExpression.js +242 -0
  52. package/src/model/OWLEntity.js +142 -0
  53. package/src/model/OWLLiteral.js +30 -0
  54. package/src/model/OWLModelManager.js +106 -0
  55. package/src/model/OWLOntology.js +119 -0
  56. package/src/model/SWRL.js +152 -0
  57. package/src/model/event/OWLModelManagerEvent.js +33 -0
  58. package/src/model/hierarchy/HierarchyProvider.js +214 -0
  59. package/src/profiles/OWL2Profiles.js +185 -0
  60. package/src/server/public/app.js +97 -0
  61. package/src/server/public/index.html +49 -0
  62. package/src/server/webServer.js +138 -0
  63. package/src/validation/GlobalRestrictionsValidator.js +172 -0
  64. package/test/core.test.js +111 -0
  65. package/test/exports-map.test.js +165 -0
  66. package/test/owl2/axioms.test.js +160 -0
  67. package/test/owl2/classExpressions.test.js +99 -0
  68. package/test/owl2/functional-syntax-writer.test.js +65 -0
  69. package/test/owl2/functional.test.js +113 -0
  70. package/test/owl2/global-restrictions.test.js +76 -0
  71. package/test/owl2/longtail.test.js +74 -0
  72. package/test/owl2/manchester-syntax.test.js +100 -0
  73. package/test/owl2/ontology-loader-imports.test.js +62 -0
  74. package/test/owl2/owlxml-parser.test.js +112 -0
  75. package/test/owl2/profile-reasoners.test.js +108 -0
  76. package/test/owl2/profiles.test.js +66 -0
  77. package/test/owl2/rdf-graph-to-ontology.test.js +177 -0
  78. package/test/owl2/rdfxml-punning.test.js +25 -0
  79. package/test/owl2/rdfxml-writer.test.js +55 -0
  80. package/test/owl2/rdfxml.test.js +91 -0
  81. package/test/owl2/reasoner-queries.test.js +58 -0
  82. package/test/owl2/sample-e2e.test.js +195 -0
  83. package/test/owl2/swrl-parser.test.js +60 -0
  84. package/test/owl2/swrl.test.js +131 -0
  85. package/test/owl2/turtle-writer.test.js +84 -0
  86. package/test/owl2/turtle.test.js +124 -0
  87. package/test/owl2rl/cax.test.js +57 -0
  88. package/test/owl2rl/cls.test.js +210 -0
  89. package/test/owl2rl/dt.test.js +72 -0
  90. package/test/owl2rl/eq.test.js +95 -0
  91. package/test/owl2rl/prp.test.js +198 -0
  92. package/test/owl2rl/scm.test.js +203 -0
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+
3
+ // ===========================================================================
4
+ // 业务案例 2:家庭关系推理 —— SWRL 规则推导「叔叔」与「成年」
5
+ // ---------------------------------------------------------------------------
6
+ // 【业务背景】
7
+ // 家谱/社交应用需要从一个家庭的显式关系里,自动推导出隐含亲属关系,
8
+ // 并根据年龄自动判断是否为「成年人」,用于权限与内容分级。
9
+ //
10
+ // 【业务规则】
11
+ // 1. 叔叔规则:x 的父亲/母亲是 y,y 的兄弟是 z ⇒ z 是 x 的叔叔(hasUncle)。
12
+ // 2. 成年规则:p 的年龄 age >= 18 ⇒ p 是 成年人(Adult)。(SWRL builtin)
13
+ // 3. 家庭关系:hasParent 的逆关系是 hasChild(owl:inverseOf)。
14
+ // 4. 标准本体对齐:Person ⊑ BFO material entity(人是一种物质实体)。
15
+ //
16
+ // 【用到的 OWL 2 能力】
17
+ // SWRL 规则(ClassAtom + ObjectPropertyAtom + DataPropertyAtom + BuiltInAtom)
18
+ // owl:inverseOf (prp-inv1/inv2) / SWRLReasoner 前向链执行
19
+ // BFO 顶层本体导入(Person ⊑ BFO_0000040 material entity)
20
+ //
21
+ // 【预期结果】
22
+ // - 由 alice -hasParent-> bob,bob -hasBrother-> carol,推出 alice -hasUncle-> carol。
23
+ // - bob 年龄 40 ⇒ 推出 bob 是 Adult;carol 年龄 12 ⇒ 不是 Adult。
24
+ // - alice/bob/carol 沿 BFO 层级被归因为 material entity。
25
+ // ===========================================================================
26
+
27
+ // 引入推理引擎与 SWRL 模型组件
28
+ const fs = require('fs'); // 文件系统:读取 BFO 本体
29
+ const path = require('path'); // 路径处理
30
+ const { parseRDFXML } = require('../src/io/RDFXMLParser'); // RDF/XML 解析器
31
+ const { TripleStore } = require('../src/inference/TripleStore');
32
+ const {
33
+ SWRLRule, SWRLClassAtom, SWRLObjectPropertyAtom, SWRLDataPropertyAtom,
34
+ SWRLBuiltInAtom, SWRLVariable
35
+ } = require('../src/model/SWRL'); // SWRL 规则模型(规则体/规则头原子)
36
+ const { SWRLReasoner } = require('../src/inference/SWRLReasoner'); // SWRL 前向链推理机
37
+ const { OWLClass, OWLObjectProperty, OWLDataProperty } = require('../src/model/OWLEntity'); // OWL 实体
38
+ const { IRI } = require('../src/model/IRI'); // IRI 构造器
39
+ const { NS } = require('../src/inference/rdf'); // 命名空间常量
40
+
41
+ // 简化命名空间引用
42
+ const RDF = NS.RDF, RDFS = NS.RDFS, OWL = NS.OWL, XSD = NS.XSD;
43
+ // 本案例统一前缀:http://family.example.com/#
44
+ const ex = (s) => 'http://family.example.com/#' + s;
45
+ // BFO 标准类 IRI(顶层本体)
46
+ const BFO_MATERIAL_ENTITY = 'http://purl.obolibrary.org/obo/BFO_0000040'; // material entity
47
+
48
+ // ========== 快捷构造函数:把字符串包装成模型对象 ==========
49
+ const cls = (s) => new OWLClass(IRI.create(ex(s))); // 构造 OWL 类
50
+ const op = (s) => new OWLObjectProperty(IRI.create(ex(s))); // 构造 OWL 对象属性
51
+ const dp = (s) => new OWLDataProperty(IRI.create(ex(s))); // 构造 OWL 数据属性
52
+ const v = (n) => new SWRLVariable(IRI.create('urn:swrl:var:' + n)); // 构造 SWRL 变量 ?x ?y ?z
53
+ const intLit = (n) => ({ lexicalValue: String(n), datatype: { iri: IRI.create(XSD + 'integer') } }); // 整数字面量
54
+
55
+ // 构建知识图谱(实例 + 关系 + 逆关系声明)
56
+ function buildStore() {
57
+ const s = new TripleStore();
58
+ // 加载 BFO 顶层本体(Person ⊑ material entity)
59
+ const bfoPath = path.join(__dirname, 'ontologies', 'bfo.owl');
60
+ const bfoOnt = parseRDFXML(fs.readFileSync(bfoPath, 'utf8'));
61
+ for (const ax of bfoOnt.getAxioms()) {
62
+ if (ax.constructor.name === 'OWLSubClassOfAxiom') {
63
+ const sub = typeof ax.subClass === 'string' ? ax.subClass : (ax.subClass && ax.subClass.iri && ax.subClass.iri._iri);
64
+ const sup = typeof ax.superClass === 'string' ? ax.superClass : (ax.superClass && ax.superClass.iri && ax.superClass.iri._iri);
65
+ if (sub && sup && sub.startsWith('http') && sup.startsWith('http')) s.add(sub, RDFS + 'subClassOf', sup);
66
+ }
67
+ }
68
+ // 声明 Person ⊑ BFO material entity
69
+ s.add(ex('Person'), RDFS + 'subClassOf', BFO_MATERIAL_ENTITY);
70
+ // 三个人物实例
71
+ s.add(ex('alice'), RDF + 'type', ex('Person'));
72
+ s.add(ex('bob'), RDF + 'type', ex('Person'));
73
+ s.add(ex('carol'), RDF + 'type', ex('Person'));
74
+ // 家庭关系:alice 的父亲是 bob;bob 的兄弟是 carol
75
+ s.add(ex('alice'), ex('hasParent'), ex('bob'));
76
+ s.add(ex('bob'), ex('hasBrother'), ex('carol'));
77
+ // hasParent 与 hasChild 互逆(OWL inverseOf)
78
+ s.add(ex('hasParent'), OWL + 'inverseOf', ex('hasChild'));
79
+ // 年龄(用 xsd:integer 字面量编码,与 SWRLReasoner 的字面量解析一致)
80
+ s.add(ex('bob'), ex('age'), '"40"^^<' + XSD + 'integer>'); // bob 40 岁
81
+ s.add(ex('carol'), ex('age'), '"12"^^<' + XSD + 'integer>'); // carol 12 岁
82
+ return s;
83
+ }
84
+
85
+ // 定义 SWRL 规则集
86
+ function rules() {
87
+ const SWRLB = 'http://www.w3.org/2003/11/swrlb#'; // SWRL builtin 命名空间
88
+ // 规则1:叔叔规则
89
+ // body: Person(?x) ∧ hasParent(?x,?y) ∧ hasBrother(?y,?z)
90
+ // head: hasUncle(?x,?z)
91
+ const uncle = new SWRLRule(
92
+ [
93
+ new SWRLClassAtom(cls('Person'), v('x')), // ?x 是 Person
94
+ new SWRLObjectPropertyAtom(op('hasParent'), v('x'), v('y')), // ?x 的父亲是 ?y
95
+ new SWRLObjectPropertyAtom(op('hasBrother'), v('y'), v('z')) // ?y 的兄弟是 ?z
96
+ ],
97
+ [new SWRLObjectPropertyAtom(op('hasUncle'), v('x'), v('z'))] // 推出 ?x 的叔叔是 ?z
98
+ );
99
+ // 规则2:成年规则(age >= 18 ⇒ Adult)
100
+ // body: Person(?p) ∧ age(?p,?a) ∧ greaterThanOrEqual(?a,18)
101
+ // head: Adult(?p)
102
+ const adult = new SWRLRule(
103
+ [
104
+ new SWRLClassAtom(cls('Person'), v('p')), // ?p 是 Person
105
+ new SWRLDataPropertyAtom(dp('age'), v('p'), v('a')), // ?p 的年龄是 ?a
106
+ new SWRLBuiltInAtom(IRI.create(SWRLB + 'greaterThanOrEqual'), [v('a'), intLit(18)]) // ?a >= 18
107
+ ],
108
+ [new SWRLClassAtom(cls('Adult'), v('p'))] // 推出 ?p 是 Adult
109
+ );
110
+ return [uncle, adult];
111
+ }
112
+
113
+ // 执行推理并返回业务结论
114
+ function run() {
115
+ const store = buildStore();
116
+ const reasoner = new SWRLReasoner(store);
117
+ reasoner.run(rules()); // 执行 SWRL 前向链推理
118
+
119
+ // 1) 叔叔推理:alice 的叔叔应是 carol
120
+ const uncleInferred = store.match(ex('alice'), ex('hasUncle'), ex('carol')).length === 1;
121
+ // 2) 成年判断:bob(40) 应是 Adult;carol(12) 不应是 Adult
122
+ const bobAdult = store.match(ex('bob'), RDF + 'type', ex('Adult')).length === 1;
123
+ const carolAdult = store.match(ex('carol'), RDF + 'type', ex('Adult')).length === 1;
124
+ // 3) 逆关系:hasParent 的逆是 hasChild → bob 的孩子应包含 alice
125
+ // SWRLReasoner 不跑 RL 规则,单独用 OWL2RLReasoner 验证 inverseOf
126
+ const { OWL2RLReasoner } = require('../src/inference/OWL2RLReasoner');
127
+ const rl = new OWL2RLReasoner(store);
128
+ rl.materialize();
129
+ const childInferred = store.match(ex('bob'), ex('hasChild'), ex('alice')).length === 1;
130
+ // 4) BFO 层级归因:alice 应被归为 material entity
131
+ const aliceIsMaterialEntity = store.has(ex('alice'), RDF + 'type', BFO_MATERIAL_ENTITY);
132
+
133
+ return { uncleInferred, bobAdult, carolAdult, childInferred, aliceIsMaterialEntity, store };
134
+ }
135
+
136
+ // 导出供测试与复用
137
+ module.exports = { run, buildStore, rules, ex };
138
+
139
+ // 直接运行时打印业务结论
140
+ if (require.main === module) {
141
+ const r = run();
142
+ console.log('推出 alice 的叔叔是 carol :', r.uncleInferred); // true
143
+ console.log('bob(40岁) 被判定为成年人 :', r.bobAdult); // true
144
+ console.log('carol(12岁) 被判定为成年人 :', r.carolAdult, '(应为 false)'); // false
145
+ console.log('逆关系推出 bob 的孩子是 alice:', r.childInferred); // true
146
+ console.log('alice 归为 BFO material entity:', r.aliceIsMaterialEntity); // true
147
+ }
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ // ===========================================================================
4
+ // 业务案例 3:医疗用药安全 —— 药物相互作用冲突检测(OWL 2 RL 一致性)
5
+ // ---------------------------------------------------------------------------
6
+ // 【业务背景】
7
+ // 医院开药系统要防止给同一病人同时开具两种「相互作用禁忌」的药物,
8
+ // 否则会造成医疗事故。系统在开处方时做一致性校验。
9
+ //
10
+ // 【业务规则】
11
+ // 1. 药物分类:阿司匹林(Aspirin) 与 华法林(Warfarin) 都是 抗凝药(Anticoagulant)。
12
+ // 2. 抗凝药 与 抗凝增效剂(AnticoagBooster) 联合使用属禁忌 → 用 disjoint
13
+ // 表达「安全合用(SafeCombo)」与「禁忌合用(ForbiddenCombo)」互斥。
14
+ // 3. 病人 pat-1 的用药方案 同时被判定为 SafeCombo 与 ForbiddenCombo 时,
15
+ // 推理机应报告本体不一致,系统据此拦截处方。
16
+ // 4. owl:hasKey:病人用 身份证号(ssn) 唯一标识;两个 ssn 相同的病人记录
17
+ // 应被识别为同一个人(prp-key)。
18
+ // 5. 标准本体对齐:药物 ⊑ OGMS medication role(OGMS_0000148)。
19
+ //
20
+ // 【用到的 OWL 2 能力】
21
+ // owl:disjointWith 一致性 (cax-dw) / owl:hasKey 唯一识别 (prp-key) /
22
+ // rdfs:subClassOf 分类 (cax-sco) / owl:sameAs 等价合并 (eq-*) /
23
+ // OGMS 医学本体导入(Drug ⊑ OGMS_0000148 medication role)
24
+ //
25
+ // 【预期结果】
26
+ // - 合法用药方案一致;叠加禁忌判定后 isConsistent()=false。
27
+ // - 两条 ssn 相同的病人记录被 prp-key 合并为同一个体(owl:sameAs)。
28
+ // - 药物沿 OGMS 层级被归因为 medication role。
29
+ // ===========================================================================
30
+
31
+ // 引入推理引擎核心组件
32
+ const fs = require('fs'); // 文件系统:读取 OGMS 本体
33
+ const path = require('path'); // 路径处理
34
+ const { parseRDFXML } = require('../src/io/RDFXMLParser'); // RDF/XML 解析器
35
+ const { TripleStore } = require('../src/inference/TripleStore'); // 三元组存储
36
+ const { OWL2RLReasoner } = require('../src/inference/OWL2RLReasoner'); // OWL 2 RL 推理机
37
+ const { NS } = require('../src/inference/rdf'); // 命名空间常量
38
+
39
+ // 简化命名空间引用
40
+ const RDF = NS.RDF, RDFS = NS.RDFS, OWL = NS.OWL, XSD = NS.XSD;
41
+ // 本案例统一前缀:http://hospital.example.com/rx#
42
+ const ex = (s) => 'http://hospital.example.com/rx#' + s;
43
+ // OGMS 标准类 IRI
44
+ const OGMS_MEDICATION_ROLE = 'http://purl.obolibrary.org/obo/OGMS_0000148'; // medication role
45
+
46
+ // 构建知识图谱(药物分类 + 病人记录 + 用药方案)
47
+ function buildStore() {
48
+ const s = new TripleStore();
49
+
50
+ // 加载 OGMS 医学本体(药物 ⊑ medication role)
51
+ const ogmsPath = path.join(__dirname, 'ontologies', 'ogms.owl');
52
+ const ogmsOnt = parseRDFXML(fs.readFileSync(ogmsPath, 'utf8'));
53
+ for (const ax of ogmsOnt.getAxioms()) {
54
+ if (ax.constructor.name === 'OWLSubClassOfAxiom') {
55
+ const sub = typeof ax.subClass === 'string' ? ax.subClass : (ax.subClass && ax.subClass.iri && ax.subClass.iri._iri);
56
+ const sup = typeof ax.superClass === 'string' ? ax.superClass : (ax.superClass && ax.superClass.iri && ax.superClass.iri._iri);
57
+ if (sub && sup && sub.startsWith('http') && sup.startsWith('http')) s.add(sub, RDFS + 'subClassOf', sup);
58
+ }
59
+ }
60
+ // 声明 Anticoagulant ⊑ OGMS medication role
61
+ s.add(ex('Anticoagulant'), RDFS + 'subClassOf', OGMS_MEDICATION_ROLE);
62
+
63
+ // ========== TBox:药物分类与方案约束 ==========
64
+ // 阿司匹林是抗凝药
65
+ s.add(ex('Aspirin'), RDFS + 'subClassOf', ex('Anticoagulant'));
66
+ // 华法林是抗凝药
67
+ s.add(ex('Warfarin'), RDFS + 'subClassOf', ex('Anticoagulant'));
68
+ // 安全合用 与 禁忌合用 互斥(disjointWith)
69
+ s.add(ex('SafeCombo'), OWL + 'disjointWith', ex('ForbiddenCombo'));
70
+ // 安全合用是用药方案的一种
71
+ s.add(ex('SafeCombo'), RDFS + 'subClassOf', ex('PrescriptionPlan'));
72
+ // 禁忌合用是用药方案的一种
73
+ s.add(ex('ForbiddenCombo'), RDFS + 'subClassOf', ex('PrescriptionPlan'));
74
+
75
+ // ========== hasKey:病人用 ssn 唯一标识 ==========
76
+ // 声明 Patient 的 hasKey 是 ssn 属性
77
+ s.add(ex('Patient'), OWL + 'hasKey', '_:key1');
78
+ // key 列表 _:key1 = ( ssn ),RDF List 结构
79
+ s.add('_:key1', RDF + 'first', ex('ssn'));
80
+ s.add('_:key1', RDF + 'rest', RDF + 'nil');
81
+
82
+ // ========== ABox:病人记录与用药方案 ==========
83
+ // 第一条病人记录(ssn: 110101199001011234)
84
+ s.add(ex('pat-record-1'), RDF + 'type', ex('Patient'));
85
+ s.add(ex('pat-record-1'), ex('ssn'), '"110101199001011234"^^<' + XSD + 'string>');
86
+ // 第二条病人记录,ssn 相同 → 应被 hasKey 识别为同一人
87
+ s.add(ex('pat-record-2'), RDF + 'type', ex('Patient'));
88
+ s.add(ex('pat-record-2'), ex('ssn'), '"110101199001011234"^^<' + XSD + 'string>');
89
+
90
+ // 一个用药方案,初始标记为安全合用
91
+ s.add(ex('plan-001'), RDF + 'type', ex('SafeCombo'));
92
+ return s;
93
+ }
94
+
95
+ // 执行推理并返回业务结论
96
+ function run() {
97
+ const store = buildStore();
98
+ const reasoner = new OWL2RLReasoner(store);
99
+ reasoner.materialize(); // 执行 OWL 2 RL 物化
100
+
101
+ // 1) 初始:方案仅标记为 SafeCombo,应一致
102
+ const consistentBefore = reasoner.isConsistent();
103
+
104
+ // 2) hasKey 合并:两条 ssn 相同的病人记录应被识别为同一个体(owl:sameAs)
105
+ const merged = store.has(ex('pat-record-1'), OWL + 'sameAs', ex('pat-record-2'))
106
+ || store.has(ex('pat-record-2'), OWL + 'sameAs', ex('pat-record-1'));
107
+
108
+ // 3) 制造冲突:医生又开了相互作用的药,方案被追加判定为 ForbiddenCombo
109
+ store.add(ex('plan-001'), RDF + 'type', ex('ForbiddenCombo'));
110
+ const r2 = new OWL2RLReasoner(store); // 重建推理机(因 store 已改)
111
+ r2.materialize();
112
+ const consistentAfter = r2.isConsistent(); // 现在应不一致
113
+ const conflictFound = r2.inconsistencies.length > 0; // 推理机应记录冲突明细
114
+
115
+ // 4) OGMS 层级归因:Anticoagulant 应被归为 medication role
116
+ const anticoagulantIsMedRole = store.has(ex('Anticoagulant'), RDFS + 'subClassOf', OGMS_MEDICATION_ROLE);
117
+
118
+ return { consistentBefore, merged, consistentAfter, conflictFound, anticoagulantIsMedRole, store };
119
+ }
120
+
121
+ // 导出供测试与复用
122
+ module.exports = { run, buildStore, ex };
123
+
124
+ // 直接运行时打印业务结论
125
+ if (require.main === module) {
126
+ const r = run();
127
+ console.log('初始合法用药方案一致 :', r.consistentBefore); // true
128
+ console.log('相同ssn病人记录被识别为同一人:', r.merged); // true
129
+ console.log('叠加禁忌后触发不一致 :', !r.consistentAfter); // true(取反后)
130
+ console.log('推理机报告了冲突明细 :', r.conflictFound); // true
131
+ console.log('抗凝药归为 OGMS medication role:', r.anticoagulantIsMedRole); // true
132
+ }
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ // ===========================================================================
4
+ // 业务案例 4:知识库内容发布 —— Turtle 解析 → 本体建模 → 序列化 round-trip
5
+ // ---------------------------------------------------------------------------
6
+ // 【业务背景】
7
+ // 内容团队用 Turtle 手写一份「课程知识图谱」本体(类、层级、属性、个体),
8
+ // 发布平台要:(a) 解析为结构化本体做校验,(b) 再用序列化器导出为
9
+ // Turtle / Functional Syntax / RDF/XML,供下游系统消费。要求解析-导出
10
+ // 往返(round-trip)后核心公理不丢失。
11
+ //
12
+ // 【业务规则】
13
+ // 1. 类层级:编程课(ProgrammingCourse) / 数据课(DataCourse) ⊑ 课程(Course)。
14
+ // 2. 属性:授课(teaches) 的 domain 是 讲师(Instructor),range 是 Course。
15
+ // 3. 个体:讲师 alice 讲授 课程 cs101。
16
+ // 4. 前缀保真:解析时记录的 ex: 前缀,导出时应保留缩写。
17
+ // 5. 标准本体对齐:Course ⊑ IAO document(IAO_0000310),Instructor ⊑ BFO role(BFO_0000023)。
18
+ //
19
+ // 【用到的 OWL 2 能力】
20
+ // TurtleParser / triplesToOntology 桥接 / TurtleWriter / FunctionalSyntaxWriter
21
+ // / RDFXMLWriter / OWLOntology 前缀保真 (addPrefix/getPrefixes)
22
+ // IAO 信息本体导入(Course ⊑ IAO_0000310 document)
23
+ //
24
+ // 【预期结果】
25
+ // - Turtle 解析 + 桥接得到包含类/属性/个体的本体(公理数 > 0)。
26
+ // - TurtleWriter 导出文本可被 TurtleParser 再次解析(round-trip)。
27
+ // - 三种 Writer 均产出非空且含关键标识符的文本。
28
+ // - Course 沿 IAO 层级被归因为 document。
29
+ // ===========================================================================
30
+
31
+ // 引入解析器与序列化器
32
+ const fs = require('fs'); // 文件系统:读取 IAO 本体
33
+ const path = require('path'); // 路径处理
34
+ const { parseRDFXML } = require('../src/io/RDFXMLParser'); // RDF/XML 解析器
35
+ const { TurtleParser } = require('../src/io/TurtleParser'); // Turtle 解析器
36
+ const { triplesToOntology } = require('../src/io/RDFGraphToOntology'); // TripleStore → OWLOntology 桥接
37
+ const { writeTurtle } = require('../src/io/TurtleWriter'); // Turtle 序列化器
38
+ const { writeFunctionalSyntax } = require('../src/io/FunctionalSyntaxWriter'); // OWL Functional Syntax 序列化器
39
+ const { writeRDFXML } = require('../src/io/RDFXMLWriter'); // RDF/XML 序列化器
40
+
41
+ // ========== 输入:课程知识图谱 Turtle 文本(含 IAO 标准本体对齐) ==========
42
+ // 内容团队手写的 Turtle,声明了类、层级、属性和个体,并挂到 IAO 标准类
43
+ const COURSE_TTL = [
44
+ '@prefix ex: <http://edu.example.com/kg#> .', // 定义 ex 前缀
45
+ '@prefix owl: <http://www.w3.org/2002/07/owl#> .', // 定义 owl 前缀
46
+ '@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .', // 定义 rdfs 前缀
47
+ '@prefix obo: <http://purl.obolibrary.org/obo/> .', // 定义 obo 前缀(OBO 标准 IRI)
48
+ '',
49
+ 'ex:Course a owl:Class ; rdfs:subClassOf obo:IAO_0000310 .', // Course ⊑ IAO document
50
+ 'ex:ProgrammingCourse a owl:Class ; rdfs:subClassOf ex:Course .', // ProgrammingCourse ⊑ Course
51
+ 'ex:DataCourse a owl:Class ; rdfs:subClassOf ex:Course .', // DataCourse ⊑ Course
52
+ 'ex:Instructor a owl:Class ; rdfs:subClassOf obo:BFO_0000023 .', // Instructor ⊑ BFO role
53
+ 'ex:teaches a owl:ObjectProperty ; rdfs:domain ex:Instructor ; rdfs:range ex:Course .', // teaches 属性定义
54
+ 'ex:alice a ex:Instructor .', // alice 是讲师
55
+ 'ex:cs101 a ex:ProgrammingCourse .', // cs101 是编程课
56
+ 'ex:alice ex:teaches ex:cs101 .', // alice 讲授 cs101
57
+ ''
58
+ ].join('\n');
59
+
60
+ // 执行解析 → 建模 → 序列化 → round-trip 全流程
61
+ function run() {
62
+ // 1) Turtle → TripleStore:把文本解析成三元组存储
63
+ const store = new TurtleParser().parse(COURSE_TTL);
64
+
65
+ // 2) TripleStore → OWLOntology:结构化桥接为 OWL 本体对象
66
+ const ont = triplesToOntology(store);
67
+ const axiomCount = ont.getAxiomCount(); // 统计公理数(应 > 0)
68
+
69
+ // 3) 前缀保真演示:发布平台记录命名空间缩写
70
+ ont.addPrefix('ex', 'http://edu.example.com/kg#');
71
+ const prefixes = ont.getPrefixes(); // 读取已记录的前缀映射
72
+
73
+ // 4) 三种序列化导出:Turtle / Functional Syntax / RDF/XML
74
+ const turtleOut = writeTurtle(ont); // 导出 Turtle
75
+ const fsOut = writeFunctionalSyntax(ont); // 导出 OWL Functional Syntax
76
+ const xmlOut = writeRDFXML(ont); // 导出 RDF/XML
77
+
78
+ // 5) round-trip:导出的 Turtle 能再次被解析,验证核心公理不丢失
79
+ const store2 = new TurtleParser().parse(turtleOut);
80
+ const ont2 = triplesToOntology(store2);
81
+
82
+ // 6) IAO 层级归因验证:Course 应被归为 IAO document
83
+ const courseIsDocument = turtleOut.includes('IAO_0000310');
84
+
85
+ return {
86
+ axiomCount, // 原始公理数
87
+ prefixes, // 前缀映射
88
+ turtleOut, fsOut, xmlOut, // 三种导出文本
89
+ turtleHasCourse: turtleOut.includes('Course'), // Turtle 导出含 Course
90
+ fsHasSubClass: /SubClassOf/.test(fsOut), // FS 导出含 SubClassOf
91
+ xmlIsXML: xmlOut.trim().startsWith('<?xml'), // RDF/XML 是合法 XML
92
+ roundTripAxiomCount: ont2.getAxiomCount(), // round-trip 后公理数
93
+ roundTripOK: ont2.getAxiomCount() > 0, // round-trip 成功标志
94
+ courseIsDocument // Course ⊑ IAO document
95
+ };
96
+ }
97
+
98
+ // 导出供测试与复用
99
+ module.exports = { run, COURSE_TTL };
100
+
101
+ // 直接运行时打印业务结论
102
+ if (require.main === module) {
103
+ const r = run();
104
+ console.log('解析得到公理数 :', r.axiomCount); // > 0
105
+ console.log('记录的前缀 :', JSON.stringify(r.prefixes)); // 含 ex 前缀
106
+ console.log('Turtle 导出含 Course :', r.turtleHasCourse); // true
107
+ console.log('FS 导出含 SubClassOf :', r.fsHasSubClass); // true
108
+ console.log('RDF/XML 导出为合法XML :', r.xmlIsXML); // true
109
+ console.log('round-trip 后公理数 :', r.roundTripAxiomCount, '(>0 即往返成功)'); // > 0
110
+ console.log('Course ⊑ IAO document :', r.courseIsDocument); // true
111
+ }
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+
3
+ // ===========================================================================
4
+ // 业务案例 5:企业数据集成(OBDA)—— OWL 2 QL 查询 + EL 分类 + Profile 校验
5
+ // ---------------------------------------------------------------------------
6
+ // 【业务背景】
7
+ // 企业把分散的数据库统一映射到一个「领域本体」上做集成查询(OBDA:
8
+ // Ontology-Based Data Access)。为了在大数据量下仍高效,限定使用
9
+ // OWL 2 QL profile(适合查询重写)与 OWL 2 EL profile(适合大规模分类),
10
+ // 并先用 profile 检查器确认本体没有超出对应 profile 的表达力。
11
+ //
12
+ // 【业务规则】
13
+ // 1. 组织层级:正式员工(RegularEmployee) / 合同工(Contractor) ⊑ 员工(Employee)
14
+ // ⊑ 人员(Person)。部门(Department) 通过 employs 关联员工。
15
+ // 2. QL 场景:查询「所有员工」时,正式员工与合同工的实例都应被召回
16
+ // (子类实例自动归属父类)。
17
+ // 3. EL 场景:传递闭包 —— partOf 具有传递性,「小组 ⊑ 部门 ⊑ 公司」
18
+ // 应推出「小组 partOf 公司」(prp-trp)。
19
+ // 4. Profile 校验:确认该本体不含 RL/QL/EL 之外的构造。
20
+ // 5. 标准本体对齐:Person ⊑ BFO material entity,Department ⊑ BFO object aggregate。
21
+ //
22
+ // 【用到的 OWL 2 能力】
23
+ // OWL2QLReasoner / OWL2ELReasoner / ReasonerQueries 查询 API /
24
+ // prp-trp 传递性 / OWL2Profiles profile 检查 (checkQL / checkEL) /
25
+ // BFO 顶层本体导入(Person ⊑ BFO_0000040 material entity)
26
+ //
27
+ // 【预期结果】
28
+ // - QL:查询 Employee 召回全部 3 名员工(含子类实例)。
29
+ // - EL:小组 partOf 公司 经传递性被推导出来。
30
+ // - profile 检查器对本体的违规列表可正常返回(数组)。
31
+ // - 员工沿 BFO 层级被归因为 material entity。
32
+ // ===========================================================================
33
+
34
+ // 引入推理引擎与 Profile 检查器
35
+ const fs = require('fs'); // 文件系统:读取 BFO 本体
36
+ const path = require('path'); // 路径处理
37
+ const { parseRDFXML } = require('../src/io/RDFXMLParser'); // RDF/XML 解析器
38
+ const { TripleStore } = require('../src/inference/TripleStore'); // 三元组存储
39
+ const { OWL2QLReasoner, OWL2ELReasoner } = require('../src/inference/OWL2ProfileReasoners'); // QL/EL 专用推理机
40
+ const { ReasonerQueries } = require('../src/inference/ReasonerQueries'); // 查询 API
41
+ const { checkQL, checkEL } = require('../src/profiles/OWL2Profiles'); // QL/EL Profile 检查器
42
+ const { triplesToOntology } = require('../src/io/RDFGraphToOntology'); // TripleStore → OWLOntology 桥接
43
+ const { NS } = require('../src/inference/rdf'); // 命名空间常量
44
+
45
+ // 简化命名空间引用
46
+ const RDF = NS.RDF, RDFS = NS.RDFS, OWL = NS.OWL;
47
+ // 本案例统一前缀:http://corp.example.com/org#
48
+ const ex = (s) => 'http://corp.example.com/org#' + s;
49
+ // BFO 标准类 IRI
50
+ const BFO_MATERIAL_ENTITY = 'http://purl.obolibrary.org/obo/BFO_0000040'; // material entity
51
+ const BFO_OBJECT_AGGREGATE = 'http://purl.obolibrary.org/obo/BFO_0000027'; // object aggregate
52
+
53
+ // 构建知识图谱(组织层级 + 员工实例 + partOf 传递链)
54
+ function buildStore() {
55
+ const s = new TripleStore();
56
+
57
+ // 加载 BFO 顶层本体(Person ⊑ material entity,Department ⊑ object aggregate)
58
+ const bfoPath = path.join(__dirname, 'ontologies', 'bfo.owl');
59
+ const bfoOnt = parseRDFXML(fs.readFileSync(bfoPath, 'utf8'));
60
+ for (const ax of bfoOnt.getAxioms()) {
61
+ if (ax.constructor.name === 'OWLSubClassOfAxiom') {
62
+ const sub = typeof ax.subClass === 'string' ? ax.subClass : (ax.subClass && ax.subClass.iri && ax.subClass.iri._iri);
63
+ const sup = typeof ax.superClass === 'string' ? ax.superClass : (ax.superClass && ax.superClass.iri && ax.superClass.iri._iri);
64
+ if (sub && sup && sub.startsWith('http') && sup.startsWith('http')) s.add(sub, RDFS + 'subClassOf', sup);
65
+ }
66
+ }
67
+
68
+ // ========== TBox:类层级(含 BFO 对齐) ==========
69
+ // 人员 ⊑ BFO material entity
70
+ s.add(ex('Person'), RDFS + 'subClassOf', BFO_MATERIAL_ENTITY);
71
+ // 部门 ⊑ BFO object aggregate
72
+ s.add(ex('Department'), RDFS + 'subClassOf', BFO_OBJECT_AGGREGATE);
73
+ // 正式员工 ⊑ 员工
74
+ s.add(ex('RegularEmployee'), RDFS + 'subClassOf', ex('Employee'));
75
+ // 合同工 ⊑ 员工
76
+ s.add(ex('Contractor'), RDFS + 'subClassOf', ex('Employee'));
77
+ // 员工 ⊑ 人员
78
+ s.add(ex('Employee'), RDFS + 'subClassOf', ex('Person'));
79
+
80
+ // ========== ABox:员工实例(分散在各子类) ==========
81
+ s.add(ex('emp-alice'), RDF + 'type', ex('RegularEmployee')); // alice 是正式员工
82
+ s.add(ex('emp-bob'), RDF + 'type', ex('RegularEmployee')); // bob 是正式员工
83
+ s.add(ex('emp-carol'), RDF + 'type', ex('Contractor')); // carol 是合同工
84
+
85
+ // ========== EL 场景:partOf 传递链 ==========
86
+ // 声明 partOf 是传递属性
87
+ s.add(ex('partOf'), RDF + 'type', OWL + 'TransitiveProperty');
88
+ // 小组 partOf 部门
89
+ s.add(ex('team-ml'), ex('partOf'), ex('dept-ai'));
90
+ // 部门 partOf 公司
91
+ s.add(ex('dept-ai'), ex('partOf'), ex('company-hq'));
92
+ // 声明部门实例类型
93
+ s.add(ex('dept-ai'), RDF + 'type', ex('Department'));
94
+ return s;
95
+ }
96
+
97
+ // 执行 QL 查询 + EL 分类 + Profile 校验
98
+ function run() {
99
+ const store = buildStore();
100
+
101
+ // --- QL:集成查询「所有员工」(子类实例自动归属父类) ---
102
+ const ql = new OWL2QLReasoner(store);
103
+ ql.materialize(); // QL 物化
104
+ const qQL = new ReasonerQueries(ql); // 查询 API
105
+ const allEmployees = qQL.getInstances(ex('Employee')); // 召回所有员工实例
106
+ const qlCarolIsEmployee = qQL.getTypes(ex('emp-carol')).includes(ex('Employee')); // carol 被归为 Employee
107
+ const qlCarolIsPerson = qQL.getTypes(ex('emp-carol')).includes(ex('Person')); // carol 被归为 Person
108
+
109
+ // --- EL:传递闭包分类 ---
110
+ const el = new OWL2ELReasoner(store);
111
+ el.materialize(); // EL 物化(含 prp-trp 传递性)
112
+ const teamInCompany = store.has(ex('team-ml'), ex('partOf'), ex('company-hq')); // 小组 partOf 公司
113
+
114
+ // --- Profile 校验:确认本体是否超出 QL/EL 表达力 ---
115
+ const ont = triplesToOntology(store); // 桥接为 OWLOntology
116
+ const qlViolations = checkQL(ont); // QL 违规列表
117
+ const elViolations = checkEL(ont); // EL 违规列表
118
+
119
+ // --- BFO 层级归因:员工应被归为 material entity ---
120
+ const aliceIsMaterialEntity = store.has(ex('emp-alice'), RDF + 'type', BFO_MATERIAL_ENTITY);
121
+ const deptIsObjectAggregate = store.has(ex('dept-ai'), RDF + 'type', BFO_OBJECT_AGGREGATE);
122
+
123
+ return {
124
+ employeeCount: allEmployees.length, // 召回员工数(应=3)
125
+ allEmployees, // 员工实例列表
126
+ qlCarolIsEmployee, // carol 归为 Employee
127
+ qlCarolIsPerson, // carol 归为 Person
128
+ teamInCompany, // 小组 partOf 公司被传递推出
129
+ qlViolationsIsArray: Array.isArray(qlViolations), // QL 校验返回数组
130
+ elViolationsIsArray: Array.isArray(elViolations), // EL 校验返回数组
131
+ qlViolationCount: qlViolations.length, // QL 违规数
132
+ elViolationCount: elViolations.length, // EL 违规数
133
+ aliceIsMaterialEntity, // 员工归为 BFO material entity
134
+ deptIsObjectAggregate, // 部门归为 BFO object aggregate
135
+ store
136
+ };
137
+ }
138
+
139
+ // 导出供测试与复用
140
+ module.exports = { run, buildStore, ex };
141
+
142
+ // 直接运行时打印业务结论
143
+ if (require.main === module) {
144
+ const r = run();
145
+ console.log('QL 集成查询召回员工数 :', r.employeeCount, '(含子类实例, 应为3)'); // 3
146
+ console.log(' 合同工 carol 归为 Employee:', r.qlCarolIsEmployee); // true
147
+ console.log(' 合同工 carol 归为 Person :', r.qlCarolIsPerson); // true
148
+ console.log('EL 传递推出 小组partOf公司 :', r.teamInCompany); // true
149
+ console.log('QL profile 校验返回数组 :', r.qlViolationsIsArray, '(违规数', r.qlViolationCount + ')'); // true
150
+ console.log('EL profile 校验返回数组 :', r.elViolationsIsArray, '(违规数', r.elViolationCount + ')'); // true
151
+ console.log('员工归为 BFO material entity :', r.aliceIsMaterialEntity); // true
152
+ console.log('部门归为 BFO object aggregate:', r.deptIsObjectAggregate); // true
153
+ }