rdf-reasoner-konclude 0.3.2 → 0.4.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,12 @@
1
+ import type { Quad } from "@rdfjs/types";
2
+ export type ProbeKind = "subClassOf" | "type" | "unsupported";
3
+ export interface ProbePlan {
4
+ kind: ProbeKind;
5
+ probeQuads: Quad[];
6
+ probeKeys: Set<string>;
7
+ reason?: string;
8
+ }
9
+ export declare function tripleKey(s: string, p: string, o: string): string;
10
+ export declare function classifyAxiom(predicateIri: string, objectIsClassLike: boolean): ProbeKind;
11
+ export declare function buildEntailmentProbe(subjectIri: string, predicateIri: string, objectIri: string, objectIsClassLike: boolean, probeId?: string): ProbePlan;
12
+ //# sourceMappingURL=entailmentProbe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entailmentProbe.d.ts","sourceRoot":"","sources":["../ts/entailmentProbe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAQzC,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,IAAI,EAAE,CAAC;IACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,OAAO,GACzB,SAAS,CAKX;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,OAAO,EAC1B,OAAO,SAAO,GACb,SAAS,CA0CX"}
@@ -0,0 +1,57 @@
1
+ import { DataFactory } from "n3";
2
+ const RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
3
+ const RDFS_SUBCLASSOF = "http://www.w3.org/2000/01/rdf-schema#subClassOf";
4
+ const OWL_CLASS = "http://www.w3.org/2002/07/owl#Class";
5
+ const OWL_COMPLEMENT_OF = "http://www.w3.org/2002/07/owl#complementOf";
6
+ export function tripleKey(s, p, o) {
7
+ return `${s}\0${p}\0${o}`;
8
+ }
9
+ export function classifyAxiom(predicateIri, objectIsClassLike) {
10
+ if (!objectIsClassLike)
11
+ return "unsupported";
12
+ if (predicateIri === RDFS_SUBCLASSOF)
13
+ return "subClassOf";
14
+ if (predicateIri === RDF_TYPE)
15
+ return "type";
16
+ return "unsupported";
17
+ }
18
+ export function buildEntailmentProbe(subjectIri, predicateIri, objectIri, objectIsClassLike, probeId = "p0") {
19
+ const kind = classifyAxiom(predicateIri, objectIsClassLike);
20
+ if (kind === "unsupported") {
21
+ return {
22
+ kind,
23
+ probeQuads: [],
24
+ probeKeys: new Set(),
25
+ reason: objectIsClassLike
26
+ ? `predicate ${predicateIri} is not a supported entailment shape`
27
+ : "object is a literal; the entailment reduction requires an IRI object",
28
+ };
29
+ }
30
+ const { namedNode, blankNode, quad, defaultGraph } = DataFactory;
31
+ const g = defaultGraph();
32
+ const owlClass = namedNode(OWL_CLASS);
33
+ const negClass = blankNode(`vg_neg_${probeId}`);
34
+ const objNode = namedNode(objectIri);
35
+ const quads = [];
36
+ const keys = new Set();
37
+ const push = (s, sKey, p, o, oKey) => {
38
+ quads.push(quad(s, namedNode(p), o, g));
39
+ keys.add(tripleKey(sKey, p, oKey));
40
+ };
41
+ const negClassKey = `vg_neg_${probeId}`;
42
+ push(negClass, negClassKey, RDF_TYPE, owlClass, OWL_CLASS);
43
+ push(negClass, negClassKey, OWL_COMPLEMENT_OF, objNode, objectIri);
44
+ if (kind === "subClassOf") {
45
+ const subjNode = namedNode(subjectIri);
46
+ const witness = blankNode(`vg_wit_${probeId}`);
47
+ const witnessKey = `vg_wit_${probeId}`;
48
+ push(subjNode, subjectIri, RDFS_SUBCLASSOF, negClass, negClassKey);
49
+ push(witness, witnessKey, RDF_TYPE, subjNode, subjectIri);
50
+ }
51
+ else {
52
+ const subjNode = namedNode(subjectIri);
53
+ push(subjNode, subjectIri, RDF_TYPE, negClass, negClassKey);
54
+ }
55
+ return { kind, probeQuads: quads, probeKeys: keys };
56
+ }
57
+ //# sourceMappingURL=entailmentProbe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entailmentProbe.js","sourceRoot":"","sources":["../ts/entailmentProbe.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -14,9 +14,9 @@
14
14
  */
