eyeling 1.17.2 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HANDBOOK.md +162 -14
- package/examples/bmi.n3 +219 -0
- package/examples/output/bmi.n3 +20 -0
- package/examples/output/pn-junction-tunneling.n3 +23 -0
- package/examples/output/sudoku.n3 +42 -0
- package/examples/output/transistor-switch.n3 +24 -0
- package/examples/pn-junction-tunneling.n3 +227 -0
- package/examples/sudoku.n3 +257 -0
- package/examples/transistor-switch.n3 +299 -0
- package/eyeling.js +678 -2
- package/index.d.ts +21 -0
- package/index.js +13 -0
- package/lib/builtin-sudoku.js +465 -0
- package/lib/builtins.js +156 -0
- package/lib/cli.js +33 -2
- package/lib/engine.js +21 -0
- package/package.json +1 -1
- package/test/api.test.js +29 -0
package/HANDBOOK.md
CHANGED
|
@@ -1870,28 +1870,162 @@ That’s the whole engine in miniature: unify, compose substitutions, emit head
|
|
|
1870
1870
|
|
|
1871
1871
|
Eyeling is small, which makes it pleasant to extend — but there are a few invariants worth respecting.
|
|
1872
1872
|
|
|
1873
|
-
|
|
1873
|
+
The most important update is architectural: **you no longer need to patch `lib/builtins.js` just to add a project-specific builtin**. The preferred path is now to load a custom builtin module, either programmatically or from the CLI. Core builtins still live in `lib/builtins.js`, but user extensions can stay outside the engine.
|
|
1874
1874
|
|
|
1875
|
-
|
|
1875
|
+
### 16.1 The preferred path: custom builtin modules
|
|
1876
1876
|
|
|
1877
|
-
|
|
1878
|
-
- a test (0/1 solution)
|
|
1879
|
-
- functional (bind output)
|
|
1880
|
-
- generator (many solutions)
|
|
1881
|
-
- Return _deltas_ `{ varName: Term }`, not full substitutions.
|
|
1882
|
-
- Be cautious with fully-unbound cases: generators can explode the search space.
|
|
1883
|
-
- If you add a _new predicate_ (not just a new case inside an existing namespace), make sure it is recognized by `isBuiltinPred(...)`.
|
|
1877
|
+
Eyeling now exposes a small custom-builtin registry.
|
|
1884
1878
|
|
|
1885
|
-
|
|
1879
|
+
At runtime, builtin predicates can be added with:
|
|
1880
|
+
|
|
1881
|
+
- `registerBuiltin(iri, handler)`
|
|
1882
|
+
- `unregisterBuiltin(iri)`
|
|
1883
|
+
- `registerBuiltinModule(moduleExport, origin?)`
|
|
1884
|
+
- `loadBuiltinModule(specifier, { resolveFrom? })`
|
|
1885
|
+
- `listBuiltinIris()`
|
|
1886
|
+
|
|
1887
|
+
That means the extension story is:
|
|
1888
|
+
|
|
1889
|
+
- keep the engine’s shipped builtins in `lib/builtins.js`
|
|
1890
|
+
- keep your own application or domain builtins in a separate `.js` module
|
|
1891
|
+
- load that module with `--builtin` or from JavaScript
|
|
1892
|
+
|
|
1893
|
+
This is the safest way to extend Eyeling because it avoids forking the builtin dispatcher and keeps upgrades merge-friendly.
|
|
1894
|
+
|
|
1895
|
+
### 16.2 CLI loading: `--builtin`
|
|
1896
|
+
|
|
1897
|
+
The CLI accepts a repeatable `--builtin <module.js>` option:
|
|
1898
|
+
|
|
1899
|
+
```bash
|
|
1900
|
+
eyeling --builtin ./hello-builtin.js rules.n3
|
|
1901
|
+
```
|
|
1902
|
+
|
|
1903
|
+
You can pass it more than once:
|
|
1904
|
+
|
|
1905
|
+
```bash
|
|
1906
|
+
eyeling --builtin ./math-extra.js --builtin ./domain-rules.js input.n3
|
|
1907
|
+
```
|
|
1908
|
+
|
|
1909
|
+
Each module is loaded before reasoning starts. Paths are resolved from the current working directory.
|
|
1910
|
+
|
|
1911
|
+
The same capability is available through the npm wrapper:
|
|
1912
|
+
|
|
1913
|
+
```js
|
|
1914
|
+
const { reason } = require('eyeling');
|
|
1915
|
+
const out = reason({ builtinModules: ['./hello-builtin.js'] }, n3Text);
|
|
1916
|
+
```
|
|
1917
|
+
|
|
1918
|
+
### 16.3 What a builtin module may export
|
|
1919
|
+
|
|
1920
|
+
Eyeling accepts three module shapes.
|
|
1921
|
+
|
|
1922
|
+
#### A function export
|
|
1923
|
+
|
|
1924
|
+
```js
|
|
1925
|
+
module.exports = ({ registerBuiltin, internLiteral, unifyTerm, terms }) => {
|
|
1926
|
+
const { Var } = terms;
|
|
1927
|
+
|
|
1928
|
+
registerBuiltin('http://example.org/custom#hello', ({ goal, subst }) => {
|
|
1929
|
+
const lit = internLiteral('"world"');
|
|
1930
|
+
if (goal.o instanceof Var) {
|
|
1931
|
+
return [{ ...subst, [goal.o.name]: lit }];
|
|
1932
|
+
}
|
|
1933
|
+
const s2 = unifyTerm(goal.o, lit, subst);
|
|
1934
|
+
return s2 ? [s2] : [];
|
|
1935
|
+
});
|
|
1936
|
+
};
|
|
1937
|
+
```
|
|
1938
|
+
|
|
1939
|
+
#### An object with `register(api)`
|
|
1940
|
+
|
|
1941
|
+
```js
|
|
1942
|
+
module.exports = {
|
|
1943
|
+
register(api) {
|
|
1944
|
+
api.registerBuiltin('http://example.org/custom#ping', ({ subst }) => [subst]);
|
|
1945
|
+
},
|
|
1946
|
+
};
|
|
1947
|
+
```
|
|
1948
|
+
|
|
1949
|
+
#### A plain object mapping predicate IRIs to handlers
|
|
1950
|
+
|
|
1951
|
+
```js
|
|
1952
|
+
module.exports = {
|
|
1953
|
+
'http://example.org/custom#ok': ({ subst }) => [subst],
|
|
1954
|
+
};
|
|
1955
|
+
```
|
|
1956
|
+
|
|
1957
|
+
If none of those shapes match, Eyeling rejects the module with a descriptive error.
|
|
1958
|
+
|
|
1959
|
+
### 16.4 The handler contract
|
|
1960
|
+
|
|
1961
|
+
Builtin handlers are called with a context object like:
|
|
1962
|
+
|
|
1963
|
+
- `iri` — the predicate IRI string
|
|
1964
|
+
- `goal` — the current triple goal
|
|
1965
|
+
- `subst` — the current substitution
|
|
1966
|
+
- `facts`, `backRules`, `depth`, `varGen`, `maxResults`
|
|
1967
|
+
- `api` — the same registration/helper API used by modules
|
|
1968
|
+
|
|
1969
|
+
A handler returns **an array of substitutions**:
|
|
1970
|
+
|
|
1971
|
+
- `[]` means failure / no solutions
|
|
1972
|
+
- `[subst2]` means one successful continuation
|
|
1973
|
+
- multiple substitutions mean a generator builtin
|
|
1974
|
+
|
|
1975
|
+
In practice:
|
|
1976
|
+
|
|
1977
|
+
- Decide if your builtin is a test, a functional relation, or a generator.
|
|
1978
|
+
- Return substitutions (or substitution deltas merged into the current substitution), not printed output.
|
|
1979
|
+
- Be cautious with fully-unbound generators: they can explode the search space.
|
|
1980
|
+
- If a builtin needs inputs to be bound first, it is fine to fail early and let forward-rule proving retry later in the conjunction.
|
|
1981
|
+
|
|
1982
|
+
Custom builtin failures are wrapped so the predicate IRI appears in the thrown error message, which makes debugging much easier from the CLI.
|
|
1983
|
+
|
|
1984
|
+
### 16.5 The helper API exposed to builtin modules
|
|
1985
|
+
|
|
1986
|
+
Builtin modules do not need to import internal engine files directly. Eyeling passes a helper API into module registration, including:
|
|
1987
|
+
|
|
1988
|
+
- registration helpers: `registerBuiltin`, `unregisterBuiltin`, `listBuiltinIris`
|
|
1989
|
+
- term constructors via `terms` (`Literal`, `Iri`, `Var`, `Blank`, `ListTerm`, `GraphTerm`, `Triple`, `Rule`, ...)
|
|
1990
|
+
- literal/term helpers such as `internLiteral`, `internIri`, `literalParts`, `termToN3`, `termToJsString`
|
|
1991
|
+
- reasoning helpers such as `unifyTerm`, `applySubstTerm`, `applySubstTriple`, `proveGoals`
|
|
1992
|
+
- namespace constants via `ns`
|
|
1993
|
+
|
|
1994
|
+
That API keeps the extension boundary explicit: custom builtins get the operations they need without reaching into Eyeling’s private module graph.
|
|
1995
|
+
|
|
1996
|
+
### 16.6 A shipped example: the Sudoku builtin
|
|
1997
|
+
|
|
1998
|
+
The repository now ships a Sudoku builtin module (`lib/builtin-sudoku.js`) and a matching example program (`sudoku.n3`).
|
|
1999
|
+
|
|
2000
|
+
So this works out of the box:
|
|
2001
|
+
|
|
2002
|
+
```bash
|
|
2003
|
+
eyeling sudoku.n3
|
|
2004
|
+
```
|
|
2005
|
+
|
|
2006
|
+
That example is useful for two reasons:
|
|
2007
|
+
|
|
2008
|
+
- it shows a realistic domain-specific builtin implemented outside the core builtin switchboard
|
|
2009
|
+
- it demonstrates the intended deployment model for larger custom relations: keep the N3 logic in the `.n3` file, and keep specialized search/verification code in a loadable builtin module
|
|
2010
|
+
|
|
2011
|
+
### 16.7 When you should still edit `lib/builtins.js`
|
|
2012
|
+
|
|
2013
|
+
Editing `lib/builtins.js` is still reasonable when you are:
|
|
2014
|
+
|
|
2015
|
+
- adding or fixing a **core** Eyeling builtin
|
|
2016
|
+
- changing builtin behavior that should ship as part of Eyeling itself
|
|
2017
|
+
- modifying the builtin helper API that custom modules depend on
|
|
2018
|
+
|
|
2019
|
+
But if the builtin is project-specific, experimental, or domain-bound, prefer a custom module first.
|
|
2020
|
+
|
|
2021
|
+
A small architectural note: `lib/builtins.js` is still initialized by the engine via `makeBuiltins(deps)`. It receives hooks (unification, proving, deref, scoped-closure helpers, …) instead of importing the engine directly, which keeps the module graph acyclic and makes browser bundling easier.
|
|
1886
2022
|
|
|
1887
2023
|
If your builtin needs a stable view of the scoped closure, follow the scoped-builtin pattern:
|
|
1888
2024
|
|
|
1889
2025
|
- read from `facts.__scopedSnapshot`
|
|
1890
2026
|
- honor `facts.__scopedClosureLevel` and priority gating
|
|
1891
2027
|
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
### 16.2 Adding new term shapes
|
|
2028
|
+
### 16.8 Adding new term shapes
|
|
1895
2029
|
|
|
1896
2030
|
If you add a new Term subclass, you’ll likely need to touch:
|
|
1897
2031
|
|
|
@@ -1900,7 +2034,7 @@ If you add a new Term subclass, you’ll likely need to touch:
|
|
|
1900
2034
|
- variable collection for compaction (`gcCollectVarsInTerm`)
|
|
1901
2035
|
- groundness checks
|
|
1902
2036
|
|
|
1903
|
-
### 16.
|
|
2037
|
+
### 16.9 Parser extensions
|
|
1904
2038
|
|
|
1905
2039
|
If you extend parsing, preserve the Rule invariants:
|
|
1906
2040
|
|
|
@@ -1983,6 +2117,7 @@ Options:
|
|
|
1983
2117
|
|
|
1984
2118
|
```
|
|
1985
2119
|
-a, --ast Print parsed AST as JSON and exit.
|
|
2120
|
+
--builtin <module.js> Load a custom builtin module (repeatable).
|
|
1986
2121
|
-d, --deterministic-skolem Make log:skolem stable across reasoning runs.
|
|
1987
2122
|
-e, --enforce-https Rewrite http:// IRIs to https:// for log dereferencing builtins.
|
|
1988
2123
|
-h, --help Show this help and exit.
|
|
@@ -2037,10 +2172,23 @@ See also:
|
|
|
2037
2172
|
|
|
2038
2173
|
Eyeling supports a built-in “standard library” across namespaces like `log:`, `math:`, `string:`, `list:`, `time:`, `crypto:`.
|
|
2039
2174
|
|
|
2175
|
+
It also supports **custom builtin modules**.
|
|
2176
|
+
|
|
2177
|
+
- From the CLI: `eyeling --builtin ./my-builtins.js input.n3`
|
|
2178
|
+
- From JavaScript: `reason({ builtinModules: ['./my-builtins.js'] }, input)`
|
|
2179
|
+
- Programmatically in-process: `registerBuiltin(...)`, `registerBuiltinModule(...)`, `loadBuiltinModule(...)`
|
|
2180
|
+
|
|
2181
|
+
A concrete shipped example is the Sudoku builtin and the root-level `sudoku.n3` program:
|
|
2182
|
+
|
|
2183
|
+
```bash
|
|
2184
|
+
eyeling sudoku.n3
|
|
2185
|
+
```
|
|
2186
|
+
|
|
2040
2187
|
References:
|
|
2041
2188
|
|
|
2042
2189
|
- W3C N3 Built-ins overview: [https://w3c.github.io/N3/reports/20230703/builtins.html](https://w3c.github.io/N3/reports/20230703/builtins.html)
|
|
2043
2190
|
- Eyeling implementation details: [Chapter 11 — Built-ins as a standard library](#ch11)
|
|
2191
|
+
- Extension API and custom module loading: [Chapter 16 — Extending Eyeling (without breaking it)](#ch16)
|
|
2044
2192
|
- The shipped builtin catalogue: `eyeling-builtins.ttl` (in this repo)
|
|
2045
2193
|
|
|
2046
2194
|
If you are running untrusted inputs, consider `--super-restricted` to disable all builtins except implication.
|
package/examples/bmi.n3
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# BMI — ARC-style Body Mass Index example.
|
|
3
|
+
#
|
|
4
|
+
# This example turns a familiar health calculation into a small, inspectable
|
|
5
|
+
# ARC program. It normalizes either metric or US inputs, computes BMI, assigns
|
|
6
|
+
# a WHO adult category, and derives a healthy-range weight band for the given
|
|
7
|
+
# height. The report explains the result and includes independent checks for
|
|
8
|
+
# boundary handling and category behavior.
|
|
9
|
+
#
|
|
10
|
+
# For reproducibility and documentation only; not medical advice.
|
|
11
|
+
# ============================================================================
|
|
12
|
+
|
|
13
|
+
@prefix : <https://example.org/bmi#> .
|
|
14
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
15
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
16
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
|
|
17
|
+
|
|
18
|
+
# --------------
|
|
19
|
+
# Editable input
|
|
20
|
+
# --------------
|
|
21
|
+
|
|
22
|
+
:Input
|
|
23
|
+
:unitSystem "metric";
|
|
24
|
+
:weight 72.0;
|
|
25
|
+
:height 178.0.
|
|
26
|
+
|
|
27
|
+
# US alternative:
|
|
28
|
+
# :Input
|
|
29
|
+
# :unitSystem "us";
|
|
30
|
+
# :weight 158.73;
|
|
31
|
+
# :height 70.08.
|
|
32
|
+
|
|
33
|
+
# ---------------------------------
|
|
34
|
+
# Normalization and BMI calculation
|
|
35
|
+
# ---------------------------------
|
|
36
|
+
|
|
37
|
+
{ :Input :unitSystem "metric"; :weight ?W; :height ?H.
|
|
38
|
+
(?H 100.0) math:quotient ?M. }
|
|
39
|
+
=>
|
|
40
|
+
{ :Case :weightKg ?W; :heightM ?M.
|
|
41
|
+
:Reason :units "Inputs were already metric, so kilograms stay kilograms and centimeters are divided by 100 to obtain meters.". } .
|
|
42
|
+
|
|
43
|
+
{ :Input :unitSystem "us"; :weight ?W; :height ?H.
|
|
44
|
+
(?W 0.45359237) math:product ?Kg.
|
|
45
|
+
(?H 0.0254) math:product ?M. }
|
|
46
|
+
=>
|
|
47
|
+
{ :Case :weightKg ?Kg; :heightM ?M.
|
|
48
|
+
:Reason :units "US inputs were converted to SI units: pounds to kilograms and inches to meters.". } .
|
|
49
|
+
|
|
50
|
+
{ :Case :weightKg ?Kg; :heightM ?M.
|
|
51
|
+
(?M ?M) math:product ?M2.
|
|
52
|
+
(?Kg ?M2) math:quotient ?Bmi.
|
|
53
|
+
(?Bmi 100.0) math:product ?BmiX100.
|
|
54
|
+
?BmiX100 math:rounded ?BmiRoundedInt.
|
|
55
|
+
(?BmiRoundedInt 100.0) math:quotient ?BmiRounded.
|
|
56
|
+
(18.5 ?M2) math:product ?HealthyMin.
|
|
57
|
+
(24.9 ?M2) math:product ?HealthyMax.
|
|
58
|
+
(?HealthyMin 10.0) math:product ?MinX10.
|
|
59
|
+
(?HealthyMax 10.0) math:product ?MaxX10.
|
|
60
|
+
?MinX10 math:rounded ?MinRoundedInt.
|
|
61
|
+
?MaxX10 math:rounded ?MaxRoundedInt.
|
|
62
|
+
(?MinRoundedInt 10.0) math:quotient ?HealthyMinRounded.
|
|
63
|
+
(?MaxRoundedInt 10.0) math:quotient ?HealthyMaxRounded. }
|
|
64
|
+
=>
|
|
65
|
+
{ :Case :heightSquared ?M2;
|
|
66
|
+
:bmi ?Bmi;
|
|
67
|
+
:bmiRounded ?BmiRounded;
|
|
68
|
+
:healthyMinKg ?HealthyMin;
|
|
69
|
+
:healthyMaxKg ?HealthyMax;
|
|
70
|
+
:healthyMinKgRounded ?HealthyMinRounded;
|
|
71
|
+
:healthyMaxKgRounded ?HealthyMaxRounded. } .
|
|
72
|
+
|
|
73
|
+
# ------------------------------------------
|
|
74
|
+
# WHO adult categories (half-open intervals)
|
|
75
|
+
# ------------------------------------------
|
|
76
|
+
|
|
77
|
+
{ :Case :bmi ?Bmi. ?Bmi math:lessThan 18.5. }
|
|
78
|
+
=> { :Decision :category "Underweight". } .
|
|
79
|
+
|
|
80
|
+
{ :Case :bmi ?Bmi. ?Bmi math:notLessThan 18.5. ?Bmi math:lessThan 25.0. }
|
|
81
|
+
=> { :Decision :category "Normal". } .
|
|
82
|
+
|
|
83
|
+
{ :Case :bmi ?Bmi. ?Bmi math:notLessThan 25.0. ?Bmi math:lessThan 30.0. }
|
|
84
|
+
=> { :Decision :category "Overweight". } .
|
|
85
|
+
|
|
86
|
+
{ :Case :bmi ?Bmi. ?Bmi math:notLessThan 30.0. ?Bmi math:lessThan 35.0. }
|
|
87
|
+
=> { :Decision :category "Obesity I". } .
|
|
88
|
+
|
|
89
|
+
{ :Case :bmi ?Bmi. ?Bmi math:notLessThan 35.0. ?Bmi math:lessThan 40.0. }
|
|
90
|
+
=> { :Decision :category "Obesity II". } .
|
|
91
|
+
|
|
92
|
+
{ :Case :bmi ?Bmi. ?Bmi math:notLessThan 40.0. }
|
|
93
|
+
=> { :Decision :category "Obesity III". } .
|
|
94
|
+
|
|
95
|
+
# ---------------------
|
|
96
|
+
# Answer and reason why
|
|
97
|
+
# ---------------------
|
|
98
|
+
|
|
99
|
+
{ :Case :bmiRounded ?BmiRounded;
|
|
100
|
+
:healthyMinKgRounded ?HealthyMinRounded;
|
|
101
|
+
:healthyMaxKgRounded ?HealthyMaxRounded;
|
|
102
|
+
:heightM ?M.
|
|
103
|
+
:Decision :category ?Category.
|
|
104
|
+
(?M 100.0) math:product ?Cm.
|
|
105
|
+
?Cm math:rounded ?CmRounded. }
|
|
106
|
+
=>
|
|
107
|
+
{ :Answer :bmi ?BmiRounded;
|
|
108
|
+
:category ?Category;
|
|
109
|
+
:healthyMinKg ?HealthyMinRounded;
|
|
110
|
+
:healthyMaxKg ?HealthyMaxRounded;
|
|
111
|
+
:heightCm ?CmRounded. } .
|
|
112
|
+
|
|
113
|
+
{ :Case :weightKg ?Kg; :heightM ?M; :heightSquared ?M2; :bmiRounded ?BmiRounded.
|
|
114
|
+
:Reason :units ?Units.
|
|
115
|
+
:Decision :category ?Category. }
|
|
116
|
+
=>
|
|
117
|
+
{ :Reason :formula "BMI is defined as weight in kilograms divided by height in meters squared.";
|
|
118
|
+
:calculation "The normalized weight and height were used to compute BMI, then the result was mapped to the WHO adult category table.";
|
|
119
|
+
:categoryRule ?Category;
|
|
120
|
+
:unitsExplanation ?Units. } .
|
|
121
|
+
|
|
122
|
+
# ------------------
|
|
123
|
+
# Independent checks
|
|
124
|
+
# ------------------
|
|
125
|
+
|
|
126
|
+
{ :Case :weightKg ?Kg; :heightM ?M. ?Kg math:greaterThan 0. ?M math:greaterThan 0. }
|
|
127
|
+
=> { :Check :c1 "OK - the input was normalized into positive SI values.". } .
|
|
128
|
+
|
|
129
|
+
{ :Case :heightM ?M; :heightSquared ?M2. (?M ?M) math:product ?M2. }
|
|
130
|
+
=> { :Check :c2 "OK - height squared was reconstructed from the normalized height.". } .
|
|
131
|
+
|
|
132
|
+
{ :Case :weightKg ?Kg; :heightSquared ?M2; :bmi ?Bmi. (?Kg ?M2) math:quotient ?Bmi. }
|
|
133
|
+
=> { :Check :c3 "OK - the BMI value matches the BMI = kg / m² formula.". } .
|
|
134
|
+
|
|
135
|
+
{ 18.49 math:lessThan 18.5. }
|
|
136
|
+
=> { :Check :c4 "OK - a BMI of 18.49 stays below the normal-weight threshold.". } .
|
|
137
|
+
|
|
138
|
+
{ 18.5 math:notLessThan 18.5. 18.5 math:lessThan 25.0. }
|
|
139
|
+
=> { :Check :c5 "OK - the lower boundary is half-open: BMI 18.5 is classified as Normal.". } .
|
|
140
|
+
|
|
141
|
+
{ 25.0 math:notLessThan 25.0. 25.0 math:lessThan 30.0. }
|
|
142
|
+
=> { :Check :c6 "OK - BMI 25.0 starts the Overweight category.". } .
|
|
143
|
+
|
|
144
|
+
{ 30.0 math:notLessThan 30.0. 30.0 math:lessThan 35.0. }
|
|
145
|
+
=> { :Check :c7 "OK - BMI 30.0 starts the Obesity I category.". } .
|
|
146
|
+
|
|
147
|
+
{ 22.0 math:notLessThan 18.5. 22.0 math:lessThan 25.0.
|
|
148
|
+
27.0 math:notLessThan 25.0. 27.0 math:lessThan 30.0.
|
|
149
|
+
41.0 math:notLessThan 40.0. }
|
|
150
|
+
=> { :Check :c8 "OK - classification behavior is monotonic across representative BMI values.". } .
|
|
151
|
+
|
|
152
|
+
{ :Case :heightSquared ?M2; :healthyMinKg ?Min; :healthyMaxKg ?Max.
|
|
153
|
+
(18.5 ?M2) math:product ?Min.
|
|
154
|
+
(24.9 ?M2) math:product ?Max. }
|
|
155
|
+
=> { :Check :c9 "OK - the healthy-weight band was reconstructed from BMI 18.5 to 24.9 at the same height.". } .
|
|
156
|
+
|
|
157
|
+
# -----
|
|
158
|
+
# Fuses
|
|
159
|
+
# -----
|
|
160
|
+
|
|
161
|
+
{ :Decision :category ?Any.
|
|
162
|
+
1 log:notIncludes {
|
|
163
|
+
:Check :c1 ?C1.
|
|
164
|
+
:Check :c2 ?C2.
|
|
165
|
+
:Check :c3 ?C3.
|
|
166
|
+
:Check :c4 ?C4.
|
|
167
|
+
:Check :c5 ?C5.
|
|
168
|
+
:Check :c6 ?C6.
|
|
169
|
+
:Check :c7 ?C7.
|
|
170
|
+
:Check :c8 ?C8.
|
|
171
|
+
:Check :c9 ?C9.
|
|
172
|
+
}. }
|
|
173
|
+
=> false .
|
|
174
|
+
|
|
175
|
+
{ :Decision :category ?C1, ?C2.
|
|
176
|
+
?C1 log:notEqualTo ?C2. }
|
|
177
|
+
=> false .
|
|
178
|
+
|
|
179
|
+
# ---------------------------------------------
|
|
180
|
+
# ARC report (built from actual derived values)
|
|
181
|
+
# ---------------------------------------------
|
|
182
|
+
|
|
183
|
+
{ :Answer :bmi ?BmiRounded;
|
|
184
|
+
:category ?Category;
|
|
185
|
+
:healthyMinKg ?HealthyMinRounded;
|
|
186
|
+
:healthyMaxKg ?HealthyMaxRounded;
|
|
187
|
+
:heightCm ?CmRounded.
|
|
188
|
+
:Check :c1 ?C1.
|
|
189
|
+
:Check :c2 ?C2.
|
|
190
|
+
:Check :c3 ?C3.
|
|
191
|
+
:Check :c4 ?C4.
|
|
192
|
+
:Check :c5 ?C5.
|
|
193
|
+
:Check :c6 ?C6.
|
|
194
|
+
:Check :c7 ?C7.
|
|
195
|
+
:Check :c8 ?C8.
|
|
196
|
+
:Check :c9 ?C9.
|
|
197
|
+
(
|
|
198
|
+
"BMI — ARC-style Body Mass Index example\n\n"
|
|
199
|
+
"Answer\n"
|
|
200
|
+
"BMI = " ?BmiRounded "\n"
|
|
201
|
+
"Category = " ?Category "\n"
|
|
202
|
+
"At height " ?CmRounded " cm, a healthy-weight range is about " ?HealthyMinRounded "–" ?HealthyMaxRounded " kg (BMI 18.5–24.9).\n\n"
|
|
203
|
+
"Reason Why\n"
|
|
204
|
+
"BMI is defined as weight in kilograms divided by height in meters squared. "
|
|
205
|
+
"This program first normalizes the input to SI units, computes BMI, and then applies WHO adult categories as half-open intervals. "
|
|
206
|
+
"The healthy-weight band is the weight range at the same height that corresponds to BMI 18.5 through 24.9.\n\n"
|
|
207
|
+
"Check\n"
|
|
208
|
+
"C1 " ?C1 "\n"
|
|
209
|
+
"C2 " ?C2 "\n"
|
|
210
|
+
"C3 " ?C3 "\n"
|
|
211
|
+
"C4 " ?C4 "\n"
|
|
212
|
+
"C5 " ?C5 "\n"
|
|
213
|
+
"C6 " ?C6 "\n"
|
|
214
|
+
"C7 " ?C7 "\n"
|
|
215
|
+
"C8 " ?C8 "\n"
|
|
216
|
+
"C9 " ?C9 "\n"
|
|
217
|
+
) string:concatenation ?Block. }
|
|
218
|
+
=>
|
|
219
|
+
{ :report log:outputString ?Block. } .
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
BMI — ARC-style Body Mass Index example
|
|
2
|
+
|
|
3
|
+
Answer
|
|
4
|
+
BMI = 22.72
|
|
5
|
+
Category = Normal
|
|
6
|
+
At height 178 cm, a healthy-weight range is about 58.6–78.9 kg (BMI 18.5–24.9).
|
|
7
|
+
|
|
8
|
+
Reason Why
|
|
9
|
+
BMI is defined as weight in kilograms divided by height in meters squared. This program first normalizes the input to SI units, computes BMI, and then applies WHO adult categories as half-open intervals. The healthy-weight band is the weight range at the same height that corresponds to BMI 18.5 through 24.9.
|
|
10
|
+
|
|
11
|
+
Check
|
|
12
|
+
C1 OK - the input was normalized into positive SI values.
|
|
13
|
+
C2 OK - height squared was reconstructed from the normalized height.
|
|
14
|
+
C3 OK - the BMI value matches the BMI = kg / m² formula.
|
|
15
|
+
C4 OK - a BMI of 18.49 stays below the normal-weight threshold.
|
|
16
|
+
C5 OK - the lower boundary is half-open: BMI 18.5 is classified as Normal.
|
|
17
|
+
C6 OK - BMI 25.0 starts the Overweight category.
|
|
18
|
+
C7 OK - BMI 30.0 starts the Obesity I category.
|
|
19
|
+
C8 OK - classification behavior is monotonic across representative BMI values.
|
|
20
|
+
C9 OK - the healthy-weight band was reconstructed from BMI 18.5 to 24.9 at the same height.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
=== Answer ===
|
|
2
|
+
In this toy PN-junction tunneling model, heavy doping narrows the depletion region enough for a tunneling window that rises to a peak and then falls, producing a negative-differential region.
|
|
3
|
+
case : pn-junction-tunneling
|
|
4
|
+
peak bias : 2
|
|
5
|
+
peak current proxy : 4
|
|
6
|
+
negative differential region : yes
|
|
7
|
+
|
|
8
|
+
=== Reason Why ===
|
|
9
|
+
We model tunneling current as an exact overlap count between filled N-side states and empty P-side states while forward bias shifts the bands. Heavy doping is represented by a much narrower depletion region.
|
|
10
|
+
ordinary depletion width (nm) : 8
|
|
11
|
+
tunnel depletion width (nm) : 1
|
|
12
|
+
filled N-side states : [1, 2, 3, 4]
|
|
13
|
+
empty P-side states at 0 bias : [3, 4, 5, 6]
|
|
14
|
+
bias -> overlap current proxy : 0->2, 1->3, 2->4, 3->3, 4->2, 5->1, 6->0
|
|
15
|
+
peak point : 2 -> 4
|
|
16
|
+
high-bias point : 6 -> 0
|
|
17
|
+
|
|
18
|
+
=== Check ===
|
|
19
|
+
heavily doped barrier is narrower : yes
|
|
20
|
+
peak occurs before overlap closes : yes
|
|
21
|
+
negative differential region present : yes
|
|
22
|
+
high-bias overlap closes : yes
|
|
23
|
+
peak equals full four-state overlap: yes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
=== Answer ===
|
|
2
|
+
The puzzle is solved, and the completed grid is the unique valid Sudoku solution.
|
|
3
|
+
case : sudoku
|
|
4
|
+
default puzzle : classic
|
|
5
|
+
|
|
6
|
+
Puzzle
|
|
7
|
+
1 . . | . . 7 | . 9 .
|
|
8
|
+
. 3 . | . 2 . | . . 8
|
|
9
|
+
. . 9 | 6 . . | 5 . .
|
|
10
|
+
|
|
11
|
+
. . 5 | 3 . . | 9 . .
|
|
12
|
+
. 1 . | . 8 . | . . 2
|
|
13
|
+
6 . . | . . 4 | . . .
|
|
14
|
+
|
|
15
|
+
3 . . | . . . | . 1 .
|
|
16
|
+
. 4 . | . . . | . . 7
|
|
17
|
+
. . 7 | . . . | 3 . .
|
|
18
|
+
|
|
19
|
+
Completed grid
|
|
20
|
+
1 6 2 | 8 5 7 | 4 9 3
|
|
21
|
+
5 3 4 | 1 2 9 | 6 7 8
|
|
22
|
+
7 8 9 | 6 4 3 | 5 2 1
|
|
23
|
+
|
|
24
|
+
4 7 5 | 3 1 2 | 9 8 6
|
|
25
|
+
9 1 3 | 5 8 6 | 7 4 2
|
|
26
|
+
6 2 8 | 7 9 4 | 1 3 5
|
|
27
|
+
|
|
28
|
+
3 5 6 | 4 7 8 | 2 1 9
|
|
29
|
+
2 4 1 | 9 3 5 | 8 6 7
|
|
30
|
+
8 9 7 | 2 6 1 | 3 5 4
|
|
31
|
+
=== Reason Why ===
|
|
32
|
+
The solver starts from 23 clues and fills the remaining 58 cells by combining constraint propagation with depth-first search. At each step it chooses the empty cell with the fewest legal digits, places forced singles immediately, and only guesses when more than one candidate remains. Across the search it made 213 forced placements and tried 22 guesses, visited 23 search nodes overall, and backtracked 12 times before reaching the completed grid. The solver also confirmed that the solution is unique. Early steps: r2c3=4: guess, r5c3=3: forced, r2c1=5: guess, r2c4=1: guess, r2c6=9: forced, r2c7=6: guess, r2c8=7: forced, r1c7=4: guess, … and 50 more placements
|
|
33
|
+
|
|
34
|
+
=== Check ===
|
|
35
|
+
C1 OK - every given clue is preserved in the final grid.
|
|
36
|
+
C2 OK - the final grid contains only digits 1 through 9, with no blanks left.
|
|
37
|
+
C3 OK - each row contains every digit exactly once.
|
|
38
|
+
C4 OK - each column contains every digit exactly once.
|
|
39
|
+
C5 OK - each 3×3 box contains every digit exactly once.
|
|
40
|
+
C6 OK - replaying the recorded placements from the original puzzle remains legal at every step.
|
|
41
|
+
C7 OK - the search statistics and the successful proof path are internally consistent.
|
|
42
|
+
C8 OK - a second search found no alternative solution, so the solution is unique.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
=== Answer ===
|
|
2
|
+
In this toy transistor-switch model, a low input leaves the transistor in cutoff (OFF) and a high input drives it into saturation (ON), so the load behaves like an on/off branch rather than a linear amplifier.
|
|
3
|
+
case : transistor-switch
|
|
4
|
+
low input state : cutoff / OFF
|
|
5
|
+
high input state : saturation / ON
|
|
6
|
+
on-state load current : 4.80 mA
|
|
7
|
+
|
|
8
|
+
=== Reason Why ===
|
|
9
|
+
We model an NPN low-side switch with exact millivolt and microamp arithmetic. The base current comes from (Vin - Vbe,on)/Rb when the base-emitter junction is forward biased, and the collector current is the smaller of beta * Ib and the load-limited current (Vcc - Vce,sat)/Rl.
|
|
10
|
+
supply voltage : 5.00 V
|
|
11
|
+
base resistor : 10000 ohms
|
|
12
|
+
load resistor : 1000 ohms
|
|
13
|
+
transistor beta proxy : 100
|
|
14
|
+
low input : Vin=0.00 V -> Ib=0.00 mA, Ic=0.00 mA, Vce=5.00 V, state=cutoff / OFF
|
|
15
|
+
high input : Vin=5.00 V -> Ib=0.43 mA, Ic=4.80 mA, Vce=0.20 V, state=saturation / ON
|
|
16
|
+
high-input gain limit : 43.00 mA
|
|
17
|
+
high-input load limit : 4.80 mA
|
|
18
|
+
|
|
19
|
+
=== Check ===
|
|
20
|
+
low input stays in cutoff : yes
|
|
21
|
+
high input reaches saturation : yes
|
|
22
|
+
switching states differ : yes
|
|
23
|
+
on-state current is load-limited : yes
|
|
24
|
+
load voltage matches resistor drop : yes
|