eyeling 1.14.0 → 1.14.1
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 +6 -18
- package/examples/genetic-algorithm.n3 +136 -22
- package/eyeling-builtins.ttl +2 -4
- package/eyeling.js +37 -75
- package/lib/builtins.js +34 -75
- package/package.json +1 -1
package/HANDBOOK.md
CHANGED
|
@@ -1551,6 +1551,12 @@ If `i` is out of range, `out` is the original string.
|
|
|
1551
1551
|
|
|
1552
1552
|
Returns the number of differing positions between `a` and `b`. Fails if the two strings have different lengths.
|
|
1553
1553
|
|
|
1554
|
+
#### `string:mutateSelectBest`
|
|
1555
|
+
|
|
1556
|
+
**Shape:** `( samples current target mutProb seedState ) string:mutateSelectBest ( best bestScore seedState2 )`
|
|
1557
|
+
|
|
1558
|
+
Generates `samples` mutated variants of `current` (mutating each character with probability `mutProb` percent), scores each candidate against `target` (Hamming distance), and returns the best candidate (lowest score). RNG is a deterministic 31-bit LCG, threaded via `seedState` → `seedState2`, so runs are reproducible.
|
|
1559
|
+
|
|
1554
1560
|
### Containment and prefix/suffix tests
|
|
1555
1561
|
|
|
1556
1562
|
- `string:contains`
|
|
@@ -1976,24 +1982,6 @@ References:
|
|
|
1976
1982
|
|
|
1977
1983
|
If you are running untrusted inputs, consider `--super-restricted` to disable all builtins except implication.
|
|
1978
1984
|
|
|
1979
|
-
### Eyeling fast-path builtins
|
|
1980
|
-
|
|
1981
|
-
Eyeling ships one optional fast-path builtins builtin e.g. used by the genetic-algorithm example:
|
|
1982
|
-
|
|
1983
|
-
#### `urn:eyeling:ga:solveString`
|
|
1984
|
-
|
|
1985
|
-
**Shape:** `( target mutationProbability samples seed traceEvery maxGenerations ) urn:eyeling:ga:solveString ( generation score value seed )`
|
|
1986
|
-
|
|
1987
|
-
- `target` and `value` are `xsd:string` literals.
|
|
1988
|
-
- `mutationProbability` is interpreted as a percentage per character (0–100).
|
|
1989
|
-
- `traceEvery` controls debug tracing:
|
|
1990
|
-
- `0` disables tracing,
|
|
1991
|
-
- `1` traces every generation,
|
|
1992
|
-
- `N` traces every `N` generations.
|
|
1993
|
-
- `maxGenerations` is a safety cap:
|
|
1994
|
-
- `0` means unlimited,
|
|
1995
|
-
- otherwise the builtin stops once `generation >= maxGenerations` (even if not solved).
|
|
1996
|
-
|
|
1997
1985
|
### A.6 Skolemization and `log:skolem`
|
|
1998
1986
|
|
|
1999
1987
|
When forward rule heads contain blank nodes (existentials), Eyeling replaces them with generated Skolem IRIs so derived facts are ground.
|
|
@@ -10,46 +10,158 @@
|
|
|
10
10
|
# This is a simple hill-climbing GA (selection + mutation, no crossover), using a seeded RNG
|
|
11
11
|
# so runs are reproducible.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
13
|
+
# The GA is still expressed as backward rules (<=) for the outer evolution loop,
|
|
14
|
+
# but the expensive inner loop ("take N mutated samples and keep the best") is
|
|
15
|
+
# handled by a single string builtin to avoid huge proof trees and OOM.
|
|
15
16
|
#
|
|
16
|
-
# Configure:
|
|
17
|
-
# :mutationProbability 5 # percent per character
|
|
18
|
-
# :samples 80
|
|
19
|
-
# :seed 100
|
|
20
|
-
# :traceEvery 0 # 0=off, 1=every generation, 100=every 100 generations
|
|
21
|
-
# :maxGenerations 0 # 0=unlimited
|
|
22
17
|
# ==========================================================================================
|
|
23
18
|
|
|
24
|
-
@prefix : <
|
|
25
|
-
@prefix ega: <urn:eyeling:ga:>.
|
|
19
|
+
@prefix : <urn:ga:>.
|
|
26
20
|
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
21
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
22
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#>.
|
|
27
23
|
@prefix string: <http://www.w3.org/2000/10/swap/string#>.
|
|
28
24
|
|
|
29
25
|
:cfg
|
|
30
26
|
:mutationProbability 5;
|
|
31
27
|
:samples 80;
|
|
32
28
|
:seed 100;
|
|
33
|
-
:traceEvery 1;
|
|
34
|
-
:maxGenerations 0.
|
|
35
29
|
|
|
36
|
-
#
|
|
37
|
-
|
|
30
|
+
# Debug knobs:
|
|
31
|
+
:traceEvery 1; # 0 disables; 1 traces every generation
|
|
32
|
+
:maxGenerations 0; # 0 means unlimited
|
|
33
|
+
|
|
34
|
+
:alphabet (" " "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
|
|
35
|
+
"N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z").
|
|
36
|
+
|
|
37
|
+
# ------------------------
|
|
38
|
+
# Small arithmetic helpers
|
|
39
|
+
# ------------------------
|
|
40
|
+
|
|
41
|
+
{ ( ?N 1 ) :dec ?N1 } <= { ( ?N 1 ) math:difference ?N1 }.
|
|
42
|
+
{ ( ?N 1 ) :inc ?N1 } <= { ( ?N 1 ) math:sum ?N1 }.
|
|
43
|
+
|
|
44
|
+
# ----------------------------------------------
|
|
45
|
+
# Seeded RNG (LCG) and JS Math.round(rnd() * N)
|
|
46
|
+
# (Used only for the initial random chromosome.)
|
|
47
|
+
# ----------------------------------------------
|
|
48
|
+
|
|
49
|
+
{ ?S0 :lcgNext ?S1 } <= {
|
|
50
|
+
( ?S0 1103515245 ) math:product ?P .
|
|
51
|
+
( ?P 12345 ) math:sum ?T .
|
|
52
|
+
( ?T 2147483648 ) math:remainder ?S1
|
|
53
|
+
}.
|
|
54
|
+
|
|
55
|
+
{ ( ?S0 ?N ) :randRound ( ?R ?S1 ) } <= {
|
|
56
|
+
?S0 :lcgNext ?S1 .
|
|
57
|
+
( ?S1 ?N ) math:product ?A .
|
|
58
|
+
( ?A ?A ) math:sum ?TwoA .
|
|
59
|
+
( ?TwoA 2147483648 ) math:sum ?Num .
|
|
60
|
+
( 2147483648 2 ) math:product ?Den .
|
|
61
|
+
( ?Num ?Den ) math:integerQuotient ?R
|
|
62
|
+
}.
|
|
63
|
+
|
|
64
|
+
{ ?S0 :randomAlpha ( ?Ch ?S1 ) } <= {
|
|
65
|
+
:cfg :alphabet ?Alphabet .
|
|
66
|
+
( ?S0 26 ) :randRound ( ?Idx ?S1 ) .
|
|
67
|
+
( ?Alphabet ?Idx ) list:memberAt ?Ch
|
|
68
|
+
}.
|
|
69
|
+
|
|
70
|
+
{ ( 0 ?S0 ) :randomText ( "" ?S0 ) } <= true.
|
|
71
|
+
|
|
72
|
+
{ ( ?Len ?S0 ) :randomText ( ?Out ?S2 ) } <= {
|
|
73
|
+
?Len math:greaterThan 0 .
|
|
74
|
+
( ?Len 1 ) :dec ?Len1 .
|
|
75
|
+
?S0 :randomAlpha ( ?Ch ?S1 ) .
|
|
76
|
+
( ?Len1 ?S1 ) :randomText ( ?Rest ?S2 ) .
|
|
77
|
+
( "%s%s" ?Ch ?Rest ) string:format ?Out
|
|
78
|
+
}.
|
|
79
|
+
|
|
80
|
+
# -------------------------
|
|
81
|
+
# tracing helper (optional)
|
|
82
|
+
# -------------------------
|
|
83
|
+
|
|
84
|
+
{ ( 0 ?Gen ?Score ?Value ) :traceMaybe true } <= true.
|
|
85
|
+
|
|
86
|
+
{ ( ?Every ?Gen ?Score ?Value ) :traceMaybe true } <= {
|
|
87
|
+
?Every math:greaterThan 0 .
|
|
88
|
+
( ?Gen ?Every ) math:remainder ?R .
|
|
89
|
+
?R math:equalTo 0 .
|
|
90
|
+
?Gen log:trace ( ?Score ?Value )
|
|
91
|
+
}.
|
|
92
|
+
|
|
93
|
+
# --------------
|
|
94
|
+
# evolution loop
|
|
95
|
+
# --------------
|
|
96
|
+
|
|
97
|
+
# Stop when score == 0
|
|
98
|
+
{ ( ?Current 0 ?Target ?MutProb ?Samples ?Gen ?S0 ?MaxGen ?TraceEvery )
|
|
99
|
+
:evolve ( ?Current ?Gen ?S0 ) } <= true.
|
|
100
|
+
|
|
101
|
+
# Stop when generation cap is reached (only if MaxGen > 0)
|
|
102
|
+
{ ( ?Current ?Score ?Target ?MutProb ?Samples ?Gen ?S0 ?MaxGen ?TraceEvery )
|
|
103
|
+
:evolve ( ?Current ?Gen ?S0 ) } <= {
|
|
104
|
+
?Score math:notEqualTo 0 .
|
|
105
|
+
?MaxGen math:greaterThan 0 .
|
|
106
|
+
?Gen math:equalTo ?MaxGen
|
|
107
|
+
}.
|
|
108
|
+
|
|
109
|
+
# Unlimited run (MaxGen == 0): keep evolving
|
|
110
|
+
{ ( ?Current ?Score ?Target ?MutProb ?Samples ?Gen ?S0 0 ?TraceEvery )
|
|
111
|
+
:evolve ( ?Final ?FinalGen ?S2 ) } <= {
|
|
112
|
+
?Score math:notEqualTo 0 .
|
|
113
|
+
( ?Samples ?Current ?Target ?MutProb ?S0 )
|
|
114
|
+
string:mutateSelectBest
|
|
115
|
+
( ?Best ?BestScore ?S1 ) .
|
|
116
|
+
( ?Gen 1 ) :inc ?Gen1 .
|
|
117
|
+
( ?TraceEvery ?Gen1 ?BestScore ?Best ) :traceMaybe true .
|
|
118
|
+
( ?Best ?BestScore ?Target ?MutProb ?Samples ?Gen1 ?S1 0 ?TraceEvery )
|
|
119
|
+
:evolve ( ?Final ?FinalGen ?S2 )
|
|
120
|
+
}.
|
|
121
|
+
|
|
122
|
+
# Bounded run (MaxGen > 0): keep evolving while Gen < MaxGen
|
|
123
|
+
{ ( ?Current ?Score ?Target ?MutProb ?Samples ?Gen ?S0 ?MaxGen ?TraceEvery )
|
|
124
|
+
:evolve ( ?Final ?FinalGen ?S2 ) } <= {
|
|
125
|
+
?Score math:notEqualTo 0 .
|
|
126
|
+
?MaxGen math:greaterThan 0 .
|
|
127
|
+
?Gen math:lessThan ?MaxGen .
|
|
128
|
+
( ?Samples ?Current ?Target ?MutProb ?S0 )
|
|
129
|
+
string:mutateSelectBest
|
|
130
|
+
( ?Best ?BestScore ?S1 ) .
|
|
131
|
+
( ?Gen 1 ) :inc ?Gen1 .
|
|
132
|
+
( ?TraceEvery ?Gen1 ?BestScore ?Best ) :traceMaybe true .
|
|
133
|
+
( ?Best ?BestScore ?Target ?MutProb ?Samples ?Gen1 ?S1 ?MaxGen ?TraceEvery )
|
|
134
|
+
:evolve ( ?Final ?FinalGen ?S2 )
|
|
135
|
+
}.
|
|
136
|
+
|
|
137
|
+
# ----------------------------------
|
|
138
|
+
# Public solver wrapper (string API)
|
|
139
|
+
# ----------------------------------
|
|
140
|
+
|
|
141
|
+
{ ?TargetString :solveResult ( ?Gen 0 ?Value ?Seed ) } <= {
|
|
38
142
|
:cfg :mutationProbability ?MutProb;
|
|
39
143
|
:samples ?Samples;
|
|
40
144
|
:seed ?Seed;
|
|
41
145
|
:traceEvery ?TraceEvery;
|
|
42
|
-
:maxGenerations ?
|
|
146
|
+
:maxGenerations ?MaxGen.
|
|
147
|
+
|
|
148
|
+
?TargetString string:length ?Len .
|
|
149
|
+
|
|
150
|
+
( ?Len ?Seed ) :randomText ( ?Current0 ?S1 ) .
|
|
151
|
+
|
|
152
|
+
# initial score using string:hammingDistance
|
|
153
|
+
( ?Current0 ?TargetString ) string:hammingDistance ?Score0 .
|
|
43
154
|
|
|
44
|
-
( ?TargetString ?MutProb ?Samples ?
|
|
45
|
-
|
|
46
|
-
( ?
|
|
155
|
+
( ?Current0 ?Score0 ?TargetString ?MutProb ?Samples 0 ?S1 ?MaxGen ?TraceEvery )
|
|
156
|
+
:evolve
|
|
157
|
+
( ?Value ?Gen ?S2 ) .
|
|
47
158
|
|
|
48
|
-
#
|
|
49
|
-
?
|
|
159
|
+
# Succeeds only when solved:
|
|
160
|
+
( ?Value ?TargetString ) string:hammingDistance 0 .
|
|
161
|
+
?Value log:equalTo ?TargetString .
|
|
162
|
+
?S2 log:equalTo ?_FinalSeedState
|
|
50
163
|
}.
|
|
51
164
|
|
|
52
|
-
# Boolean-ish API for querying success
|
|
53
165
|
{ ?TargetString :solved true } <= {
|
|
54
166
|
?TargetString :solveResult ( ?Gen 0 ?TargetString ?Seed )
|
|
55
167
|
}.
|
|
@@ -60,4 +172,6 @@
|
|
|
60
172
|
|
|
61
173
|
{ "METHINKS IT IS LIKE A WEASEL" :solved true .
|
|
62
174
|
( "solve('%s').\n" "METHINKS IT IS LIKE A WEASEL" ) string:format ?Line
|
|
63
|
-
}
|
|
175
|
+
}
|
|
176
|
+
log:query
|
|
177
|
+
{ 1 log:outputString ?Line }.
|
package/eyeling-builtins.ttl
CHANGED
|
@@ -346,7 +346,5 @@ string:setCharAt a ex:Builtin ; ex:kind ex:Function ;
|
|
|
346
346
|
string:hammingDistance a ex:Builtin ; ex:kind ex:Function ;
|
|
347
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
348
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
ega:solveString a ex:Builtin ; ex:kind ex:Function ;
|
|
352
|
-
rdfs:comment "Eyeling extension: GA solver on strings. Subject list: (target mutationProbability samples seed traceEvery maxGenerations). Object list: (generation score value seed). traceEvery=0 disables tracing; maxGenerations=0 means unlimited." .
|
|
349
|
+
string:mutateSelectBest a ex:Builtin ; ex:kind ex:Function ;
|
|
350
|
+
rdfs:comment "Shape: (samples current target mutProb seedState) string:mutateSelectBest (best bestScore seedState2). Generates samples mutated variants of current (mutating each character with probability mutProb percent), scores each candidate against target (Hamming distance), and returns the best candidate (lowest score). RNG is a deterministic 31-bit LCG, threaded via seedState → seedState2, so runs are reproducible." .
|
package/eyeling.js
CHANGED
|
@@ -3547,34 +3547,35 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3547
3547
|
return s2 !== null ? [s2] : [];
|
|
3548
3548
|
}
|
|
3549
3549
|
|
|
3550
|
-
//
|
|
3551
|
-
//
|
|
3552
|
-
//
|
|
3553
|
-
|
|
3554
|
-
//
|
|
3555
|
-
//
|
|
3556
|
-
//
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
const
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3550
|
+
// string:mutateSelectBest
|
|
3551
|
+
// Schema:
|
|
3552
|
+
// ( $samples $current $target $mutProb $seedState )
|
|
3553
|
+
// string:mutateSelectBest
|
|
3554
|
+
// ( $best $bestScore $seedState2 )
|
|
3555
|
+
//
|
|
3556
|
+
// Deterministic 31-bit LCG (same as the GA demo):
|
|
3557
|
+
// state = (1103515245 * state + 12345) % 2^31
|
|
3558
|
+
// and randRound(N) = Math.round((state/2^31) * N) with state advanced once per draw.
|
|
3559
|
+
//
|
|
3560
|
+
// This builtin exists to keep GA-style "many samples × many characters" loops out of
|
|
3561
|
+
// the proof search (dramatically reducing memory use).
|
|
3562
|
+
if (pv === STRING_NS + 'mutateSelectBest') {
|
|
3563
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 5) return [];
|
|
3564
|
+
|
|
3565
|
+
const samplesNum = parseNum(g.s.elems[0]);
|
|
3566
|
+
const current = termToJsXsdStringNoLang(g.s.elems[1]);
|
|
3567
|
+
const target = termToJsXsdStringNoLang(g.s.elems[2]);
|
|
3568
|
+
const mutProbNum = parseNum(g.s.elems[3]);
|
|
3569
|
+
const seedNum = parseNum(g.s.elems[4]);
|
|
3570
|
+
|
|
3571
|
+
if (samplesNum === null || current === null || target === null || mutProbNum === null || seedNum === null)
|
|
3572
|
+
return [];
|
|
3573
|
+
if (current.length !== target.length) return [];
|
|
3569
3574
|
|
|
3570
|
-
const mutProb = Math.max(0, Math.trunc(mutProbNum));
|
|
3571
3575
|
const samples = Math.max(1, Math.trunc(samplesNum));
|
|
3576
|
+
const mutProb = Math.max(0, Math.trunc(mutProbNum));
|
|
3572
3577
|
const seed0 = Math.trunc(seedNum);
|
|
3573
3578
|
|
|
3574
|
-
const traceEvery = Math.max(0, Math.trunc(traceEveryNum));
|
|
3575
|
-
const maxGenerations = Math.max(0, Math.trunc(maxGenNum));
|
|
3576
|
-
|
|
3577
|
-
// Deterministic 31-bit LCG using BigInt arithmetic (matches the N3 version)
|
|
3578
3579
|
let state = BigInt(seed0);
|
|
3579
3580
|
const MOD = 2147483648n; // 2^31
|
|
3580
3581
|
const A = 1103515245n;
|
|
@@ -3594,12 +3595,6 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3594
3595
|
return p === 0 ? ' ' : String.fromCharCode(64 + p);
|
|
3595
3596
|
}
|
|
3596
3597
|
|
|
3597
|
-
function randomText(len) {
|
|
3598
|
-
let out = '';
|
|
3599
|
-
for (let i = 0; i < len; i++) out += randomAlpha();
|
|
3600
|
-
return out;
|
|
3601
|
-
}
|
|
3602
|
-
|
|
3603
3598
|
function mutate(str) {
|
|
3604
3599
|
let out = '';
|
|
3605
3600
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -3616,50 +3611,22 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3616
3611
|
return diffs;
|
|
3617
3612
|
}
|
|
3618
3613
|
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
const genLit = internLiteral(String(gen));
|
|
3622
|
-
const scLit = internLiteral(String(sc));
|
|
3623
|
-
const valLit = makeStringLiteral(val);
|
|
3624
|
-
const obj = new ListTerm([scLit, valLit]);
|
|
3625
|
-
const xStr = termToN3(genLit, pref);
|
|
3626
|
-
const yStr = termToN3(obj, pref);
|
|
3627
|
-
trace.writeTraceLine(`${xStr} TRACE ${yStr}`);
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
let generation = 0;
|
|
3631
|
-
let current = randomText(target.length);
|
|
3632
|
-
let currentScore = score(current);
|
|
3633
|
-
|
|
3634
|
-
if (traceEvery > 0 && generation % traceEvery === 0) doTrace(generation, currentScore, current);
|
|
3635
|
-
|
|
3636
|
-
while (currentScore !== 0) {
|
|
3637
|
-
if (maxGenerations > 0 && generation >= maxGenerations) break;
|
|
3638
|
-
|
|
3639
|
-
let best = '';
|
|
3640
|
-
let bestScore = Infinity;
|
|
3641
|
-
|
|
3642
|
-
for (let i = 0; i < samples; i++) {
|
|
3643
|
-
const cand = mutate(current);
|
|
3644
|
-
const candScore = score(cand);
|
|
3645
|
-
if (candScore < bestScore) {
|
|
3646
|
-
best = cand;
|
|
3647
|
-
bestScore = candScore;
|
|
3648
|
-
}
|
|
3649
|
-
}
|
|
3614
|
+
let best = '';
|
|
3615
|
+
let bestScore = Infinity;
|
|
3650
3616
|
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3617
|
+
for (let i = 0; i < samples; i++) {
|
|
3618
|
+
const cand = mutate(current);
|
|
3619
|
+
const candScore = score(cand);
|
|
3620
|
+
if (candScore < bestScore) {
|
|
3621
|
+
best = cand;
|
|
3622
|
+
bestScore = candScore;
|
|
3623
|
+
}
|
|
3656
3624
|
}
|
|
3657
3625
|
|
|
3658
3626
|
const outList = new ListTerm([
|
|
3659
|
-
|
|
3660
|
-
internLiteral(String(
|
|
3661
|
-
|
|
3662
|
-
internLiteral(String(seed0)),
|
|
3627
|
+
makeStringLiteral(best),
|
|
3628
|
+
internLiteral(String(bestScore)),
|
|
3629
|
+
internLiteral(state.toString()),
|
|
3663
3630
|
]);
|
|
3664
3631
|
|
|
3665
3632
|
const s2 = unifyTerm(g.o, outList, subst);
|
|
@@ -3686,11 +3653,6 @@ function isBuiltinPred(p) {
|
|
|
3686
3653
|
return true;
|
|
3687
3654
|
}
|
|
3688
3655
|
|
|
3689
|
-
// Eyeling extension: GA demo builtins
|
|
3690
|
-
if (v === 'urn:eyeling:ga:solveString') {
|
|
3691
|
-
return true;
|
|
3692
|
-
}
|
|
3693
|
-
|
|
3694
3656
|
return (
|
|
3695
3657
|
v.startsWith(CRYPTO_NS) ||
|
|
3696
3658
|
v.startsWith(MATH_NS) ||
|
package/lib/builtins.js
CHANGED
|
@@ -3535,34 +3535,32 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3535
3535
|
return s2 !== null ? [s2] : [];
|
|
3536
3536
|
}
|
|
3537
3537
|
|
|
3538
|
-
//
|
|
3539
|
-
//
|
|
3540
|
-
//
|
|
3541
|
-
|
|
3542
|
-
//
|
|
3543
|
-
//
|
|
3544
|
-
//
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
const samplesNum = parseNum(g.s.elems[
|
|
3551
|
-
const
|
|
3552
|
-
const
|
|
3553
|
-
const
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
if (
|
|
3538
|
+
// string:mutateSelectBest
|
|
3539
|
+
// Schema:
|
|
3540
|
+
// ( $samples $current $target $mutProb $seedState )
|
|
3541
|
+
// string:mutateSelectBest
|
|
3542
|
+
// ( $best $bestScore $seedState2 )
|
|
3543
|
+
//
|
|
3544
|
+
// Deterministic 31-bit LCG (same as the GA demo):
|
|
3545
|
+
// state = (1103515245 * state + 12345) % 2^31
|
|
3546
|
+
// and randRound(N) = Math.round((state/2^31) * N) with state advanced once per draw.
|
|
3547
|
+
if (pv === STRING_NS + 'mutateSelectBest') {
|
|
3548
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 5) return [];
|
|
3549
|
+
|
|
3550
|
+
const samplesNum = parseNum(g.s.elems[0]);
|
|
3551
|
+
const current = termToJsXsdStringNoLang(g.s.elems[1]);
|
|
3552
|
+
const target = termToJsXsdStringNoLang(g.s.elems[2]);
|
|
3553
|
+
const mutProbNum = parseNum(g.s.elems[3]);
|
|
3554
|
+
const seedNum = parseNum(g.s.elems[4]);
|
|
3555
|
+
|
|
3556
|
+
if (samplesNum === null || current === null || target === null || mutProbNum === null || seedNum === null)
|
|
3557
|
+
return [];
|
|
3558
|
+
if (current.length !== target.length) return [];
|
|
3557
3559
|
|
|
3558
|
-
const mutProb = Math.max(0, Math.trunc(mutProbNum));
|
|
3559
3560
|
const samples = Math.max(1, Math.trunc(samplesNum));
|
|
3561
|
+
const mutProb = Math.max(0, Math.trunc(mutProbNum));
|
|
3560
3562
|
const seed0 = Math.trunc(seedNum);
|
|
3561
3563
|
|
|
3562
|
-
const traceEvery = Math.max(0, Math.trunc(traceEveryNum));
|
|
3563
|
-
const maxGenerations = Math.max(0, Math.trunc(maxGenNum));
|
|
3564
|
-
|
|
3565
|
-
// Deterministic 31-bit LCG using BigInt arithmetic (matches the N3 version)
|
|
3566
3564
|
let state = BigInt(seed0);
|
|
3567
3565
|
const MOD = 2147483648n; // 2^31
|
|
3568
3566
|
const A = 1103515245n;
|
|
@@ -3582,12 +3580,6 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3582
3580
|
return p === 0 ? ' ' : String.fromCharCode(64 + p);
|
|
3583
3581
|
}
|
|
3584
3582
|
|
|
3585
|
-
function randomText(len) {
|
|
3586
|
-
let out = '';
|
|
3587
|
-
for (let i = 0; i < len; i++) out += randomAlpha();
|
|
3588
|
-
return out;
|
|
3589
|
-
}
|
|
3590
|
-
|
|
3591
3583
|
function mutate(str) {
|
|
3592
3584
|
let out = '';
|
|
3593
3585
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -3604,50 +3596,22 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3604
3596
|
return diffs;
|
|
3605
3597
|
}
|
|
3606
3598
|
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
const genLit = internLiteral(String(gen));
|
|
3610
|
-
const scLit = internLiteral(String(sc));
|
|
3611
|
-
const valLit = makeStringLiteral(val);
|
|
3612
|
-
const obj = new ListTerm([scLit, valLit]);
|
|
3613
|
-
const xStr = termToN3(genLit, pref);
|
|
3614
|
-
const yStr = termToN3(obj, pref);
|
|
3615
|
-
trace.writeTraceLine(`${xStr} TRACE ${yStr}`);
|
|
3616
|
-
}
|
|
3617
|
-
|
|
3618
|
-
let generation = 0;
|
|
3619
|
-
let current = randomText(target.length);
|
|
3620
|
-
let currentScore = score(current);
|
|
3621
|
-
|
|
3622
|
-
if (traceEvery > 0 && generation % traceEvery === 0) doTrace(generation, currentScore, current);
|
|
3623
|
-
|
|
3624
|
-
while (currentScore !== 0) {
|
|
3625
|
-
if (maxGenerations > 0 && generation >= maxGenerations) break;
|
|
3626
|
-
|
|
3627
|
-
let best = '';
|
|
3628
|
-
let bestScore = Infinity;
|
|
3629
|
-
|
|
3630
|
-
for (let i = 0; i < samples; i++) {
|
|
3631
|
-
const cand = mutate(current);
|
|
3632
|
-
const candScore = score(cand);
|
|
3633
|
-
if (candScore < bestScore) {
|
|
3634
|
-
best = cand;
|
|
3635
|
-
bestScore = candScore;
|
|
3636
|
-
}
|
|
3637
|
-
}
|
|
3599
|
+
let best = '';
|
|
3600
|
+
let bestScore = Infinity;
|
|
3638
3601
|
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3602
|
+
for (let i = 0; i < samples; i++) {
|
|
3603
|
+
const cand = mutate(current);
|
|
3604
|
+
const candScore = score(cand);
|
|
3605
|
+
if (candScore < bestScore) {
|
|
3606
|
+
best = cand;
|
|
3607
|
+
bestScore = candScore;
|
|
3608
|
+
}
|
|
3644
3609
|
}
|
|
3645
3610
|
|
|
3646
3611
|
const outList = new ListTerm([
|
|
3647
|
-
|
|
3648
|
-
internLiteral(String(
|
|
3649
|
-
|
|
3650
|
-
internLiteral(String(seed0)),
|
|
3612
|
+
makeStringLiteral(best),
|
|
3613
|
+
internLiteral(String(bestScore)),
|
|
3614
|
+
internLiteral(state.toString()),
|
|
3651
3615
|
]);
|
|
3652
3616
|
|
|
3653
3617
|
const s2 = unifyTerm(g.o, outList, subst);
|
|
@@ -3674,11 +3638,6 @@ function isBuiltinPred(p) {
|
|
|
3674
3638
|
return true;
|
|
3675
3639
|
}
|
|
3676
3640
|
|
|
3677
|
-
// Eyeling extension: GA demo builtins
|
|
3678
|
-
if (v === 'urn:eyeling:ga:solveString') {
|
|
3679
|
-
return true;
|
|
3680
|
-
}
|
|
3681
|
-
|
|
3682
3641
|
return (
|
|
3683
3642
|
v.startsWith(CRYPTO_NS) ||
|
|
3684
3643
|
v.startsWith(MATH_NS) ||
|