15
15
  import type { Quad } from "@rdfjs/types";
16
16
  import { Store } from "n3";
17
- export type { ReasoningOptions, ReasoningResult, StoreReasoningOptions, MaterializeOptions, MaterializeStoreOptions, ClassifyPropertiesStoreOptions, InferenceDelta, WhatIfOptions, ExplainOptions, ClassWarning, ValidationResult, ValidateOptions } from "./types.js";
17
+ export type { ReasoningOptions, ReasoningResult, StoreReasoningOptions, MaterializeOptions, MaterializeStoreOptions, ClassifyPropertiesStoreOptions, InferenceDelta, WhatIfOptions, ExplainOptions, ClassWarning, ValidationResult, ValidateOptions, RdfReasonerOptions, EntailmentResult, ExplainEntailmentOptions, LaconicPart, LaconicJustification, LaconicExplainOptions } from "./types.js";
18
18
  export { INFERRED_GRAPH_IRI, HYPOTHETICAL_IRI } from "./types.js";
19
- import type { ReasoningOptions, StoreReasoningOptions, MaterializeOptions, MaterializeStoreOptions, ClassifyPropertiesStoreOptions, InferenceDelta, WhatIfOptions, ExplainOptions, ValidationResult, ValidateOptions } from "./types.js";
19
+ import type { ReasoningOptions, StoreReasoningOptions, MaterializeOptions, MaterializeStoreOptions, ClassifyPropertiesStoreOptions, InferenceDelta, WhatIfOptions, ExplainOptions, ValidationResult, ValidateOptions, RdfReasonerOptions, EntailmentResult, ExplainEntailmentOptions, LaconicJustification, LaconicExplainOptions } from "./types.js";
20
20
  export declare class RdfReasoner {
21
21
  /** Resolves when the Worker WASM module is ready; rejects on init failure. */
22
22
  readonly ready: Promise<void>;
@@ -33,7 +33,8 @@ export declare class RdfReasoner {
33
33
  private _materializeCache;
34
34
  private _classifyPropertiesCache;
35
35
  private _consistencyCache;
36
- constructor();
36
+ private _entailmentProbeCounter;
37
+ constructor(opts?: RdfReasonerOptions);
37
38
  /**
38
39
  * Send a method call to the Worker and return a Promise for the result.
39
40
  * Pass `transfer` to transfer ownership of ArrayBuffers (zero-copy).
@@ -214,6 +215,9 @@ export declare class RdfReasoner {
214
215
  * Safe to call from inside a _queue body.
215
216
  */
216
217
  private _checkUnsatisfiabilityDirect;
218
+ private _isSubClassOfDirect;
219
+ private _isInstanceOfDirect;
220
+ private _isSatisfiableClassDirect;
217
221
  /** Compute minimal justifications for an axiom using the BlackBox algorithm.
218
222
  *
219
223
  * Returns a list of minimal subsets of the store's base quads, each of which
@@ -234,6 +238,31 @@ export declare class RdfReasoner {
234
238
  * owl:Thing rdfs:subClassOf owl:Nothing being emitted as an inferred triple.
235
239
  */
236
240
  explainInconsistency(store: Store, opts?: ExplainOptions): Promise<Quad[][]>;
241
+ /** Compute MIPS justifications for an inconsistent ontology and post-process
242
+ * each via the Horridge laconic algorithm to identify exactly which part of
243
+ * each axiom drives the clash.
244
+ *
245
+ * Returns [] if the ontology is consistent.
246
+ * Returns [] if `opts.maxJustifications === 0`.
247
+ * When the cost cap (`laconicMaxAxioms` / `laconicMaxParts`) is exceeded for
248
+ * a justification, that entry has `laconic.skipped === true`.
249
+ */
250
+ explainInconsistencyLaconic(store: Store, opts?: LaconicExplainOptions): Promise<Array<{
251
+ justification: Quad[];
252
+ laconic: LaconicJustification;
253
+ }>>;
254
+ /** Explain why a triple (subjectIri, predicateIri, objectIri) is entailed
255
+ * by the ontology, using entailment-as-unsatisfiability reduction.
256
+ *
257
+ * Returns an `EntailmentResult` containing:
258
+ * - `isEntailed` — `true/false/null` (null when ontology is inconsistent)
259
+ * - `justifications` — minimal justifications (each is a Quad[] subset)
260
+ * - `ontologyInconsistent` / `vacuous` — special-case flags
261
+ *
262
+ * Supports rdfs:subClassOf and rdf:type shapes. Returns unsupported for
263
+ * other predicates (isEntailed reflects only asserted facts).
264
+ */
265
+ explainEntailment(store: Store, subjectIri: string, predicateIri: string, objectIri: string, opts?: ExplainEntailmentOptions): Promise<EntailmentResult>;
237
266
  /** Run a combined diagnostic: check consistency, find unsatisfiable classes,
238
267
  * and (optionally) compute minimal justifications for each.
239
268
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -16,6 +16,8 @@ import { Store, DataFactory } from "n3";
16
16
  import { encodeToBuffers, decodeBuffers, computeStoreFingerprint } from "./intern.js";
17
17
  export { INFERRED_GRAPH_IRI, HYPOTHETICAL_IRI } from "./types.js";
18
18
  import { INFERRED_GRAPH_IRI, HYPOTHETICAL_IRI } from "./types.js";
19
+ import { buildEntailmentProbe, tripleKey as probeTripleKey } from "./entailmentProbe.js";
20
+ import { computeLaconicAsync, groupQuadsIntoAxioms, splitAxiom, axiomKey } from "./laconicJustification.js";
19
21
  // ---------------------------------------------------------------------------
20
22
  // OWL/RDF predicate IRIs used by explain
21
23
  // ---------------------------------------------------------------------------
@@ -52,8 +54,12 @@ export class RdfReasoner {
52
54
  _materializeCache = null;
53
55
  _classifyPropertiesCache = null;
54
56
  _consistencyCache = null;
55
- constructor() {
56
- this.worker = new Worker(new URL("./worker.js", import.meta.url), {
57
+ _entailmentProbeCounter = 0;
58
+ constructor(opts) {
59
+ const url = opts?.workerUrl
60
+ ? (typeof opts.workerUrl === "string" ? new URL(opts.workerUrl) : opts.workerUrl)
61
+ : new URL("./worker.js", import.meta.url);
62
+ this.worker = new Worker(url, {
57
63
  type: "module",
58
64
  });
59
65
  // Store the readyReject handle so the onerror handler can use it if the
@@ -716,6 +722,15 @@ export class RdfReasoner {
716
722
  const raw = (await this._callDirect("getUnsatisfiableClassBuffer", []));
717
723
  return raw.split('\n').filter(Boolean).includes(classIRI);
718
724
  }
725
+ async _isSubClassOfDirect(sub, sup) {
726
+ return (await this._callDirect("isSubClassOf", [sub, sup]));
727
+ }
728
+ async _isInstanceOfDirect(indi, cls) {
729
+ return (await this._callDirect("isInstanceOf", [indi, cls]));
730
+ }
731
+ async _isSatisfiableClassDirect(cls) {
732
+ return (await this._callDirect("isSatisfiableClass", [cls]));
733
+ }
719
734
  // -------------------------------------------------------------------------
720
735
  // explain()
721
736
  // -------------------------------------------------------------------------
@@ -1003,6 +1018,375 @@ export class RdfReasoner {
1003
1018
  return result;
1004
1019
  }
1005
1020
  // -------------------------------------------------------------------------
1021
+ // explainInconsistencyLaconic()
1022
+ // -------------------------------------------------------------------------
1023
+ /** Compute MIPS justifications for an inconsistent ontology and post-process
1024
+ * each via the Horridge laconic algorithm to identify exactly which part of
1025
+ * each axiom drives the clash.
1026
+ *
1027
+ * Returns [] if the ontology is consistent.
1028
+ * Returns [] if `opts.maxJustifications === 0`.
1029
+ * When the cost cap (`laconicMaxAxioms` / `laconicMaxParts`) is exceeded for
1030
+ * a justification, that entry has `laconic.skipped === true`.
1031
+ */
1032
+ explainInconsistencyLaconic(store, opts) {
1033
+ const maxAxioms = opts?.laconicMaxAxioms ?? 20;
1034
+ const maxParts = opts?.laconicMaxParts ?? 40;
1035
+ const result = this._queue.then(async () => {
1036
+ // Get MIPS justifications (reuse explainInconsistency logic inline)
1037
+ const allBase = store.getQuads(null, null, null, null).filter(q => {
1038
+ const g = q.graph.value;
1039
+ return g !== INFERRED_GRAPH_IRI && g !== HYPOTHETICAL_IRI;
1040
+ });
1041
+ const fingerprint = computeStoreFingerprint(store.getQuads(null, null, null, null));
1042
+ let consistent;
1043
+ if (this._consistencyCache?.hash === fingerprint) {
1044
+ consistent = this._consistencyCache.result;
1045
+ }
1046
+ else {
1047
+ consistent = !(await this._checkInconsistencyDirect(allBase));
1048
+ this._consistencyCache = { hash: fingerprint, result: consistent };
1049
+ }
1050
+ this._classifyCache = null;
1051
+ this._materializeCache = null;
1052
+ this._classifyPropertiesCache = null;
1053
+ if (consistent)
1054
+ return [];
1055
+ const maxJustifications = opts?.maxJustifications ?? 1;
1056
+ if (maxJustifications === 0)
1057
+ return [];
1058
+ const allCandidates = store.getQuads(null, null, null, null).filter(q => {
1059
+ const g = q.graph.value;
1060
+ if (g === INFERRED_GRAPH_IRI || g === HYPOTHETICAL_IRI)
1061
+ return false;
1062
+ if (opts?.axiomFilter && !opts.axiomFilter(q))
1063
+ return false;
1064
+ return true;
1065
+ });
1066
+ this._classifyCache = null;
1067
+ this._materializeCache = null;
1068
+ this._classifyPropertiesCache = null;
1069
+ this._consistencyCache = null;
1070
+ if (!(await this._checkInconsistencyDirect(allCandidates)))
1071
+ return [];
1072
+ // Find MIPS (same inline BlackBox as explainInconsistency)
1073
+ const findOne = async (cands) => {
1074
+ let w = [...cands];
1075
+ let changed = true;
1076
+ while (changed && w.length > 1) {
1077
+ changed = false;
1078
+ const mid = Math.floor(w.length / 2);
1079
+ const [fh, sh] = [w.slice(0, mid), w.slice(mid)];
1080
+ if (await this._checkInconsistencyDirect(fh)) {
1081
+ w = fh;
1082
+ changed = true;
1083
+ continue;
1084
+ }
1085
+ if (await this._checkInconsistencyDirect(sh)) {
1086
+ w = sh;
1087
+ changed = true;
1088
+ continue;
1089
+ }
1090
+ break;
1091
+ }
1092
+ let i = 0;
1093
+ while (i < w.length) {
1094
+ if (w.length === 1)
1095
+ break;
1096
+ const without = [...w.slice(0, i), ...w.slice(i + 1)];
1097
+ if (await this._checkInconsistencyDirect(without)) {
1098
+ w = without;
1099
+ }
1100
+ else {
1101
+ i++;
1102
+ }
1103
+ }
1104
+ return w;
1105
+ };
1106
+ const justifications = [];
1107
+ const j1 = await findOne(allCandidates);
1108
+ if (!j1 || j1.length === 0)
1109
+ return [];
1110
+ justifications.push(j1);
1111
+ if (maxJustifications > 1) {
1112
+ const hsQueue = [
1113
+ { excluded: new Set(), justification: j1 },
1114
+ ];
1115
+ const explored = new Set();
1116
+ const keyOf = (q) => `${q.subject.value}\0${q.predicate.value}\0${q.object.value}`;
1117
+ while (hsQueue.length > 0 && justifications.length < maxJustifications) {
1118
+ const { excluded, justification: curJ } = hsQueue.shift();
1119
+ const eKey = [...excluded].sort().join("|");
1120
+ if (explored.has(eKey))
1121
+ continue;
1122
+ explored.add(eKey);
1123
+ for (const ax of curJ) {
1124
+ const newExcl = new Set(excluded);
1125
+ newExcl.add(keyOf(ax));
1126
+ const nKey = [...newExcl].sort().join("|");
1127
+ if (explored.has(nKey))
1128
+ continue;
1129
+ const reduced = allCandidates.filter(q => !newExcl.has(keyOf(q)));
1130
+ if (!(await this._checkInconsistencyDirect(reduced)))
1131
+ continue;
1132
+ const jN = await findOne(reduced);
1133
+ if (!jN || jN.length === 0)
1134
+ continue;
1135
+ const jKey = jN.map(keyOf).sort().join("|");
1136
+ if (!justifications.some(j => j.map(keyOf).sort().join("|") === jKey)) {
1137
+ justifications.push(jN);
1138
+ if (justifications.length >= maxJustifications)
1139
+ break;
1140
+ hsQueue.push({ excluded: newExcl, justification: jN });
1141
+ }
1142
+ }
1143
+ }
1144
+ }
1145
+ // Post-process each justification with laconic
1146
+ const out = [];
1147
+ for (const j of justifications) {
1148
+ const { axioms, sourceQuads } = groupQuadsIntoAxioms(j, allBase);
1149
+ const totalParts = axioms.reduce((n, ax) => n + splitAxiom(ax).length, 0);
1150
+ if (axioms.length > maxAxioms || totalParts > maxParts) {
1151
+ out.push({
1152
+ justification: j,
1153
+ laconic: {
1154
+ parts: axioms.map(ax => ({
1155
+ quad: ax[0],
1156
+ sourceQuad: ax[0],
1157
+ isPartOf: false,
1158
+ })),
1159
+ sharpened: false,
1160
+ skipped: true,
1161
+ },
1162
+ });
1163
+ continue;
1164
+ }
1165
+ // Map split-part keys → original source quads so the oracle can
1166
+ // resolve any sub-part back to its parent axiom's full RDF encoding.
1167
+ const partKeyToSource = new Map();
1168
+ for (const ax of axioms) {
1169
+ const src = sourceQuads.get(axiomKey(ax)) ?? ax;
1170
+ for (const part of splitAxiom(ax)) {
1171
+ partKeyToSource.set(axiomKey(part), src);
1172
+ }
1173
+ // Also map the original axiom key itself
1174
+ partKeyToSource.set(axiomKey(ax), src);
1175
+ }
1176
+ const entails = async (parts) => {
1177
+ const quads = [];
1178
+ for (const p of parts) {
1179
+ const k = axiomKey(p);
1180
+ const src = partKeyToSource.get(k);
1181
+ quads.push(...(src ?? p));
1182
+ }
1183
+ return this._checkInconsistencyDirect(quads);
1184
+ };
1185
+ const { laconic, sources } = await computeLaconicAsync(axioms, entails);
1186
+ const sharpened = laconic.length !== axioms.length
1187
+ || laconic.some(p => !sourceQuads.has(axiomKey(p)));
1188
+ out.push({
1189
+ justification: j,
1190
+ laconic: {
1191
+ parts: laconic.map(part => {
1192
+ const source = sources.get(part) ?? part;
1193
+ return {
1194
+ quad: part[0],
1195
+ sourceQuad: source[0],
1196
+ isPartOf: axiomKey(part) !== axiomKey(source),
1197
+ };
1198
+ }),
1199
+ sharpened,
1200
+ skipped: false,
1201
+ },
1202
+ });
1203
+ }
1204
+ return out;
1205
+ });
1206
+ this._queue = result.then(() => { }, () => { });
1207
+ return result;
1208
+ }
1209
+ // -------------------------------------------------------------------------
1210
+ // explainEntailment()
1211
+ // -------------------------------------------------------------------------
1212
+ /** Explain why a triple (subjectIri, predicateIri, objectIri) is entailed
1213
+ * by the ontology, using entailment-as-unsatisfiability reduction.
1214
+ *
1215
+ * Returns an `EntailmentResult` containing:
1216
+ * - `isEntailed` — `true/false/null` (null when ontology is inconsistent)
1217
+ * - `justifications` — minimal justifications (each is a Quad[] subset)
1218
+ * - `ontologyInconsistent` / `vacuous` — special-case flags
1219
+ *
1220
+ * Supports rdfs:subClassOf and rdf:type shapes. Returns unsupported for
1221
+ * other predicates (isEntailed reflects only asserted facts).
1222
+ */
1223
+ explainEntailment(store, subjectIri, predicateIri, objectIri, opts) {
1224
+ const maxJustifications = opts?.maxJustifications ?? 1;
1225
+ const objectIsClassLike = opts?.objectIsClassLike ?? true;
1226
+ const result = this._queue.then(async () => {
1227
+ const allBase = store.getQuads(null, null, null, null).filter(q => {
1228
+ const g = q.graph.value;
1229
+ return g !== INFERRED_GRAPH_IRI && g !== HYPOTHETICAL_IRI;
1230
+ });
1231
+ // C1: ontology must be consistent for the reduction to be sound
1232
+ if (await this._checkInconsistencyDirect(allBase)) {
1233
+ return {
1234
+ isEntailed: null,
1235
+ justifications: [],
1236
+ ontologyInconsistent: true,
1237
+ reason: "Ontology is already inconsistent; entailment is vacuous.",
1238
+ };
1239
+ }
1240
+ // Asserted-triple short circuit
1241
+ const asserted = allBase.some(q => q.subject.value === subjectIri &&
1242
+ q.predicate.value === predicateIri &&
1243
+ q.object.value === objectIri);
1244
+ const probeId = `probe_${++this._entailmentProbeCounter}`;
1245
+ const probe = buildEntailmentProbe(subjectIri, predicateIri, objectIri, objectIsClassLike, probeId);
1246
+ if (probe.kind === "unsupported") {
1247
+ return { isEntailed: asserted, justifications: [] };
1248
+ }
1249
+ // O ∪ ¬α — test entailment via the consistency oracle
1250
+ const withProbe = [...allBase, ...probe.probeQuads];
1251
+ const entailed = await this._checkInconsistencyDirect(withProbe);
1252
+ if (!entailed) {
1253
+ return { isEntailed: false, justifications: [] };
1254
+ }
1255
+ // C2: vacuous-truth detection for subClassOf — an unsatisfiable class
1256
+ // is a subclass of everything (vacuous truth, not a useful justification).
1257
+ if (probe.kind === "subClassOf") {
1258
+ const { tripleBuffer, strTableBuffer } = encodeToBuffers(allBase);
1259
+ await this._callDirect("loadTripleBuffer", [tripleBuffer, strTableBuffer, false], [tripleBuffer, strTableBuffer]);
1260
+ await this._callDirect("classification", []);
1261
+ const sat = await this._isSatisfiableClassDirect(subjectIri);
1262
+ if (!sat) {
1263
+ return {
1264
+ isEntailed: true,
1265
+ justifications: [],
1266
+ vacuous: true,
1267
+ reason: "Subject class is unsatisfiable; it is a subclass of anything (vacuous truth).",
1268
+ };
1269
+ }
1270
+ }
1271
+ if (maxJustifications === 0) {
1272
+ return { isEntailed: true, justifications: [] };
1273
+ }
1274
+ // Invalidate caches — sub-calls modify WASM state
1275
+ this._classifyCache = null;
1276
+ this._materializeCache = null;
1277
+ this._classifyPropertiesCache = null;
1278
+ this._consistencyCache = null;
1279
+ // Partition into justification candidates vs background declarations.
1280
+ // Background (rdf:type owl:Class etc.) must be passed to every oracle
1281
+ // call so Konclude recognises classes/properties, but is never returned
1282
+ // as part of a justification.
1283
+ const ontologyCandidates = [];
1284
+ const background = [];
1285
+ for (const q of allBase) {
1286
+ if (this._isBuiltInDeclaration(q)) {
1287
+ background.push(q);
1288
+ }
1289
+ else {
1290
+ ontologyCandidates.push(q);
1291
+ }
1292
+ }
1293
+ const filteredCandidates = opts?.axiomFilter
1294
+ ? ontologyCandidates.filter(q => opts.axiomFilter(q))
1295
+ : ontologyCandidates;
1296
+ const keyOf = (q) => `${q.subject.value}\0${q.predicate.value}\0${q.object.value}`;
1297
+ const stripProbe = (j) => j.filter(q => !probe.probeKeys.has(probeTripleKey(q.subject.value, q.predicate.value, q.object.value)));
1298
+ const justifications = [];
1299
+ // BlackBox find one justification — probe quads are PINNED: only ontology
1300
+ // candidates participate in bisection/deletion, probe quads are always
1301
+ // re-added before each oracle call so they are never lost.
1302
+ const findOne = async (ontCandidates) => {
1303
+ let working = [...ontCandidates];
1304
+ // Shrink phase: binary partition (ontology candidates only)
1305
+ let changed = true;
1306
+ while (changed && working.length > 1) {
1307
+ changed = false;
1308
+ const mid = Math.floor(working.length / 2);
1309
+ const fh = working.slice(0, mid);
1310
+ const sh = working.slice(mid);
1311
+ if (await this._checkInconsistencyDirect([...fh, ...probe.probeQuads, ...background])) {
1312
+ working = fh;
1313
+ changed = true;
1314
+ continue;
1315
+ }
1316
+ if (await this._checkInconsistencyDirect([...sh, ...probe.probeQuads, ...background])) {
1317
+ working = sh;
1318
+ changed = true;
1319
+ continue;
1320
+ }
1321
+ break;
1322
+ }
1323
+ // Deletion pass: remove each ontology candidate that is not individually required
1324
+ let i = 0;
1325
+ while (i < working.length) {
1326
+ if (working.length === 0)
1327
+ break;
1328
+ const without = [...working.slice(0, i), ...working.slice(i + 1)];
1329
+ if (await this._checkInconsistencyDirect([...without, ...probe.probeQuads, ...background])) {
1330
+ working = without;
1331
+ }
1332
+ else {
1333
+ i++;
1334
+ }
1335
+ }
1336
+ return working.length > 0 ? [...working, ...probe.probeQuads] : null;
1337
+ };
1338
+ // Verify full candidate set + probe is inconsistent
1339
+ if (!(await this._checkInconsistencyDirect([...filteredCandidates, ...probe.probeQuads, ...background]))) {
1340
+ return { isEntailed: true, justifications: [] };
1341
+ }
1342
+ const j1 = await findOne(filteredCandidates);
1343
+ if (!j1 || j1.length === 0) {
1344
+ return { isEntailed: true, justifications: [] };
1345
+ }
1346
+ justifications.push(stripProbe(j1));
1347
+ // HSDAG for additional justifications
1348
+ if (maxJustifications > 1) {
1349
+ const hsQueue = [
1350
+ { excluded: new Set(), justification: j1 },
1351
+ ];
1352
+ const explored = new Set();
1353
+ while (hsQueue.length > 0 && justifications.length < maxJustifications) {
1354
+ const { excluded, justification: curJ } = hsQueue.shift();
1355
+ const eKey = [...excluded].sort().join("|");
1356
+ if (explored.has(eKey))
1357
+ continue;
1358
+ explored.add(eKey);
1359
+ for (const ax of curJ) {
1360
+ // Never exclude probe quads from the hitting set
1361
+ if (probe.probeKeys.has(probeTripleKey(ax.subject.value, ax.predicate.value, ax.object.value)))
1362
+ continue;
1363
+ const newExcl = new Set(excluded);
1364
+ newExcl.add(keyOf(ax));
1365
+ const nKey = [...newExcl].sort().join("|");
1366
+ if (explored.has(nKey))
1367
+ continue;
1368
+ const reduced = filteredCandidates.filter(q => !newExcl.has(keyOf(q)));
1369
+ if (!(await this._checkInconsistencyDirect([...reduced, ...probe.probeQuads, ...background])))
1370
+ continue;
1371
+ const jNew = await findOne(reduced);
1372
+ if (!jNew || jNew.length === 0)
1373
+ continue;
1374
+ const jKey = stripProbe(jNew).map(keyOf).sort().join("|");
1375
+ if (!justifications.some(j => j.map(keyOf).sort().join("|") === jKey)) {
1376
+ justifications.push(stripProbe(jNew));
1377
+ if (justifications.length >= maxJustifications)
1378
+ break;
1379
+ hsQueue.push({ excluded: newExcl, justification: jNew });
1380
+ }
1381
+ }
1382
+ }
1383
+ }
1384
+ return { isEntailed: true, justifications };
1385
+ });
1386
+ this._queue = result.then(() => { }, () => { });
1387
+ return result;
1388
+ }
1389
+ // -------------------------------------------------------------------------
1006
1390
  // validate()
1007
1391
  // -------------------------------------------------------------------------
1008
1392
  /** Run a combined diagnostic: check consistency, find unsatisfiable classes,