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 }.
|
package/eyeling-builtins.ttl
CHANGED
|
@@ -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
|
}
|