eyeling 1.14.4 → 1.14.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 CHANGED
@@ -1549,12 +1549,6 @@ Returns a copy of `s` with the character at index `i` (0-based) replaced by:
1549
1549
 
1550
1550
  If `i` is out of range, `out` is the original string.
1551
1551
 
1552
- #### `string:hammingDistance`
1553
-
1554
- **Shape:** `( a b ) string:hammingDistance d`
1555
-
1556
- Returns the number of differing positions between `a` and `b`. Fails if the two strings have different lengths.
1557
-
1558
1552
  ### Containment and prefix/suffix tests
1559
1553
 
1560
1554
  - `string:contains`
@@ -0,0 +1,260 @@
1
+ # ================================================================================
2
+ # Genetic Algorithm demo: 0/1 Knapsack via mutation + selection
3
+ #
4
+ # Goal
5
+ # ----
6
+ # Evolve a bitstring genome that selects a subset of items to maximize total VALUE
7
+ # while staying within a WEIGHT capacity.
8
+ #
9
+ # Representation
10
+ # --------------
11
+ # - Items: list of pairs (weight value)
12
+ # - Genome: a string of '0'/'1' of the same length as the item list
13
+ # - bit i = '1' => item i is included
14
+ # - bit i = '0' => item i is excluded
15
+ #
16
+ # Fitness (lower is better)
17
+ # -------------------------
18
+ # We want: feasible solutions (weight <= capacity) with high value.
19
+ #
20
+ # If weight <= capacity:
21
+ # fitness = 1_000_000 - value (so higher value => lower fitness)
22
+ # If overweight:
23
+ # fitness = 2_000_000 + (weight - capacity)
24
+ # (overweight candidates are always worse than any feasible candidate)
25
+ #
26
+ # Variation (mutation)
27
+ # --------------------
28
+ # Single-bit flip: generate all "neighbors" of the current genome by flipping
29
+ # exactly one position. This is deterministic (no randomness), but it still
30
+ # demonstrates the core GA pattern of mutation + selection.
31
+ #
32
+ # Selection
33
+ # ---------
34
+ # Compare parent + all mutants, keep the candidate with the lowest fitness.
35
+ # This is essentially a (1 + λ) evolutionary strategy / hill-climber.
36
+ #
37
+ # Stopping criteria
38
+ # -----------------
39
+ # Stop when either:
40
+ # - maxGenerations is reached, or
41
+ # - no mutant improves fitness (local optimum under 1-bit flips)
42
+ # ================================================================================
43
+
44
+ @prefix : <urn:ga:>.
45
+ @prefix log: <http://www.w3.org/2000/10/swap/log#>.
46
+ @prefix math: <http://www.w3.org/2000/10/swap/math#>.
47
+ @prefix list: <http://www.w3.org/2000/10/swap/list#>.
48
+ @prefix string: <http://www.w3.org/2000/10/swap/string#>.
49
+
50
+ :cfg
51
+ :capacity 50;
52
+ :maxGenerations 64;
53
+
54
+ # Each item is (weight value). Genome bits select items by index.
55
+ :items (
56
+ (12 24)
57
+ (7 13)
58
+ (11 23)
59
+ (8 15)
60
+ (9 16)
61
+ (6 12)
62
+ (10 21)
63
+ (4 9)
64
+ (5 11)
65
+ (14 28)
66
+ (3 7)
67
+ (13 26)
68
+ ).
69
+
70
+ # -------------
71
+ # small helpers
72
+ # -------------
73
+
74
+ { ( ?N 1 ) :inc ?N1 } <= { ( ?N 1 ) math:sum ?N1 }.
75
+
76
+ # build a string of N zeros
77
+ { ( 0 ) :zeros "" } <= true.
78
+
79
+ { ( ?N ) :zeros ?S } <= {
80
+ ?N math:greaterThan 0 .
81
+ ( ?N 1 ) math:difference ?N1 .
82
+ ( ?N1 ) :zeros ?Rest .
83
+ ( "%s%s" "0" ?Rest ) string:format ?S
84
+ }.
85
+
86
+ # flip a bit at index i in a 0/1 string
87
+ { ( ?S ?I ) :flipBit ?Out } <= {
88
+ ( ?S ?I ) string:charAt "0" .
89
+ ( ?S ?I "1" ) string:setCharAt ?Out
90
+ }.
91
+
92
+ { ( ?S ?I ) :flipBit ?Out } <= {
93
+ ( ?S ?I ) string:charAt "1" .
94
+ ( ?S ?I "0" ) string:setCharAt ?Out
95
+ }.
96
+
97
+ # generate all single-bit mutants of S
98
+ { ( ?S ?Len ) :mutants ?Out } <= {
99
+ ( ?S 0 ?Len ) :mutantsFrom ?Out
100
+ }.
101
+
102
+ { ( ?S ?I ?Len ) :mutantsFrom () } <= {
103
+ ?I math:equalTo ?Len
104
+ }.
105
+
106
+ { ( ?S ?I ?Len ) :mutantsFrom ?Out } <= {
107
+ ?I math:lessThan ?Len .
108
+ ( ?S ?I ) :flipBit ?Cand .
109
+ ( ?I 1 ) :inc ?I1 .
110
+ ( ?S ?I1 ?Len ) :mutantsFrom ?Rest .
111
+ ( ( ?Cand ) ?Rest ) list:append ?Out
112
+ }.
113
+
114
+ # -----------------------------------------
115
+ # decode genome -> (totalWeight totalValue)
116
+ # -----------------------------------------
117
+
118
+ { ( ?Bits ?Items ) :totals ( ?Wsum ?Vsum ) } <= {
119
+ ?Bits string:length ?Len .
120
+ ( ?Bits ?Items 0 ?Len 0 0 ) :totalsAcc ( ?Wsum ?Vsum )
121
+ }.
122
+
123
+ { ( ?Bits ?Items ?I ?Len ?AccW ?AccV ) :totalsAcc ( ?AccW ?AccV ) } <= {
124
+ ?I math:equalTo ?Len
125
+ }.
126
+
127
+ { ( ?Bits ?Items ?I ?Len ?AccW ?AccV ) :totalsAcc ( ?Wsum ?Vsum ) } <= {
128
+ ?I math:lessThan ?Len .
129
+ ( ?Bits ?I ) string:charAt "1" .
130
+
131
+ ( ?Items ?I ) list:memberAt ?Pair .
132
+ ( ?Pair 0 ) list:memberAt ?W .
133
+ ( ?Pair 1 ) list:memberAt ?V .
134
+
135
+ ( ?AccW ?W ) math:sum ?AccW1 .
136
+ ( ?AccV ?V ) math:sum ?AccV1 .
137
+
138
+ ( ?I 1 ) :inc ?I1 .
139
+ ( ?Bits ?Items ?I1 ?Len ?AccW1 ?AccV1 ) :totalsAcc ( ?Wsum ?Vsum )
140
+ }.
141
+
142
+ { ( ?Bits ?Items ?I ?Len ?AccW ?AccV ) :totalsAcc ( ?Wsum ?Vsum ) } <= {
143
+ ?I math:lessThan ?Len .
144
+ ( ?Bits ?I ) string:charAt "0" .
145
+
146
+ ( ?I 1 ) :inc ?I1 .
147
+ ( ?Bits ?Items ?I1 ?Len ?AccW ?AccV ) :totalsAcc ( ?Wsum ?Vsum )
148
+ }.
149
+
150
+ # -------------------------
151
+ # fitness (lower is better)
152
+ # -------------------------
153
+
154
+ { ( ?W ?V ?Cap ) :fitness ?F } <= {
155
+ ?W math:notGreaterThan ?Cap .
156
+ ( 1000000 ?V ) math:difference ?F
157
+ }.
158
+
159
+ { ( ?W ?V ?Cap ) :fitness ?F } <= {
160
+ ?W math:greaterThan ?Cap .
161
+ ( ?W ?Cap ) math:difference ?Over .
162
+ ( 2000000 ?Over ) math:sum ?F
163
+ }.
164
+
165
+ { ( ?W ?Cap ) :feasible "true" } <= { ?W math:notGreaterThan ?Cap }.
166
+ { ( ?W ?Cap ) :feasible "false" } <= { ?W math:greaterThan ?Cap }.
167
+
168
+ { ( ?Bits ?Cap ?Items ) :candidateScore ( ?Fit ?Wsum ?Vsum ) } <= {
169
+ ( ?Bits ?Items ) :totals ( ?Wsum ?Vsum ) .
170
+ ( ?Wsum ?Vsum ?Cap ) :fitness ?Fit
171
+ }.
172
+
173
+ # pick best of two candidates by fitness (ties keep the left candidate)
174
+ { ( ?C1 ?S1 ?W1 ?V1 ?C2 ?S2 ?W2 ?V2 ) :pickBest ( ?C1 ?S1 ?W1 ?V1 ) } <= {
175
+ ?S1 math:notGreaterThan ?S2
176
+ }.
177
+
178
+ { ( ?C1 ?S1 ?W1 ?V1 ?C2 ?S2 ?W2 ?V2 ) :pickBest ( ?C2 ?S2 ?W2 ?V2 ) } <= {
179
+ ?S1 math:greaterThan ?S2
180
+ }.
181
+
182
+ # best candidate from a list
183
+ { ( ?Cap ?Items ?L ) :bestMutant ( ?Best ?BestScore ?BestW ?BestV ) } <= {
184
+ ?L list:length 1 .
185
+ ?L list:first ?Best .
186
+ ( ?Best ?Cap ?Items ) :candidateScore ( ?BestScore ?BestW ?BestV )
187
+ }.
188
+
189
+ { ( ?Cap ?Items ?L ) :bestMutant ( ?Best ?BestScore ?BestW ?BestV ) } <= {
190
+ ?L list:length ?N .
191
+ ?N math:greaterThan 1 .
192
+
193
+ ?L list:first ?H .
194
+ ?L list:rest ?T .
195
+
196
+ ( ?Cap ?Items ?T ) :bestMutant ( ?B2 ?S2 ?W2 ?V2 ) .
197
+ ( ?H ?Cap ?Items ) :candidateScore ( ?S1 ?W1 ?V1 ) .
198
+
199
+ ( ?H ?S1 ?W1 ?V1 ?B2 ?S2 ?W2 ?V2 ) :pickBest ( ?Best ?BestScore ?BestW ?BestV )
200
+ }.
201
+
202
+ # --------------
203
+ # evolution loop
204
+ # --------------
205
+
206
+ # generation cap
207
+ { ( ?Bits ?Cap ?Items ?Gen ?MaxGen ) :evolve ( ?Bits ?Gen ) } <= {
208
+ ?Gen math:equalTo ?MaxGen
209
+ }.
210
+
211
+ # stop if best doesn't improve
212
+ { ( ?Bits ?Cap ?Items ?Gen ?MaxGen ) :evolve ( ?Bits ?Gen ) } <= {
213
+ ?Gen math:lessThan ?MaxGen .
214
+ ( ?Bits ?Cap ?Items ) :candidateScore ( ?Score ?W ?V ) .
215
+
216
+ ?Bits string:length ?Len .
217
+ ( ?Bits ?Len ) :mutants ?Ms .
218
+ ( ( ?Bits ) ?Ms ) list:append ?Candidates .
219
+
220
+ ( ?Cap ?Items ?Candidates ) :bestMutant ( ?Best ?BestScore ?BW ?BV ) .
221
+ ?BestScore math:equalTo ?Score
222
+ }.
223
+
224
+ # keep evolving while improvement exists
225
+ { ( ?Bits ?Cap ?Items ?Gen ?MaxGen ) :evolve ( ?Final ?FinalGen ) } <= {
226
+ ?Gen math:lessThan ?MaxGen .
227
+ ( ?Bits ?Cap ?Items ) :candidateScore ( ?Score ?W ?V ) .
228
+
229
+ ?Bits string:length ?Len .
230
+ ( ?Bits ?Len ) :mutants ?Ms .
231
+ ( ( ?Bits ) ?Ms ) list:append ?Candidates .
232
+
233
+ ( ?Cap ?Items ?Candidates ) :bestMutant ( ?Best ?BestScore ?BW ?BV ) .
234
+ ?BestScore math:notEqualTo ?Score .
235
+
236
+ ( ?Gen 1 ) :inc ?Gen1 .
237
+ ( ?Best ?Cap ?Items ?Gen1 ?MaxGen ) :evolve ( ?Final ?FinalGen )
238
+ }.
239
+
240
+ # --------------
241
+ # query / output
242
+ # --------------
243
+
244
+ {
245
+ :cfg :capacity ?Cap;
246
+ :maxGenerations ?Max;
247
+ :items ?Items.
248
+
249
+ ?Items list:length ?Len .
250
+ ( ?Len ) :zeros ?Start .
251
+
252
+ ( ?Start ?Cap ?Items 0 ?Max ) :evolve ( ?Best ?Gen ) .
253
+ ( ?Best ?Cap ?Items ) :candidateScore ( ?Fit ?W ?V ) .
254
+ ( ?W ?Cap ) :feasible ?Ok .
255
+
256
+ ( "Knapsack GA best genome %s (feasible=%s): weight=%s value=%s after %s generations.\n"
257
+ ?Best ?Ok ?W ?V ?Gen ) string:format ?Line
258
+ }
259
+ log:query
260
+ { 1 log:outputString ?Line }.
@@ -0,0 +1,3 @@
1
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
2
+
3
+ 1 log:outputString "Knapsack GA best genome 101000000101 (feasible=true): weight=50 value=101 after 4 generations.\n" .
@@ -343,6 +343,3 @@ string:charAt a ex:Builtin ; ex:kind ex:Function ;
343
343
  string:setCharAt a ex:Builtin ; ex:kind ex:Function ;
