n3 1.19.0 → 1.20.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.
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ exports.getRulesFromDataset = getRulesFromDataset;
8
+ var _N3DataFactory = _interopRequireDefault(require("./N3DataFactory"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ /**
11
+ * Gets rules from a dataset. This will only collect horn rules declared using log:implies.
12
+ */
13
+ function getRulesFromDataset(dataset) {
14
+ const rules = [];
15
+ for (const {
16
+ subject,
17
+ object
18
+ } of dataset.match(null, _N3DataFactory.default.namedNode('http://www.w3.org/2000/10/swap/log#implies'), null, _N3DataFactory.default.defaultGraph())) {
19
+ const premise = [...dataset.match(null, null, null, subject)];
20
+ const conclusion = [...dataset.match(null, null, null, object)];
21
+ rules.push({
22
+ premise,
23
+ conclusion
24
+ });
25
+ }
26
+ return rules;
27
+ }
28
+ class N3Reasoner {
29
+ constructor(store) {
30
+ this._store = store;
31
+ }
32
+ _add(subject, predicate, object, graphItem, cb) {
33
+ // Only add to the remaining indexes if there is not already a value in the index
34
+ if (!this._store._addToIndex(graphItem.subjects, subject, predicate, object)) return;
35
+ this._store._addToIndex(graphItem.predicates, predicate, object, subject);
36
+ this._store._addToIndex(graphItem.objects, object, subject, predicate);
37
+ cb();
38
+ }
39
+
40
+ // eslint-disable-next-line no-warning-comments
41
+ _evaluatePremise(rule, content, cb, i = 0) {
42
+ let v1, v2, value, index1, index2;
43
+ const [val0, val1, val2] = rule.premise[i].value,
44
+ index = content[rule.premise[i].content];
45
+ const v0 = !(value = val0.value);
46
+ for (value in v0 ? index : {
47
+ [value]: index[value]
48
+ }) {
49
+ if (index1 = index[value]) {
50
+ if (v0) val0.value = Number(value);
51
+ v1 = !(value = val1.value);
52
+ for (value in v1 ? index1 : {
53
+ [value]: index1[value]
54
+ }) {
55
+ if (index2 = index1[value]) {
56
+ if (v1) val1.value = Number(value);
57
+ v2 = !(value = val2.value);
58
+ for (value in v2 ? index2 : {
59
+ [value]: index2[value]
60
+ }) {
61
+ if (v2) val2.value = Number(value);
62
+ if (i === rule.premise.length - 1) rule.conclusion.forEach(c => {
63
+ // eslint-disable-next-line max-nested-callbacks
64
+ this._add(c.subject.value, c.predicate.value, c.object.value, content, () => {
65
+ cb(c);
66
+ });
67
+ });else this._evaluatePremise(rule, content, cb, i + 1);
68
+ }
69
+ if (v2) val2.value = null;
70
+ }
71
+ }
72
+ if (v1) val1.value = null;
73
+ }
74
+ }
75
+ if (v0) val0.value = null;
76
+ }
77
+ _evaluateRules(rules, content, cb) {
78
+ for (let i = 0; i < rules.length; i++) {
79
+ this._evaluatePremise(rules[i], content, cb);
80
+ }
81
+ }
82
+
83
+ // A naive reasoning algorithm where rules are just applied by repeatedly applying rules
84
+ // until no more evaluations are made
85
+ _reasonGraphNaive(rules, content) {
86
+ const newRules = [];
87
+ function addRule(conclusion) {
88
+ if (conclusion.next) conclusion.next.forEach(rule => {
89
+ newRules.push([conclusion.subject.value, conclusion.predicate.value, conclusion.object.value, rule]);
90
+ });
91
+ }
92
+
93
+ // eslint-disable-next-line func-style
94
+ const addConclusions = conclusion => {
95
+ conclusion.forEach(c => {
96
+ // eslint-disable-next-line max-nested-callbacks
97
+ this._add(c.subject.value, c.predicate.value, c.object.value, content, () => {
98
+ addRule(c);
99
+ });
100
+ });
101
+ };
102
+ this._evaluateRules(rules, content, addRule);
103
+ let r;
104
+ while ((r = newRules.pop()) !== undefined) {
105
+ const [subject, predicate, object, rule] = r;
106
+ const v1 = rule.basePremise.subject.value;
107
+ if (!v1) rule.basePremise.subject.value = subject;
108
+ const v2 = rule.basePremise.predicate.value;
109
+ if (!v2) rule.basePremise.predicate.value = predicate;
110
+ const v3 = rule.basePremise.object.value;
111
+ if (!v3) rule.basePremise.object.value = object;
112
+ if (rule.premise.length === 0) {
113
+ addConclusions(rule.conclusion);
114
+ } else {
115
+ this._evaluatePremise(rule, content, addRule);
116
+ }
117
+ if (!v1) rule.basePremise.subject.value = null;
118
+ if (!v2) rule.basePremise.predicate.value = null;
119
+ if (!v3) rule.basePremise.object.value = null;
120
+ }
121
+ }
122
+ _createRule({
123
+ premise,
124
+ conclusion
125
+ }) {
126
+ const varMapping = {};
127
+ const toId = value => value.termType === 'Variable' ?
128
+ // If the term is a variable, then create an empty object that values can be placed into
129
+ varMapping[value.value] = varMapping[value.value] || {} :
130
+ // If the term is not a variable, then set the ID value
131
+ {
132
+ value: this._store._termToNewNumericId(value)
133
+ };
134
+
135
+ // eslint-disable-next-line func-style
136
+ const t = term => ({
137
+ subject: toId(term.subject),
138
+ predicate: toId(term.predicate),
139
+ object: toId(term.object)
140
+ });
141
+ return {
142
+ premise: premise.map(p => t(p)),
143
+ conclusion: conclusion.map(p => t(p)),
144
+ variables: Object.values(varMapping)
145
+ };
146
+ }
147
+ reason(rules) {
148
+ if (!Array.isArray(rules)) {
149
+ rules = getRulesFromDataset(rules);
150
+ }
151
+ rules = rules.map(rule => this._createRule(rule));
152
+ for (const r1 of rules) {
153
+ for (const r2 of rules) {
154
+ for (let i = 0; i < r2.premise.length; i++) {
155
+ const p = r2.premise[i];
156
+ for (const c of r1.conclusion) {
157
+ if (termEq(p.subject, c.subject) && termEq(p.predicate, c.predicate) && termEq(p.object, c.object)) {
158
+ const set = new Set();
159
+ const premise = [];
160
+
161
+ // Since these *will* be substituted when we apply the rule,
162
+ // we need to do this, so that we index correctly in the subsequent section
163
+ p.subject.value = p.subject.value || 1;
164
+ p.object.value = p.object.value || 1;
165
+ p.predicate.value = p.predicate.value || 1;
166
+ for (let j = 0; j < r2.premise.length; j++) {
167
+ if (j !== i) {
168
+ premise.push(getIndex(r2.premise[j], set));
169
+ }
170
+ }
171
+
172
+ // eslint-disable-next-line no-warning-comments
173
+ // TODO: Create new rule, with new indexing
174
+ // Future, 'collapse' the next statements when they share a premise/base-premise
175
+ (c.next = c.next || []).push({
176
+ premise,
177
+ conclusion: r2.conclusion,
178
+ // This is a single premise of the form { subject, predicate, object },
179
+ // which we can use to instantiate the rule using the new data that was emitted
180
+ basePremise: p
181
+ });
182
+ }
183
+ r2.variables.forEach(v => {
184
+ v.value = null;
185
+ });
186
+ }
187
+ }
188
+ }
189
+ }
190
+ for (const rule of rules) {
191
+ const set = new Set();
192
+ rule.premise = rule.premise.map(p => getIndex(p, set));
193
+ }
194
+ const graphs = this._store._getGraphs();
195
+ for (const graphId in graphs) {
196
+ this._reasonGraphNaive(rules, graphs[graphId]);
197
+ }
198
+ this._store._size = null;
199
+ }
200
+ }
201
+ exports.default = N3Reasoner;
202
+ function getIndex({
203
+ subject,
204
+ predicate,
205
+ object
206
+ }, set) {
207
+ const s = subject.value || set.has(subject) || (set.add(subject), false);
208
+ const p = predicate.value || set.has(predicate) || (set.add(predicate), false);
209
+ const o = object.value || set.has(object) || (set.add(object), false);
210
+ return !s && p ? {
211
+ content: 'predicates',
212
+ value: [predicate, object, subject]
213
+ } : o ? {
214
+ content: 'objects',
215
+ value: [object, subject, predicate]
216
+ } : {
217
+ content: 'subjects',
218
+ value: [subject, predicate, object]
219
+ };
220
+ }
221
+ function termEq(t1, t2) {
222
+ if (t1.value === null) {
223
+ t1.value = t2.value;
224
+ }
225
+ return t1.value === t2.value;
226
+ }
package/lib/index.js CHANGED
@@ -57,6 +57,12 @@ Object.defineProperty(exports, "Quad", {
57
57
  return _N3DataFactory.Quad;
58
58
  }
59
59
  });
60
+ Object.defineProperty(exports, "Reasoner", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _N3Reasoner.default;
64
+ }
65
+ });
60
66
  Object.defineProperty(exports, "Store", {
61
67
  enumerable: true,
62
68
  get: function () {
@@ -107,6 +113,12 @@ Object.defineProperty(exports, "Writer", {
107
113
  }
108
114
  });
109
115
  exports.default = void 0;
116
+ Object.defineProperty(exports, "getRulesFromDataset", {
117
+ enumerable: true,
118
+ get: function () {
119
+ return _N3Reasoner.getRulesFromDataset;
120
+ }
121
+ });
110
122
  Object.defineProperty(exports, "termFromId", {
111
123
  enumerable: true,
112
124
  get: function () {
@@ -124,6 +136,7 @@ var _N3Parser = _interopRequireDefault(require("./N3Parser"));
124
136
  var _N3Writer = _interopRequireDefault(require("./N3Writer"));
125
137
  var _N3Store = _interopRequireWildcard(require("./N3Store"));
126
138
  var _N3StoreFactory = _interopRequireDefault(require("./N3StoreFactory"));
139
+ var _N3Reasoner = _interopRequireWildcard(require("./N3Reasoner"));
127
140
  var _N3StreamParser = _interopRequireDefault(require("./N3StreamParser"));
128
141
  var _N3StreamWriter = _interopRequireDefault(require("./N3StreamWriter"));
129
142
  var Util = _interopRequireWildcard(require("./N3Util"));
@@ -144,6 +157,7 @@ var _default = exports.default = {
144
157
  StreamParser: _N3StreamParser.default,
145
158
  StreamWriter: _N3StreamWriter.default,
146
159
  Util,
160
+ Reasoner: _N3Reasoner.default,
147
161
  DataFactory: _N3DataFactory.default,
148
162
  Term: _N3DataFactory.Term,
149
163
  NamedNode: _N3DataFactory.NamedNode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3",
3
- "version": "1.19.0",
3
+ "version": "1.20.0",
4
4
  "description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
5
5
  "author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
6
6
  "keywords": [
@@ -37,6 +37,7 @@
37
37
  "arrayify-stream": "^1.0.0",
38
38
  "browserify": "^17.0.0",
39
39
  "colors": "^1.1.2",
40
+ "deep-taxonomy-benchmark": "^1.2.0",
40
41
  "docco": "^0.9.1",
41
42
  "eslint": "^5.14.1",
42
43
  "eslint-plugin-import": "^2.29.1",
@@ -0,0 +1,206 @@
1
+ import DF from './N3DataFactory';
2
+
3
+ /**
4
+ * Gets rules from a dataset. This will only collect horn rules declared using log:implies.
5
+ */
6
+ export function getRulesFromDataset(dataset) {
7
+ const rules = [];
8
+ for (const { subject, object } of dataset.match(null, DF.namedNode('http://www.w3.org/2000/10/swap/log#implies'), null, DF.defaultGraph())) {
9
+ const premise = [...dataset.match(null, null, null, subject)];
10
+ const conclusion = [...dataset.match(null, null, null, object)];
11
+ rules.push({ premise, conclusion });
12
+ }
13
+ return rules;
14
+ }
15
+
16
+ export default class N3Reasoner {
17
+ constructor(store) {
18
+ this._store = store;
19
+ }
20
+
21
+ _add(subject, predicate, object, graphItem, cb) {
22
+ // Only add to the remaining indexes if there is not already a value in the index
23
+ if (!this._store._addToIndex(graphItem.subjects, subject, predicate, object)) return;
24
+ this._store._addToIndex(graphItem.predicates, predicate, object, subject);
25
+ this._store._addToIndex(graphItem.objects, object, subject, predicate);
26
+ cb();
27
+ }
28
+
29
+ // eslint-disable-next-line no-warning-comments
30
+ _evaluatePremise(rule, content, cb, i = 0) {
31
+ let v1, v2, value, index1, index2;
32
+ const [val0, val1, val2] = rule.premise[i].value, index = content[rule.premise[i].content];
33
+ const v0 = !(value = val0.value);
34
+ for (value in v0 ? index : { [value]: index[value] }) {
35
+ if (index1 = index[value]) {
36
+ if (v0) val0.value = Number(value);
37
+ v1 = !(value = val1.value);
38
+ for (value in v1 ? index1 : { [value]: index1[value] }) {
39
+ if (index2 = index1[value]) {
40
+ if (v1) val1.value = Number(value);
41
+ v2 = !(value = val2.value);
42
+ for (value in v2 ? index2 : { [value]: index2[value] }) {
43
+ if (v2) val2.value = Number(value);
44
+
45
+ if (i === rule.premise.length - 1)
46
+ rule.conclusion.forEach(c => {
47
+ // eslint-disable-next-line max-nested-callbacks
48
+ this._add(c.subject.value, c.predicate.value, c.object.value, content, () => { cb(c); });
49
+ });
50
+ else
51
+ this._evaluatePremise(rule, content, cb, i + 1);
52
+ }
53
+ if (v2) val2.value = null;
54
+ }
55
+ }
56
+ if (v1) val1.value = null;
57
+ }
58
+ }
59
+ if (v0) val0.value = null;
60
+ }
61
+
62
+ _evaluateRules(rules, content, cb) {
63
+ for (let i = 0; i < rules.length; i++) {
64
+ this._evaluatePremise(rules[i], content, cb);
65
+ }
66
+ }
67
+
68
+ // A naive reasoning algorithm where rules are just applied by repeatedly applying rules
69
+ // until no more evaluations are made
70
+ _reasonGraphNaive(rules, content) {
71
+ const newRules = [];
72
+
73
+ function addRule(conclusion) {
74
+ if (conclusion.next)
75
+ conclusion.next.forEach(rule => {
76
+ newRules.push([conclusion.subject.value, conclusion.predicate.value, conclusion.object.value, rule]);
77
+ });
78
+ }
79
+
80
+ // eslint-disable-next-line func-style
81
+ const addConclusions = conclusion => {
82
+ conclusion.forEach(c => {
83
+ // eslint-disable-next-line max-nested-callbacks
84
+ this._add(c.subject.value, c.predicate.value, c.object.value, content, () => { addRule(c); });
85
+ });
86
+ };
87
+
88
+ this._evaluateRules(rules, content, addRule);
89
+
90
+ let r;
91
+ while ((r = newRules.pop()) !== undefined) {
92
+ const [subject, predicate, object, rule] = r;
93
+ const v1 = rule.basePremise.subject.value;
94
+ if (!v1) rule.basePremise.subject.value = subject;
95
+ const v2 = rule.basePremise.predicate.value;
96
+ if (!v2) rule.basePremise.predicate.value = predicate;
97
+ const v3 = rule.basePremise.object.value;
98
+ if (!v3) rule.basePremise.object.value = object;
99
+
100
+ if (rule.premise.length === 0) {
101
+ addConclusions(rule.conclusion);
102
+ }
103
+ else {
104
+ this._evaluatePremise(rule, content, addRule);
105
+ }
106
+
107
+ if (!v1) rule.basePremise.subject.value = null;
108
+ if (!v2) rule.basePremise.predicate.value = null;
109
+ if (!v3) rule.basePremise.object.value = null;
110
+ }
111
+ }
112
+
113
+ _createRule({ premise, conclusion }) {
114
+ const varMapping = {};
115
+
116
+ const toId = value => value.termType === 'Variable' ?
117
+ // If the term is a variable, then create an empty object that values can be placed into
118
+ (varMapping[value.value] = varMapping[value.value] || {}) :
119
+ // If the term is not a variable, then set the ID value
120
+ { value: this._store._termToNewNumericId(value) };
121
+
122
+ // eslint-disable-next-line func-style
123
+ const t = term => ({ subject: toId(term.subject), predicate: toId(term.predicate), object: toId(term.object) });
124
+
125
+ return {
126
+ premise: premise.map(p => t(p)),
127
+ conclusion: conclusion.map(p => t(p)),
128
+ variables: Object.values(varMapping),
129
+ };
130
+ }
131
+
132
+ reason(rules) {
133
+ if (!Array.isArray(rules)) {
134
+ rules = getRulesFromDataset(rules);
135
+ }
136
+ rules = rules.map(rule => this._createRule(rule));
137
+
138
+ for (const r1 of rules) {
139
+ for (const r2 of rules) {
140
+ for (let i = 0; i < r2.premise.length; i++) {
141
+ const p = r2.premise[i];
142
+ for (const c of r1.conclusion) {
143
+ if (termEq(p.subject, c.subject) && termEq(p.predicate, c.predicate) && termEq(p.object, c.object)) {
144
+ const set = new Set();
145
+
146
+ const premise = [];
147
+
148
+ // Since these *will* be substituted when we apply the rule,
149
+ // we need to do this, so that we index correctly in the subsequent section
150
+ p.subject.value = p.subject.value || 1;
151
+ p.object.value = p.object.value || 1;
152
+ p.predicate.value = p.predicate.value || 1;
153
+
154
+ for (let j = 0; j < r2.premise.length; j++) {
155
+ if (j !== i) {
156
+ premise.push(getIndex(r2.premise[j], set));
157
+ }
158
+ }
159
+
160
+ // eslint-disable-next-line no-warning-comments
161
+ // TODO: Create new rule, with new indexing
162
+ // Future, 'collapse' the next statements when they share a premise/base-premise
163
+ (c.next = c.next || []).push({
164
+ premise,
165
+ conclusion: r2.conclusion,
166
+ // This is a single premise of the form { subject, predicate, object },
167
+ // which we can use to instantiate the rule using the new data that was emitted
168
+ basePremise: p,
169
+ });
170
+ }
171
+ r2.variables.forEach(v => { v.value = null; });
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ for (const rule of rules) {
178
+ const set = new Set();
179
+ rule.premise = rule.premise.map(p => getIndex(p, set));
180
+ }
181
+
182
+ const graphs = this._store._getGraphs();
183
+ for (const graphId in graphs) {
184
+ this._reasonGraphNaive(rules, graphs[graphId]);
185
+ }
186
+
187
+ this._store._size = null;
188
+ }
189
+ }
190
+
191
+ function getIndex({ subject, predicate, object }, set) {
192
+ const s = subject.value || set.has(subject) || (set.add(subject), false);
193
+ const p = predicate.value || set.has(predicate) || (set.add(predicate), false);
194
+ const o = object.value || set.has(object) || (set.add(object), false);
195
+
196
+ return (!s && p) ? { content: 'predicates', value: [predicate, object, subject] } :
197
+ o ? { content: 'objects', value: [object, subject, predicate] } :
198
+ { content: 'subjects', value: [subject, predicate, object] };
199
+ }
200
+
201
+ function termEq(t1, t2) {
202
+ if (t1.value === null) {
203
+ t1.value = t2.value;
204
+ }
205
+ return t1.value === t2.value;
206
+ }
package/src/index.js CHANGED
@@ -3,6 +3,7 @@ import Parser from './N3Parser';
3
3
  import Writer from './N3Writer';
4
4
  import Store, { N3EntityIndex as EntityIndex } from './N3Store';
5
5
  import StoreFactory from './N3StoreFactory';
6
+ import Reasoner, { getRulesFromDataset } from './N3Reasoner';
6
7
  import StreamParser from './N3StreamParser';
7
8
  import StreamWriter from './N3StreamWriter';
8
9
  import * as Util from './N3Util';
@@ -34,6 +35,7 @@ export {
34
35
  StreamParser,
35
36
  StreamWriter,
36
37
  Util,
38
+ Reasoner,
37
39
 
38
40
  DataFactory,
39
41
 
@@ -48,6 +50,7 @@ export {
48
50
 
49
51
  termFromId,
50
52
  termToId,
53
+ getRulesFromDataset,
51
54
  };
52
55
 
53
56
  // Export all named exports as a default object for backward compatibility
@@ -61,6 +64,7 @@ export default {
61
64
  StreamParser,
62
65
  StreamWriter,
63
66
  Util,
67
+ Reasoner,
64
68
 
65
69
  DataFactory,
66
70