eyeling 1.19.4 → 1.19.5
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/HANDBOOK.md +48 -89
- package/examples/fibonacci.n3 +2 -0
- package/examples/output/fibonacci.n3 +1 -0
- package/eyeling.js +49 -45
- package/lib/engine.js +49 -45
- package/package.json +1 -1
package/HANDBOOK.md
CHANGED
|
@@ -2955,104 +2955,50 @@ That is why the result is 100%.
|
|
|
2955
2955
|
|
|
2956
2956
|
## Appendix F — The ARC approach: Answer • Reason Why • Check
|
|
2957
2957
|
|
|
2958
|
-
A
|
|
2958
|
+
A simple way to write a good Eyeling program is to make it do three things in one file:
|
|
2959
2959
|
|
|
2960
|
-
>
|
|
2960
|
+
> give the answer, say why, and check that it really holds.
|
|
2961
2961
|
|
|
2962
|
-
|
|
2962
|
+
That is the ARC approach: **Answer • Reason Why • Check**.
|
|
2963
2963
|
|
|
2964
|
-
|
|
2964
|
+
The idea is not to make the program more grand or formal. It is to make it more useful. A bare result is often not enough. A reader also wants to see the small reason that matters, and to know that the program will fail loudly if an important assumption is wrong.
|
|
2965
2965
|
|
|
2966
|
-
|
|
2967
|
-
2. Logic
|
|
2968
|
-
3. A Question
|
|
2966
|
+
In Eyeling this style comes quite naturally. Facts hold the data. Rules derive the conclusion. `log:outputString` can turn the conclusion into readable output. And a rule that concludes `false` acts as a fuse: if a bad condition becomes provable, the run stops instead of quietly producing a misleading result.
|
|
2969
2967
|
|
|
2970
|
-
|
|
2968
|
+
### F.1 What the three parts mean
|
|
2971
2969
|
|
|
2972
|
-
|
|
2970
|
+
The **Answer** is the direct result. It should be short and easy to recognize. In many Eyeling files it is a final recommendation, a route, a computed value, a decision such as `allowed` or `blocked`, or a small report line emitted with `log:outputString`.
|
|
2973
2971
|
|
|
2974
|
-
|
|
2972
|
+
The **Reason Why** is the compact explanation. It is not hidden chain-of-thought and it does not need to be long. Usually it is just the witness, threshold, policy, path, or intermediate fact that made the answer follow. A good reason tells the reader what mattered.
|
|
2975
2973
|
|
|
2976
|
-
The **
|
|
2974
|
+
The **Check** is the part that keeps the program honest. It should do more than repeat the answer in different words. A good check tests something that could really fail: a structural invariant, a recomputed quantity, a boundary condition, or a rule that derives `false` when the answer would be inconsistent with the inputs.
|
|
2977
2975
|
|
|
2978
|
-
|
|
2976
|
+
A short way to remember ARC is this:
|
|
2979
2977
|
|
|
2980
|
-
|
|
2981
|
-
- the selected item
|
|
2982
|
-
- the computed value
|
|
2983
|
-
- the resulting classification
|
|
2978
|
+
> an answer tells you **what** happened, a reason tells you **why**, and a check tells you **whether you should trust it**.
|
|
2984
2979
|
|
|
2985
|
-
|
|
2980
|
+
### F.2 Why this fits Eyeling well
|
|
2986
2981
|
|
|
2987
|
-
|
|
2982
|
+
ARC is not an extra subsystem in Eyeling. It is mostly a good habit.
|
|
2988
2983
|
|
|
2989
|
-
|
|
2984
|
+
Eyeling already separates data from logic. It already lets you derive readable output instead of printing ad hoc text during proof search. And it already has a very strong notion of validation through inference fuses. So ARC is really just a clean way to organize an ordinary Eyeling file so that a human reader can see the result, the explanation, and the safety net together.
|
|
2990
2985
|
|
|
2991
|
-
This is
|
|
2986
|
+
This is especially useful for examples. A newcomer can run the file and see what it does. A maintainer can inspect the few rules that justify the result. And an external developer can tell whether the example merely prints something nice or actually checks itself.
|
|
2992
2987
|
|
|
2993
|
-
|
|
2994
|
-
- the governing rule or policy
|
|
2995
|
-
- the key intermediate facts
|
|
2996
|
-
- the condition that made the conclusion follow
|
|
2988
|
+
### F.3 A simple pattern to follow
|
|
2997
2989
|
|
|
2998
|
-
|
|
2990
|
+
A practical ARC-style Eyeling file often has four visible layers.
|
|
2999
2991
|
|
|
3000
|
-
|
|
2992
|
+
First come the **facts**: the input data, parameters, thresholds, policies, or known relationships. Then comes the **logic**: the rules that derive the internal conclusion. Then comes the **presentation**: rules that turn the result into `log:outputString` lines or other report facts. Finally come the **checks**: rules that validate the result or trigger `false` when an invariant is broken.
|
|
3001
2993
|
|
|
3002
|
-
|
|
2994
|
+
You do not have to separate these layers perfectly, but it helps a lot when the file reads in roughly that order.
|
|
3003
2995
|
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
In Eyeling, Checks are a natural fit for either:
|
|
3007
|
-
|
|
3008
|
-
- derived facts such as `:ok :signatureVerified true .`, or
|
|
3009
|
-
- inference fuses such as `{ ... } => false .` when a violation must stop execution.
|
|
3010
|
-
|
|
3011
|
-
This makes verification part of the program itself rather than something left to external commentary.
|
|
3012
|
-
|
|
3013
|
-
### F.2 Proof = Reason Why + Check
|
|
3014
|
-
|
|
3015
|
-
ARC summarizes its trust model as:
|
|
3016
|
-
|
|
3017
|
-
> Proof = Reason Why + Check
|
|
3018
|
-
|
|
3019
|
-
That is a practical notion of proof. The Reason Why explains the logic in human terms. The Check verifies that the critical conditions actually hold at runtime.
|
|
3020
|
-
|
|
3021
|
-
For many real workflows, that combination is more useful than a bare result: it is inspectable, repeatable, and suitable for automation.
|
|
3022
|
-
|
|
3023
|
-
### F.3 Why ARC fits Eyeling well
|
|
3024
|
-
|
|
3025
|
-
Eyeling already encourages the separation that ARC needs.
|
|
3026
|
-
|
|
3027
|
-
Rules derive facts. Facts can include output facts. Output is not printed eagerly during proof search; instead, `log:outputString` facts are collected from the final closure and rendered deterministically whenever they are present. This makes it natural to derive a structured Answer and Reason Why as part of the logic itself.
|
|
3028
|
-
|
|
3029
|
-
Checks also map well to Eyeling. A rule with conclusion `false` acts as an inference fuse: if its body becomes provable, execution stops with a hard failure. This is exactly the behavior we want for “must-hold” conditions.
|
|
3030
|
-
|
|
3031
|
-
So ARC in Eyeling is not an add-on. It is mostly a disciplined way of organizing what Eyeling already does well: derive conclusions, expose supporting facts, and enforce invariants.
|
|
3032
|
-
|
|
3033
|
-
### F.4 A practical pattern
|
|
3034
|
-
|
|
3035
|
-
A simple ARC-oriented Eyeling file often has four layers:
|
|
3036
|
-
|
|
3037
|
-
1. **Facts**
|
|
3038
|
-
Input data, parameters, policies, and known relationships.
|
|
3039
|
-
|
|
3040
|
-
2. **Logic**
|
|
3041
|
-
Rules that derive the program’s internal conclusions.
|
|
3042
|
-
|
|
3043
|
-
3. **Presentation**
|
|
3044
|
-
Rules that turn derived conclusions into `log:outputString` lines for the Answer and Reason Why.
|
|
3045
|
-
|
|
3046
|
-
4. **Verification**
|
|
3047
|
-
Rules that derive check facts or trigger inference fuses on violations.
|
|
3048
|
-
|
|
3049
|
-
A useful habit is to keep these layers visually separate in the file.
|
|
3050
|
-
|
|
3051
|
-
### F.5 A tiny template
|
|
2996
|
+
### F.4 A tiny template
|
|
3052
2997
|
|
|
3053
2998
|
```n3
|
|
3054
2999
|
@prefix : <http://example.org/> .
|
|
3055
3000
|
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
3001
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
3056
3002
|
|
|
3057
3003
|
# Facts
|
|
3058
3004
|
:case :input 42 .
|
|
@@ -3078,25 +3024,38 @@ A useful habit is to keep these layers visually separate in the file.
|
|
|
3078
3024
|
=> false .
|
|
3079
3025
|
```
|
|
3080
3026
|
|
|
3081
|
-
The exact
|
|
3027
|
+
The exact wording can vary. The important thing is the shape: derive the result, make the key reason visible, and include at least one check that could fail for a real reason.
|
|
3028
|
+
|
|
3029
|
+
### F.5 What a good check looks like
|
|
3030
|
+
|
|
3031
|
+
A good check is not a decorative `:ok true` line. It should add real confidence.
|
|
3032
|
+
|
|
3033
|
+
Sometimes that means recomputing a quantity from another angle. Sometimes it means checking a witness path instead of only the summary result. Sometimes it means making sure a threshold really was crossed, or that a list or graph has the shape the rest of the program assumes. And sometimes the right check is simply an inference fuse that says: if this contradiction appears, stop.
|
|
3034
|
+
|
|
3035
|
+
The point is not to make checks large. The point is to make them real.
|
|
3036
|
+
|
|
3037
|
+
### F.6 Examples in `examples/` that read well in ARC style
|
|
3038
|
+
|
|
3039
|
+
The following examples are especially good places to see this style in practice.
|
|
3040
|
+
|
|
3041
|
+
- [`examples/delfour.n3`](examples/delfour.n3) — privacy-preserving shopping assistance with a concrete recommendation, an explanation, and policy checks. Expected output: [`examples/output/delfour.n3`](examples/output/delfour.n3)
|
|
3042
|
+
- [`examples/control-system.n3`](examples/control-system.n3) — derives actuator decisions, explains the control basis, and checks the result. Expected output: [`examples/output/control-system.n3`](examples/output/control-system.n3)
|
|
3043
|
+
- [`examples/deep-taxonomy-100000.n3`](examples/deep-taxonomy-100000.n3) — a deep classification stress test whose answer is whether the final goal class is reached. Expected output: [`examples/output/deep-taxonomy-100000.n3`](examples/output/deep-taxonomy-100000.n3)
|
|
3044
|
+
- [`examples/gps.n3`](examples/gps.n3) — route planning with readable route output. Expected output: [`examples/output/gps.n3`](examples/output/gps.n3)
|
|
3045
|
+
- [`examples/sudoku.n3`](examples/sudoku.n3) — solver output plus legality and consistency checks. Expected output: [`examples/output/sudoku.n3`](examples/output/sudoku.n3)
|
|
3082
3046
|
|
|
3083
|
-
### F.
|
|
3047
|
+
### F.7 How to read an ARC-style example
|
|
3084
3048
|
|
|
3085
|
-
|
|
3049
|
+
A good way to read one of these files is to start with the question in the comments or input facts. Then find the part that gives the answer. Then trace the few rules that explain why that answer follows. Finally, look for the checks: the validation facts, the recomputation, or the `=> false` fuse that would stop the run if something important were wrong.
|
|
3086
3050
|
|
|
3087
|
-
|
|
3088
|
-
- replacing checks with prose
|
|
3089
|
-
- hiding the important assumptions
|
|
3090
|
-
- relying on “trust me” comments outside the executable artifact
|
|
3051
|
+
That reading order keeps the example grounded in observable behavior rather than in source code alone.
|
|
3091
3052
|
|
|
3092
|
-
|
|
3053
|
+
### F.8 What ARC is not
|
|
3093
3054
|
|
|
3094
|
-
|
|
3055
|
+
ARC does not mean wrapping every file in ceremony. It does not mean long prose explanations. It does not mean hiding important assumptions in comments while the executable part stays thin. And it does not mean replacing checks with a confident tone.
|
|
3095
3056
|
|
|
3096
|
-
|
|
3057
|
+
A file really follows ARC only when the answer, the explanation, and the validation all live in the program itself.
|
|
3097
3058
|
|
|
3098
|
-
|
|
3099
|
-
- **Reason Why** for the key supporting explanation
|
|
3100
|
-
- **Check** for invariants and fail-loud validation
|
|
3059
|
+
### F.9 Why this style is worth using
|
|
3101
3060
|
|
|
3102
|
-
This
|
|
3061
|
+
This style is worth using because it makes an Eyeling file easier to run, easier to inspect, and easier to trust. The result is visible. The key reason is visible. The check is visible. That makes examples better teaching material, makes policy or computation examples easier to audit, and makes the whole file more reusable as a small reasoning artifact instead of an opaque session transcript.
|
package/examples/fibonacci.n3
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
10 :fibonacci ?F10.
|
|
24
24
|
100 :fibonacci ?F100.
|
|
25
25
|
1000 :fibonacci ?F1000.
|
|
26
|
+
10000 :fibonacci ?F10000.
|
|
26
27
|
} => {
|
|
27
28
|
:test :is {
|
|
28
29
|
0 :fibonacci ?F0.
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
10 :fibonacci ?F10.
|
|
31
32
|
100 :fibonacci ?F100.
|
|
32
33
|
1000 :fibonacci ?F1000.
|
|
34
|
+
10000 :fibonacci ?F10000.
|
|
33
35
|
}.
|
|
34
36
|
}.
|
|
35
37
|
|
|
@@ -6,4 +6,5 @@
|
|
|
6
6
|
10 :fibonacci 55 .
|
|
7
7
|
100 :fibonacci 354224848179261915075 .
|
|
8
8
|
1000 :fibonacci 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875 .
|
|
9
|
+
10000 :fibonacci 33644764876431783266621612005107543310302148460680063906564769974680081442166662368155595513633734025582065332680836159373734790483865268263040892463056431887354544369559827491606602099884183933864652731300088830269235673613135117579297437854413752130520504347701602264758318906527890855154366159582987279682987510631200575428783453215515103870818298969791613127856265033195487140214287532698187962046936097879900350962302291026368131493195275630227837628441540360584402572114334961180023091208287046088923962328835461505776583271252546093591128203925285393434620904245248929403901706233888991085841065183173360437470737908552631764325733993712871937587746897479926305837065742830161637408969178426378624212835258112820516370298089332099905707920064367426202389783111470054074998459250360633560933883831923386783056136435351892133279732908133732642652633989763922723407882928177953580570993691049175470808931841056146322338217465637321248226383092103297701648054726243842374862411453093812206564914032751086643394517512161526545361333111314042436854805106765843493523836959653428071768775328348234345557366719731392746273629108210679280784718035329131176778924659089938635459327894523777674406192240337638674004021330343297496902028328145933418826817683893072003634795623117103101291953169794607632737589253530772552375943788434504067715555779056450443016640119462580972216729758615026968443146952034614932291105970676243268515992834709891284706740862008587135016260312071903172086094081298321581077282076353186624611278245537208532365305775956430072517744315051539600905168603220349163222640885248852433158051534849622434848299380905070483482449327453732624567755879089187190803662058009594743150052402532709746995318770724376825907419939632265984147498193609285223945039707165443156421328157688908058783183404917434556270520223564846495196112460268313970975069382648706613264507665074611512677522748621598642530711298441182622661057163515069260029861704945425047491378115154139941550671256271197133252763631939606902895650288268608362241082050562430701794976171121233066073310059947366875 .
|
|
9
10
|
} .
|
package/eyeling.js
CHANGED
|
@@ -8133,6 +8133,18 @@ function __printTriggeredFuse(rule, prefixes, subst /* optional */, extraNote /*
|
|
|
8133
8133
|
}
|
|
8134
8134
|
}
|
|
8135
8135
|
|
|
8136
|
+
function __exitReasoning(code, message /* optional */) {
|
|
8137
|
+
if (typeof process !== 'undefined' && process && typeof process.exit === 'function') {
|
|
8138
|
+
process.exit(code);
|
|
8139
|
+
return;
|
|
8140
|
+
}
|
|
8141
|
+
|
|
8142
|
+
const err = new Error(message || `Process exited with code ${code}`);
|
|
8143
|
+
err.__eyelingExit = true;
|
|
8144
|
+
err.code = code;
|
|
8145
|
+
throw err;
|
|
8146
|
+
}
|
|
8147
|
+
|
|
8136
8148
|
function forwardChain(facts, forwardRules, backRules, onDerived /* optional */, opts = {}) {
|
|
8137
8149
|
enterReasoningRun();
|
|
8138
8150
|
try {
|
|
@@ -8272,7 +8284,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8272
8284
|
// Allow dynamic fuses: ... => ?X. where ?X becomes false
|
|
8273
8285
|
if (dynTerm instanceof Literal && dynTerm.value === 'false') {
|
|
8274
8286
|
__printTriggeredFuse(r, opts && opts.prefixes, s, 'Dynamic head resolved to false.');
|
|
8275
|
-
|
|
8287
|
+
__exitReasoning(2, 'Inference fuse triggered.');
|
|
8276
8288
|
}
|
|
8277
8289
|
|
|
8278
8290
|
const dynTriples = __graphTriplesOrTrue(dynTerm);
|
|
@@ -8300,10 +8312,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8300
8312
|
const objIsGraph = obj instanceof GraphTerm;
|
|
8301
8313
|
const subjIsTrue = subj instanceof Literal && subj.value === 'true';
|
|
8302
8314
|
const objIsTrue = obj instanceof Literal && obj.value === 'true';
|
|
8315
|
+
const objIsFalse = obj instanceof Literal && obj.value === 'false';
|
|
8303
8316
|
|
|
8304
8317
|
const isFwRuleTriple =
|
|
8305
|
-
isLogImplies(instantiated.p) &&
|
|
8306
|
-
((subjIsGraph && objIsGraph) || (subjIsTrue && objIsGraph) || (subjIsGraph && objIsTrue));
|
|
8318
|
+
isLogImplies(instantiated.p) && (subjIsGraph || subjIsTrue) && (objIsGraph || objIsTrue || objIsFalse);
|
|
8307
8319
|
|
|
8308
8320
|
const isBwRuleTriple =
|
|
8309
8321
|
isLogImpliedBy(instantiated.p) &&
|
|
@@ -8318,47 +8330,39 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8318
8330
|
changedHere = true;
|
|
8319
8331
|
}
|
|
8320
8332
|
|
|
8321
|
-
// Promote rule-producing triples to live rules, treating literal true as {}
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
newRule.__dynamicConclusionTerm || null,
|
|
8355
|
-
);
|
|
8356
|
-
if (!backRules.__ruleKeySet.has(key)) {
|
|
8357
|
-
backRules.__ruleKeySet.add(key);
|
|
8358
|
-
backRules.push(newRule);
|
|
8359
|
-
indexBackRule(backRules, newRule);
|
|
8360
|
-
rulesChanged = true;
|
|
8361
|
-
}
|
|
8333
|
+
// Promote rule-producing triples to live rules, treating literal true as {}
|
|
8334
|
+
// and literal false as a fuse head.
|
|
8335
|
+
if (isFwRuleTriple) {
|
|
8336
|
+
const newRule = __makeRuleFromTerms(subj, obj, true);
|
|
8337
|
+
__prepareForwardRule(newRule);
|
|
8338
|
+
|
|
8339
|
+
const key = __ruleKey(
|
|
8340
|
+
newRule.isForward,
|
|
8341
|
+
newRule.isFuse,
|
|
8342
|
+
newRule.premise,
|
|
8343
|
+
newRule.conclusion,
|
|
8344
|
+
newRule.__dynamicConclusionTerm || null,
|
|
8345
|
+
);
|
|
8346
|
+
if (!forwardRules.__ruleKeySet.has(key)) {
|
|
8347
|
+
forwardRules.__ruleKeySet.add(key);
|
|
8348
|
+
forwardRules.push(newRule);
|
|
8349
|
+
rulesChanged = true;
|
|
8350
|
+
}
|
|
8351
|
+
} else if (isBwRuleTriple) {
|
|
8352
|
+
const newRule = __makeRuleFromTerms(subj, obj, false);
|
|
8353
|
+
|
|
8354
|
+
const key = __ruleKey(
|
|
8355
|
+
newRule.isForward,
|
|
8356
|
+
newRule.isFuse,
|
|
8357
|
+
newRule.premise,
|
|
8358
|
+
newRule.conclusion,
|
|
8359
|
+
newRule.__dynamicConclusionTerm || null,
|
|
8360
|
+
);
|
|
8361
|
+
if (!backRules.__ruleKeySet.has(key)) {
|
|
8362
|
+
backRules.__ruleKeySet.add(key);
|
|
8363
|
+
backRules.push(newRule);
|
|
8364
|
+
indexBackRule(backRules, newRule);
|
|
8365
|
+
rulesChanged = true;
|
|
8362
8366
|
}
|
|
8363
8367
|
}
|
|
8364
8368
|
|
|
@@ -8441,7 +8445,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8441
8445
|
// Inference fuse
|
|
8442
8446
|
if (r.isFuse && sols.length) {
|
|
8443
8447
|
__printTriggeredFuse(r, opts && opts.prefixes, sols[0]);
|
|
8444
|
-
|
|
8448
|
+
__exitReasoning(2, 'Inference fuse triggered.');
|
|
8445
8449
|
}
|
|
8446
8450
|
|
|
8447
8451
|
for (const s of sols) {
|
package/lib/engine.js
CHANGED
|
@@ -2651,6 +2651,18 @@ function __printTriggeredFuse(rule, prefixes, subst /* optional */, extraNote /*
|
|
|
2651
2651
|
}
|
|
2652
2652
|
}
|
|
2653
2653
|
|
|
2654
|
+
function __exitReasoning(code, message /* optional */) {
|
|
2655
|
+
if (typeof process !== 'undefined' && process && typeof process.exit === 'function') {
|
|
2656
|
+
process.exit(code);
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
const err = new Error(message || `Process exited with code ${code}`);
|
|
2661
|
+
err.__eyelingExit = true;
|
|
2662
|
+
err.code = code;
|
|
2663
|
+
throw err;
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2654
2666
|
function forwardChain(facts, forwardRules, backRules, onDerived /* optional */, opts = {}) {
|
|
2655
2667
|
enterReasoningRun();
|
|
2656
2668
|
try {
|
|
@@ -2790,7 +2802,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
2790
2802
|
// Allow dynamic fuses: ... => ?X. where ?X becomes false
|
|
2791
2803
|
if (dynTerm instanceof Literal && dynTerm.value === 'false') {
|
|
2792
2804
|
__printTriggeredFuse(r, opts && opts.prefixes, s, 'Dynamic head resolved to false.');
|
|
2793
|
-
|
|
2805
|
+
__exitReasoning(2, 'Inference fuse triggered.');
|
|
2794
2806
|
}
|
|
2795
2807
|
|
|
2796
2808
|
const dynTriples = __graphTriplesOrTrue(dynTerm);
|
|
@@ -2818,10 +2830,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
2818
2830
|
const objIsGraph = obj instanceof GraphTerm;
|
|
2819
2831
|
const subjIsTrue = subj instanceof Literal && subj.value === 'true';
|
|
2820
2832
|
const objIsTrue = obj instanceof Literal && obj.value === 'true';
|
|
2833
|
+
const objIsFalse = obj instanceof Literal && obj.value === 'false';
|
|
2821
2834
|
|
|
2822
2835
|
const isFwRuleTriple =
|
|
2823
|
-
isLogImplies(instantiated.p) &&
|
|
2824
|
-
((subjIsGraph && objIsGraph) || (subjIsTrue && objIsGraph) || (subjIsGraph && objIsTrue));
|
|
2836
|
+
isLogImplies(instantiated.p) && (subjIsGraph || subjIsTrue) && (objIsGraph || objIsTrue || objIsFalse);
|
|
2825
2837
|
|
|
2826
2838
|
const isBwRuleTriple =
|
|
2827
2839
|
isLogImpliedBy(instantiated.p) &&
|
|
@@ -2836,47 +2848,39 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
2836
2848
|
changedHere = true;
|
|
2837
2849
|
}
|
|
2838
2850
|
|
|
2839
|
-
// Promote rule-producing triples to live rules, treating literal true as {}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
newRule.__dynamicConclusionTerm || null,
|
|
2873
|
-
);
|
|
2874
|
-
if (!backRules.__ruleKeySet.has(key)) {
|
|
2875
|
-
backRules.__ruleKeySet.add(key);
|
|
2876
|
-
backRules.push(newRule);
|
|
2877
|
-
indexBackRule(backRules, newRule);
|
|
2878
|
-
rulesChanged = true;
|
|
2879
|
-
}
|
|
2851
|
+
// Promote rule-producing triples to live rules, treating literal true as {}
|
|
2852
|
+
// and literal false as a fuse head.
|
|
2853
|
+
if (isFwRuleTriple) {
|
|
2854
|
+
const newRule = __makeRuleFromTerms(subj, obj, true);
|
|
2855
|
+
__prepareForwardRule(newRule);
|
|
2856
|
+
|
|
2857
|
+
const key = __ruleKey(
|
|
2858
|
+
newRule.isForward,
|
|
2859
|
+
newRule.isFuse,
|
|
2860
|
+
newRule.premise,
|
|
2861
|
+
newRule.conclusion,
|
|
2862
|
+
newRule.__dynamicConclusionTerm || null,
|
|
2863
|
+
);
|
|
2864
|
+
if (!forwardRules.__ruleKeySet.has(key)) {
|
|
2865
|
+
forwardRules.__ruleKeySet.add(key);
|
|
2866
|
+
forwardRules.push(newRule);
|
|
2867
|
+
rulesChanged = true;
|
|
2868
|
+
}
|
|
2869
|
+
} else if (isBwRuleTriple) {
|
|
2870
|
+
const newRule = __makeRuleFromTerms(subj, obj, false);
|
|
2871
|
+
|
|
2872
|
+
const key = __ruleKey(
|
|
2873
|
+
newRule.isForward,
|
|
2874
|
+
newRule.isFuse,
|
|
2875
|
+
newRule.premise,
|
|
2876
|
+
newRule.conclusion,
|
|
2877
|
+
newRule.__dynamicConclusionTerm || null,
|
|
2878
|
+
);
|
|
2879
|
+
if (!backRules.__ruleKeySet.has(key)) {
|
|
2880
|
+
backRules.__ruleKeySet.add(key);
|
|
2881
|
+
backRules.push(newRule);
|
|
2882
|
+
indexBackRule(backRules, newRule);
|
|
2883
|
+
rulesChanged = true;
|
|
2880
2884
|
}
|
|
2881
2885
|
}
|
|
2882
2886
|
|
|
@@ -2959,7 +2963,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
2959
2963
|
// Inference fuse
|
|
2960
2964
|
if (r.isFuse && sols.length) {
|
|
2961
2965
|
__printTriggeredFuse(r, opts && opts.prefixes, sols[0]);
|
|
2962
|
-
|
|
2966
|
+
__exitReasoning(2, 'Inference fuse triggered.');
|
|
2963
2967
|
}
|
|
2964
2968
|
|
|
2965
2969
|
for (const s of sols) {
|