344
344
  rdfs:comment "Function: subject is a 3-item list (string idx ch). Returns a copy of the string with position idx (0-based) replaced by the first character of ch; out-of-range returns the original string." .
345
345
 
346
- string:hammingDistance a ex:Builtin ; ex:kind ex:Function ;
347
- rdfs:comment "Function: subject is a 2-item list (a b). Binds/unifies object with the number of differing positions (Hamming distance). Fails if lengths differ." .
348
-
package/lib/builtins.js CHANGED
@@ -3577,27 +3577,6 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3577
3577
  return s2 !== null ? [s2] : [];
3578
3578
  }
3579
3579
 
3580
- // string:hammingDistance
3581
- // Schema: ( $a $b ) string:hammingDistance $d
3582
- // Fails if strings have different length.
3583
- if (pv === STRING_NS + 'hammingDistance') {
3584
- if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3585
- const a = termToJsString(g.s.elems[0]);
3586
- const b = termToJsString(g.s.elems[1]);
3587
- if (a === null || b === null) return [];
3588
- if (a.length !== b.length) return [];
3589
- let diffs = 0;
3590
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) diffs++;
3591
- const lit = internLiteral(String(diffs));
3592
- if (g.o instanceof Var) {
3593
- const s2 = { ...subst };
3594
- s2[g.o.name] = lit;
3595
- return [s2];
3596
- }
3597
- const s2 = unifyTerm(g.o, lit, subst);
3598
- return s2 !== null ? [s2] : [];
3599
- }
3600
-
3601
3580
  // Unknown builtin
3602
3581
  return [];
3603
3582
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.14.4",
3
+ "version": "1.14.5",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [