eyeling 1.14.3 → 1.14.4
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 +0 -6
- package/examples/fastpow.n3 +111 -0
- package/examples/genetic-algorithm.n3 +163 -120
- package/examples/modexp.n3 +87 -0
- package/examples/output/fastpow.n3 +10 -0
- package/examples/output/genetic-algorithm.n3 +1 -1
- package/examples/output/modexp.n3 +8 -0
- package/eyeling-builtins.ttl +0 -2
- package/eyeling.js +16 -86
- package/lib/builtins.js +0 -83
- package/lib/cli.js +16 -3
- package/package.json +1 -1
package/HANDBOOK.md
CHANGED
|
@@ -1555,12 +1555,6 @@ If `i` is out of range, `out` is the original string.
|
|
|
1555
1555
|
|
|
1556
1556
|
Returns the number of differing positions between `a` and `b`. Fails if the two strings have different lengths.
|
|
1557
1557
|
|
|
1558
|
-
#### `string:mutateSelectBest`
|
|
1559
|
-
|
|
1560
|
-
**Shape:** `( samples current target mutProb seedState ) string:mutateSelectBest ( best bestScore seedState2 )`
|
|
1561
|
-
|
|
1562
|
-
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.
|
|
1563
|
-
|
|
1564
1558
|
### Containment and prefix/suffix tests
|
|
1565
1559
|
|
|
1566
1560
|
- `string:contains`
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# ========================
|
|
2
|
+
# Fast exponentiation demo
|
|
3
|
+
# ========================
|
|
4
|
+
#
|
|
5
|
+
# Shows why using math:exponentiation (BigInt-capable) matters.
|
|
6
|
+
# The naive :powSlow definition uses repeated multiplication and should
|
|
7
|
+
# only be used for small exponents.
|
|
8
|
+
|
|
9
|
+
@prefix : <http://example.org/fastpow#>.
|
|
10
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
11
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
12
|
+
|
|
13
|
+
# ----------------------
|
|
14
|
+
# Fast power via builtin
|
|
15
|
+
# ----------------------
|
|
16
|
+
# pow(base, exp) = base^exp
|
|
17
|
+
{
|
|
18
|
+
(?B ?E) :pow ?R.
|
|
19
|
+
} <= {
|
|
20
|
+
(?B ?E) math:exponentiation ?R.
|
|
21
|
+
}.
|
|
22
|
+
|
|
23
|
+
# --------------------------------
|
|
24
|
+
# Slow power via repeated multiply
|
|
25
|
+
# --------------------------------
|
|
26
|
+
# powSlow(base, 0) = 1
|
|
27
|
+
{
|
|
28
|
+
(?B 0) :powSlow 1.
|
|
29
|
+
} <= {
|
|
30
|
+
0 math:equalTo 0.
|
|
31
|
+
}.
|
|
32
|
+
|
|
33
|
+
# powSlow(base, E>0) = powSlow(base, E-1) * base
|
|
34
|
+
{
|
|
35
|
+
(?B ?E) :powSlow ?R.
|
|
36
|
+
} <= {
|
|
37
|
+
?E math:greaterThan 0.
|
|
38
|
+
(?E 1) math:difference ?E1.
|
|
39
|
+
(?B ?E1) :powSlow ?Prev.
|
|
40
|
+
(?Prev ?B) math:product ?R.
|
|
41
|
+
}.
|
|
42
|
+
|
|
43
|
+
# ----------------------------------
|
|
44
|
+
# Power tower (tetration) using :pow
|
|
45
|
+
# ----------------------------------
|
|
46
|
+
# tower(base, 0) = 1
|
|
47
|
+
{
|
|
48
|
+
(?B 0) :tower 1.
|
|
49
|
+
} <= {
|
|
50
|
+
0 math:equalTo 0.
|
|
51
|
+
}.
|
|
52
|
+
|
|
53
|
+
# tower(base, H>0) = base ^ tower(base, H-1)
|
|
54
|
+
{
|
|
55
|
+
(?B ?H) :tower ?R.
|
|
56
|
+
} <= {
|
|
57
|
+
?H math:greaterThan 0.
|
|
58
|
+
(?H 1) math:difference ?H1.
|
|
59
|
+
(?B ?H1) :tower ?Prev.
|
|
60
|
+
(?B ?Prev) math:exponentiation ?R.
|
|
61
|
+
}.
|
|
62
|
+
|
|
63
|
+
# -----------------------------------------
|
|
64
|
+
# “Projection” helpers to keep output small
|
|
65
|
+
# -----------------------------------------
|
|
66
|
+
# powMod1e6(base, exp) = (base^exp) mod 1,000,000
|
|
67
|
+
{
|
|
68
|
+
(?B ?E) :powMod1e6 ?M.
|
|
69
|
+
} <= {
|
|
70
|
+
(?B ?E) :pow ?R.
|
|
71
|
+
(?R 1000000) math:remainder ?M.
|
|
72
|
+
}.
|
|
73
|
+
|
|
74
|
+
# towerMod1e6(base, height) = tower(base,height) mod 1,000,000
|
|
75
|
+
{
|
|
76
|
+
(?B ?H) :towerMod1e6 ?M.
|
|
77
|
+
} <= {
|
|
78
|
+
(?B ?H) :tower ?R.
|
|
79
|
+
(?R 1000000) math:remainder ?M.
|
|
80
|
+
}.
|
|
81
|
+
|
|
82
|
+
# ----
|
|
83
|
+
# test
|
|
84
|
+
# ----
|
|
85
|
+
{
|
|
86
|
+
# small sanity: fast and slow should agree
|
|
87
|
+
(2 10) :pow ?P10.
|
|
88
|
+
(2 10) :powSlow ?S10.
|
|
89
|
+
|
|
90
|
+
# big exponent, but only output mod 1e6 (so results stay short)
|
|
91
|
+
(2 10000) :powMod1e6 ?M1.
|
|
92
|
+
(3 10000) :powMod1e6 ?M2.
|
|
93
|
+
|
|
94
|
+
# power tower (2 ↑↑ 4 = 65536), and a slightly larger one modulo 1e6
|
|
95
|
+
(2 4) :tower ?T4.
|
|
96
|
+
(2 5) :towerMod1e6 ?TM5.
|
|
97
|
+
|
|
98
|
+
# WARNING: uncommenting this will be slow / memory-hungry (repeat multiply)
|
|
99
|
+
# (2 10000) :powSlow ?Boom.
|
|
100
|
+
}
|
|
101
|
+
=>
|
|
102
|
+
{
|
|
103
|
+
:test :is {
|
|
104
|
+
(2 10) :pow ?P10.
|
|
105
|
+
(2 10) :powSlow ?S10.
|
|
106
|
+
(2 10000) :powMod1e6 ?M1.
|
|
107
|
+
(3 10000) :powMod1e6 ?M2.
|
|
108
|
+
(2 4) :tower ?T4.
|
|
109
|
+
(2 5) :towerMod1e6 ?TM5.
|
|
110
|
+
}.
|
|
111
|
+
}.
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
# ==========================================================================================
|
|
2
2
|
# Genetic algorithm demo
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
# - Start with a random candidate string of the same length as the target.
|
|
6
|
-
# - Repeatedly create a batch of “children” by copying the current best string and randomly
|
|
7
|
-
# mutating each character with a small probability.
|
|
8
|
-
# - Score each child by how many positions differ from the target (Hamming distance).
|
|
9
|
-
# - Keep the best-scoring child as the new current string and iterate until the score is 0.
|
|
10
|
-
# This is a simple hill-climbing GA (selection + mutation, no crossover), using a seeded RNG
|
|
11
|
-
# so runs are reproducible.
|
|
4
|
+
# We "evolve" a genome (a bitstring) that encodes a target integer.
|
|
12
5
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
6
|
+
# - Genome: a string of '0'/'1' bits (same length as :weights).
|
|
7
|
+
# - Phenotype: sum of selected weights where the genome bit is '1'.
|
|
8
|
+
# - Fitness: remaining distance to the target, with a heavy penalty for overshoot:
|
|
9
|
+
# if sum <= target: fitness = target - sum
|
|
10
|
+
# if sum > target: fitness = 1_000_000 + (sum - target)
|
|
11
|
+
# - Variation: single-point mutation (flip one bit).
|
|
12
|
+
# - Selection: pick the candidate with the lowest fitness (ties keep the earlier candidate).
|
|
16
13
|
#
|
|
14
|
+
# With powers-of-two weights, this deterministic hill-climbing GA converges quickly.
|
|
17
15
|
# ==========================================================================================
|
|
18
16
|
|
|
19
17
|
@prefix : <urn:ga:>.
|
|
@@ -23,155 +21,200 @@
|
|
|
23
21
|
@prefix string: <http://www.w3.org/2000/10/swap/string#>.
|
|
24
22
|
|
|
25
23
|
:cfg
|
|
26
|
-
:
|
|
27
|
-
:
|
|
28
|
-
:seed 100;
|
|
24
|
+
:target 2026;
|
|
25
|
+
:maxGenerations 64;
|
|
29
26
|
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
# weights align with bit positions (index 0 is the left-most bit):
|
|
28
|
+
# genome = b0 b1 ... b10
|
|
29
|
+
# value = b0*1024 + b1*512 + ... + b10*1
|
|
30
|
+
:weights (1024 512 256 128 64 32 16 8 4 2 1).
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
# -------------
|
|
33
|
+
# small helpers
|
|
34
|
+
# -------------
|
|
36
35
|
|
|
37
|
-
# ------------------------
|
|
38
|
-
# Small arithmetic helpers
|
|
39
|
-
# ------------------------
|
|
40
|
-
|
|
41
|
-
{ ( ?N 1 ) :dec ?N1 } <= { ( ?N 1 ) math:difference ?N1 }.
|
|
42
36
|
{ ( ?N 1 ) :inc ?N1 } <= { ( ?N 1 ) math:sum ?N1 }.
|
|
43
37
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
# build a string of N zeros
|
|
39
|
+
{ ( 0 ) :zeros "" } <= true.
|
|
40
|
+
|
|
41
|
+
{ ( ?N ) :zeros ?S } <= {
|
|
42
|
+
?N math:greaterThan 0 .
|
|
43
|
+
( ?N 1 ) math:difference ?N1 .
|
|
44
|
+
( ?N1 ) :zeros ?Rest .
|
|
45
|
+
( "%s%s" "0" ?Rest ) string:format ?S
|
|
46
|
+
}.
|
|
47
|
+
|
|
48
|
+
# flip a bit at index i in a 0/1 string
|
|
49
|
+
{ ( ?S ?I ) :flipBit ?Out } <= {
|
|
50
|
+
( ?S ?I ) string:charAt "0" .
|
|
51
|
+
( ?S ?I "1" ) string:setCharAt ?Out
|
|
52
|
+
}.
|
|
53
|
+
|
|
54
|
+
{ ( ?S ?I ) :flipBit ?Out } <= {
|
|
55
|
+
( ?S ?I ) string:charAt "1" .
|
|
56
|
+
( ?S ?I "0" ) string:setCharAt ?Out
|
|
57
|
+
}.
|
|
58
|
+
|
|
59
|
+
# generate all single-bit mutants of S: [flip(0), flip(1), ..., flip(len-1)]
|
|
60
|
+
{ ( ?S ?Len ) :mutants ?Out } <= {
|
|
61
|
+
( ?S 0 ?Len ) :mutantsFrom ?Out
|
|
62
|
+
}.
|
|
63
|
+
|
|
64
|
+
{ ( ?S ?I ?Len ) :mutantsFrom () } <= {
|
|
65
|
+
?I math:equalTo ?Len
|
|
66
|
+
}.
|
|
67
|
+
|
|
68
|
+
{ ( ?S ?I ?Len ) :mutantsFrom ?Out } <= {
|
|
69
|
+
?I math:lessThan ?Len .
|
|
70
|
+
( ?S ?I ) :flipBit ?Cand .
|
|
71
|
+
( ?I 1 ) :inc ?I1 .
|
|
72
|
+
( ?S ?I1 ?Len ) :mutantsFrom ?Rest .
|
|
73
|
+
( ( ?Cand ) ?Rest ) list:append ?Out
|
|
74
|
+
}.
|
|
75
|
+
|
|
76
|
+
# ------------------------
|
|
77
|
+
# decode genome -> integer
|
|
78
|
+
# ------------------------
|
|
48
79
|
|
|
49
|
-
{ ?
|
|
50
|
-
|
|
51
|
-
( ?
|
|
52
|
-
( ?T 2147483648 ) math:remainder ?S1
|
|
80
|
+
{ ( ?Bits ?Weights ) :value ?Sum } <= {
|
|
81
|
+
?Bits string:length ?Len .
|
|
82
|
+
( ?Bits ?Weights 0 ?Len 0 ) :valueAcc ?Sum
|
|
53
83
|
}.
|
|
54
84
|
|
|
55
|
-
{ ( ?
|
|
56
|
-
?
|
|
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
|
|
85
|
+
{ ( ?Bits ?Weights ?I ?Len ?Acc ) :valueAcc ?Acc } <= {
|
|
86
|
+
?I math:equalTo ?Len
|
|
62
87
|
}.
|
|
63
88
|
|
|
64
|
-
{ ?
|
|
65
|
-
|
|
66
|
-
( ?
|
|
67
|
-
|
|
89
|
+
{ ( ?Bits ?Weights ?I ?Len ?Acc ) :valueAcc ?Sum } <= {
|
|
90
|
+
?I math:lessThan ?Len .
|
|
91
|
+
( ?Weights ?I ) list:memberAt ?W .
|
|
92
|
+
|
|
93
|
+
( ?Bits ?I ) string:charAt "1" .
|
|
94
|
+
( ?Acc ?W ) math:sum ?Acc1 .
|
|
95
|
+
( ?I 1 ) :inc ?I1 .
|
|
96
|
+
( ?Bits ?Weights ?I1 ?Len ?Acc1 ) :valueAcc ?Sum
|
|
68
97
|
}.
|
|
69
98
|
|
|
70
|
-
{ (
|
|
99
|
+
{ ( ?Bits ?Weights ?I ?Len ?Acc ) :valueAcc ?Sum } <= {
|
|
100
|
+
?I math:lessThan ?Len .
|
|
101
|
+
( ?Weights ?I ) list:memberAt ?W .
|
|
71
102
|
|
|
72
|
-
|
|
73
|
-
?
|
|
74
|
-
( ?Len
|
|
75
|
-
?S0 :randomAlpha ( ?Ch ?S1 ) .
|
|
76
|
-
( ?Len1 ?S1 ) :randomText ( ?Rest ?S2 ) .
|
|
77
|
-
( "%s%s" ?Ch ?Rest ) string:format ?Out
|
|
103
|
+
( ?Bits ?I ) string:charAt "0" .
|
|
104
|
+
( ?I 1 ) :inc ?I1 .
|
|
105
|
+
( ?Bits ?Weights ?I1 ?Len ?Acc ) :valueAcc ?Sum
|
|
78
106
|
}.
|
|
79
107
|
|
|
80
108
|
# -------------------------
|
|
81
|
-
#
|
|
109
|
+
# fitness (lower is better)
|
|
82
110
|
# -------------------------
|
|
83
111
|
|
|
84
|
-
{ (
|
|
112
|
+
{ ( ?Sum ?Target ) :fitness ?F } <= {
|
|
113
|
+
?Sum math:notGreaterThan ?Target .
|
|
114
|
+
( ?Target ?Sum ) math:difference ?F
|
|
115
|
+
}.
|
|
116
|
+
|
|
117
|
+
{ ( ?Sum ?Target ) :fitness ?F } <= {
|
|
118
|
+
?Sum math:greaterThan ?Target .
|
|
119
|
+
( ?Sum ?Target ) math:difference ?Over .
|
|
120
|
+
( 1000000 ?Over ) math:sum ?F
|
|
121
|
+
}.
|
|
122
|
+
|
|
123
|
+
{ ( ?Bits ?Target ?Weights ) :candidateScore ( ?Fit ?Sum ) } <= {
|
|
124
|
+
( ?Bits ?Weights ) :value ?Sum .
|
|
125
|
+
( ?Sum ?Target ) :fitness ?Fit
|
|
126
|
+
}.
|
|
127
|
+
|
|
128
|
+
# pick best of two candidates by fitness (ties keep the left candidate)
|
|
129
|
+
{ ( ?C1 ?S1 ?Sum1 ?C2 ?S2 ?Sum2 ) :pickBest ( ?C1 ?S1 ?Sum1 ) } <= {
|
|
130
|
+
?S1 math:notGreaterThan ?S2
|
|
131
|
+
}.
|
|
132
|
+
|
|
133
|
+
{ ( ?C1 ?S1 ?Sum1 ?C2 ?S2 ?Sum2 ) :pickBest ( ?C2 ?S2 ?Sum2 ) } <= {
|
|
134
|
+
?S1 math:greaterThan ?S2
|
|
135
|
+
}.
|
|
136
|
+
|
|
137
|
+
# best candidate from a list
|
|
138
|
+
{ ( ?Target ?Weights ?L ) :bestMutant ( ?Best ?BestScore ?BestSum ) } <= {
|
|
139
|
+
?L list:length 1 .
|
|
140
|
+
?L list:first ?Best .
|
|
141
|
+
( ?Best ?Target ?Weights ) :candidateScore ( ?BestScore ?BestSum )
|
|
142
|
+
}.
|
|
143
|
+
|
|
144
|
+
{ ( ?Target ?Weights ?L ) :bestMutant ( ?Best ?BestScore ?BestSum ) } <= {
|
|
145
|
+
?L list:length ?N .
|
|
146
|
+
?N math:greaterThan 1 .
|
|
147
|
+
|
|
148
|
+
?L list:first ?H .
|
|
149
|
+
?L list:rest ?T .
|
|
150
|
+
|
|
151
|
+
( ?Target ?Weights ?T ) :bestMutant ( ?B2 ?S2 ?Sum2 ) .
|
|
152
|
+
( ?H ?Target ?Weights ) :candidateScore ( ?S1 ?Sum1 ) .
|
|
85
153
|
|
|
86
|
-
|
|
87
|
-
?Every math:greaterThan 0 .
|
|
88
|
-
( ?Gen ?Every ) math:remainder ?R .
|
|
89
|
-
?R math:equalTo 0 .
|
|
90
|
-
?Gen log:trace ( ?Score ?Value )
|
|
154
|
+
( ?H ?S1 ?Sum1 ?B2 ?S2 ?Sum2 ) :pickBest ( ?Best ?BestScore ?BestSum )
|
|
91
155
|
}.
|
|
92
156
|
|
|
93
157
|
# --------------
|
|
94
158
|
# evolution loop
|
|
95
159
|
# --------------
|
|
96
160
|
|
|
97
|
-
#
|
|
98
|
-
{ ( ?
|
|
99
|
-
|
|
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
|
|
161
|
+
# solved
|
|
162
|
+
{ ( ?Bits ?Target ?Weights ?Gen ?MaxGen ) :evolve ( ?Bits ?Gen ) } <= {
|
|
163
|
+
( ?Bits ?Target ?Weights ) :candidateScore ( 0 ?Sum )
|
|
107
164
|
}.
|
|
108
165
|
|
|
109
|
-
#
|
|
110
|
-
{ ( ?
|
|
111
|
-
|
|
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 )
|
|
166
|
+
# generation cap (safety)
|
|
167
|
+
{ ( ?Bits ?Target ?Weights ?Gen ?MaxGen ) :evolve ( ?Bits ?Gen ) } <= {
|
|
168
|
+
?Gen math:equalTo ?MaxGen
|
|
120
169
|
}.
|
|
121
170
|
|
|
122
|
-
#
|
|
123
|
-
{ ( ?
|
|
124
|
-
:evolve ( ?Final ?FinalGen ?S2 ) } <= {
|
|
125
|
-
?Score math:notEqualTo 0 .
|
|
126
|
-
?MaxGen math:greaterThan 0 .
|
|
171
|
+
# stop if best doesn't improve
|
|
172
|
+
{ ( ?Bits ?Target ?Weights ?Gen ?MaxGen ) :evolve ( ?Bits ?Gen ) } <= {
|
|
127
173
|
?Gen math:lessThan ?MaxGen .
|
|
128
|
-
( ?
|
|
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
|
-
}.
|
|
174
|
+
( ?Bits ?Target ?Weights ) :candidateScore ( ?Score ?Sum ) .
|
|
136
175
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
176
|
+
?Bits string:length ?Len .
|
|
177
|
+
( ?Bits ?Len ) :mutants ?Ms .
|
|
178
|
+
( ( ?Bits ) ?Ms ) list:append ?Candidates .
|
|
140
179
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
:seed ?Seed;
|
|
145
|
-
:traceEvery ?TraceEvery;
|
|
146
|
-
:maxGenerations ?MaxGen.
|
|
147
|
-
|
|
148
|
-
?TargetString string:length ?Len .
|
|
180
|
+
( ?Target ?Weights ?Candidates ) :bestMutant ( ?Best ?BestScore ?BestSum ) .
|
|
181
|
+
?BestScore math:equalTo ?Score
|
|
182
|
+
}.
|
|
149
183
|
|
|
150
|
-
|
|
184
|
+
# keep evolving while improvement exists
|
|
185
|
+
{ ( ?Bits ?Target ?Weights ?Gen ?MaxGen ) :evolve ( ?Final ?FinalGen ) } <= {
|
|
186
|
+
?Gen math:lessThan ?MaxGen .
|
|
187
|
+
( ?Bits ?Target ?Weights ) :candidateScore ( ?Score ?Sum ) .
|
|
151
188
|
|
|
152
|
-
|
|
153
|
-
( ?
|
|
189
|
+
?Bits string:length ?Len .
|
|
190
|
+
( ?Bits ?Len ) :mutants ?Ms .
|
|
191
|
+
( ( ?Bits ) ?Ms ) list:append ?Candidates .
|
|
154
192
|
|
|
155
|
-
( ?
|
|
156
|
-
|
|
157
|
-
( ?Value ?Gen ?S2 ) .
|
|
193
|
+
( ?Target ?Weights ?Candidates ) :bestMutant ( ?Best ?BestScore ?BestSum ) .
|
|
194
|
+
?BestScore math:notEqualTo ?Score .
|
|
158
195
|
|
|
159
|
-
|
|
160
|
-
( ?
|
|
161
|
-
?Value log:equalTo ?TargetString .
|
|
162
|
-
?S2 log:equalTo ?_FinalSeedState
|
|
196
|
+
( ?Gen 1 ) :inc ?Gen1 .
|
|
197
|
+
( ?Best ?Target ?Weights ?Gen1 ?MaxGen ) :evolve ( ?Final ?FinalGen )
|
|
163
198
|
}.
|
|
164
199
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
200
|
+
# --------------
|
|
201
|
+
# query / output
|
|
202
|
+
# --------------
|
|
203
|
+
|
|
204
|
+
{
|
|
205
|
+
:cfg :target ?T;
|
|
206
|
+
:maxGenerations ?Max;
|
|
207
|
+
:weights ?W.
|
|
208
|
+
|
|
209
|
+
?W list:length ?Len .
|
|
210
|
+
( ?Len ) :zeros ?Start .
|
|
168
211
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
212
|
+
( ?Start ?T ?W 0 ?Max ) :evolve ( ?Best ?Gen ) .
|
|
213
|
+
( ?Best ?W ) :value ?Sum .
|
|
214
|
+
?Sum math:equalTo ?T .
|
|
172
215
|
|
|
173
|
-
|
|
174
|
-
|
|
216
|
+
( "GA evolved %s -> %s in %s generations.\n" ?Best ?Sum ?Gen )
|
|
217
|
+
string:format ?Line
|
|
175
218
|
}
|
|
176
219
|
log:query
|
|
177
220
|
{ 1 log:outputString ?Line }.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# ===============================================
|
|
2
|
+
# Fast modular exponentiation (repeated squaring)
|
|
3
|
+
# ===============================================
|
|
4
|
+
|
|
5
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
6
|
+
@prefix : <https://eyereasoner.github.io/ns#>.
|
|
7
|
+
|
|
8
|
+
# (a * b) mod m
|
|
9
|
+
{
|
|
10
|
+
(?A ?B ?M) :modMul ?R.
|
|
11
|
+
} <= {
|
|
12
|
+
(?A ?B) math:product ?P.
|
|
13
|
+
(?P ?M) math:remainder ?R.
|
|
14
|
+
}.
|
|
15
|
+
|
|
16
|
+
# modPow(b, 0, m) = 1 mod m
|
|
17
|
+
{
|
|
18
|
+
(?B 0 ?M) :modPow ?R.
|
|
19
|
+
} <= {
|
|
20
|
+
?M math:greaterThan 0.
|
|
21
|
+
(1 ?M) math:remainder ?R.
|
|
22
|
+
}.
|
|
23
|
+
|
|
24
|
+
# modPow(b, 1, m) = b mod m
|
|
25
|
+
{
|
|
26
|
+
(?B 1 ?M) :modPow ?R.
|
|
27
|
+
} <= {
|
|
28
|
+
?M math:greaterThan 0.
|
|
29
|
+
(?B ?M) math:remainder ?R.
|
|
30
|
+
}.
|
|
31
|
+
|
|
32
|
+
# even exponent: modPow(b, e, m) = modPow((b*b mod m), e/2, m)
|
|
33
|
+
{
|
|
34
|
+
(?B ?E ?M) :modPow ?R.
|
|
35
|
+
} <= {
|
|
36
|
+
?M math:greaterThan 0.
|
|
37
|
+
?E math:greaterThan 1.
|
|
38
|
+
(?E 2) math:remainder 0.
|
|
39
|
+
|
|
40
|
+
(?B ?B ?M) :modMul ?B2.
|
|
41
|
+
(?E 2) math:integerQuotient ?Half.
|
|
42
|
+
(?B2 ?Half ?M) :modPow ?R.
|
|
43
|
+
}.
|
|
44
|
+
|
|
45
|
+
# odd exponent: modPow(b, e, m) = (b * modPow((b*b mod m), (e-1)/2, m)) mod m
|
|
46
|
+
{
|
|
47
|
+
(?B ?E ?M) :modPow ?R.
|
|
48
|
+
} <= {
|
|
49
|
+
?M math:greaterThan 0.
|
|
50
|
+
?E math:greaterThan 1.
|
|
51
|
+
(?E 2) math:remainder 1.
|
|
52
|
+
|
|
53
|
+
(?B ?B ?M) :modMul ?B2.
|
|
54
|
+
(?E 1) math:difference ?E1.
|
|
55
|
+
(?E1 2) math:integerQuotient ?Half.
|
|
56
|
+
|
|
57
|
+
(?B2 ?Half ?M) :modPow ?T.
|
|
58
|
+
(?B ?T ?M) :modMul ?R.
|
|
59
|
+
}.
|
|
60
|
+
|
|
61
|
+
# -----------------
|
|
62
|
+
# tests / showcases
|
|
63
|
+
# -----------------
|
|
64
|
+
{
|
|
65
|
+
# sanity check against a small naive computation:
|
|
66
|
+
(7 13) math:exponentiation ?PowSmall.
|
|
67
|
+
(?PowSmall 97) math:remainder ?NaiveSmall.
|
|
68
|
+
(7 13 97) :modPow ?FastSmall.
|
|
69
|
+
|
|
70
|
+
# “impressive” case: 7^1,000,000,000 mod 1,000,000,007
|
|
71
|
+
(7 1000000000 1000000007) :modPow ?R1.
|
|
72
|
+
|
|
73
|
+
# exponent itself computed via exponentiation: 2^25 = 33,554,432
|
|
74
|
+
(2 25) math:exponentiation ?E25.
|
|
75
|
+
(3 ?E25 1000000007) :modPow ?R2.
|
|
76
|
+
|
|
77
|
+
# last 12 digits of 2^(2^20)
|
|
78
|
+
(2 20) math:exponentiation ?E20.
|
|
79
|
+
(2 ?E20 1000000000000) :modPow ?Last12.
|
|
80
|
+
}
|
|
81
|
+
=>
|
|
82
|
+
{
|
|
83
|
+
:test :smallCheck [ :naive ?NaiveSmall; :fast ?FastSmall ].
|
|
84
|
+
:test :sevenPow1e9Mod1e9p7 ?R1.
|
|
85
|
+
:test :threePow2Pow25Mod1e9p7 ?R2.
|
|
86
|
+
:test :last12DigitsOf2Pow2Pow20 ?Last12.
|
|
87
|
+
}.
|
package/eyeling-builtins.ttl
CHANGED
|
@@ -346,5 +346,3 @@ 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
|
-
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
|
@@ -3610,89 +3610,6 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3610
3610
|
return s2 !== null ? [s2] : [];
|
|
3611
3611
|
}
|
|
3612
3612
|
|
|
3613
|
-
// string:mutateSelectBest
|
|
3614
|
-
// Schema:
|
|
3615
|
-
// ( $samples $current $target $mutProb $seedState )
|
|
3616
|
-
// string:mutateSelectBest
|
|
3617
|
-
// ( $best $bestScore $seedState2 )
|
|
3618
|
-
//
|
|
3619
|
-
// Deterministic 31-bit LCG (same as the GA demo):
|
|
3620
|
-
// state = (1103515245 * state + 12345) % 2^31
|
|
3621
|
-
// and randRound(N) = Math.round((state/2^31) * N) with state advanced once per draw.
|
|
3622
|
-
if (pv === STRING_NS + 'mutateSelectBest') {
|
|
3623
|
-
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 5) return [];
|
|
3624
|
-
|
|
3625
|
-
const samplesNum = parseNum(g.s.elems[0]);
|
|
3626
|
-
const current = termToJsXsdStringNoLang(g.s.elems[1]);
|
|
3627
|
-
const target = termToJsXsdStringNoLang(g.s.elems[2]);
|
|
3628
|
-
const mutProbNum = parseNum(g.s.elems[3]);
|
|
3629
|
-
const seedNum = parseNum(g.s.elems[4]);
|
|
3630
|
-
|
|
3631
|
-
if (samplesNum === null || current === null || target === null || mutProbNum === null || seedNum === null)
|
|
3632
|
-
return [];
|
|
3633
|
-
if (current.length !== target.length) return [];
|
|
3634
|
-
|
|
3635
|
-
const samples = Math.max(1, Math.trunc(samplesNum));
|
|
3636
|
-
const mutProb = Math.max(0, Math.trunc(mutProbNum));
|
|
3637
|
-
const seed0 = Math.trunc(seedNum);
|
|
3638
|
-
|
|
3639
|
-
let state = BigInt(seed0);
|
|
3640
|
-
const MOD = 2147483648n; // 2^31
|
|
3641
|
-
const A = 1103515245n;
|
|
3642
|
-
const C = 12345n;
|
|
3643
|
-
|
|
3644
|
-
function rnd() {
|
|
3645
|
-
state = (A * state + C) % MOD;
|
|
3646
|
-
return Number(state) / 2147483648;
|
|
3647
|
-
}
|
|
3648
|
-
|
|
3649
|
-
function randRound(N) {
|
|
3650
|
-
return Math.round(rnd() * N);
|
|
3651
|
-
}
|
|
3652
|
-
|
|
3653
|
-
function randomAlpha() {
|
|
3654
|
-
const p = randRound(26);
|
|
3655
|
-
return p === 0 ? ' ' : String.fromCharCode(64 + p);
|
|
3656
|
-
}
|
|
3657
|
-
|
|
3658
|
-
function mutate(str) {
|
|
3659
|
-
let out = '';
|
|
3660
|
-
for (let i = 0; i < str.length; i++) {
|
|
3661
|
-
const p = randRound(100);
|
|
3662
|
-
if (p > mutProb) out += str[i];
|
|
3663
|
-
else out += randomAlpha();
|
|
3664
|
-
}
|
|
3665
|
-
return out;
|
|
3666
|
-
}
|
|
3667
|
-
|
|
3668
|
-
function score(str) {
|
|
3669
|
-
let diffs = 0;
|
|
3670
|
-
for (let i = 0; i < target.length; i++) if (str[i] !== target[i]) diffs++;
|
|
3671
|
-
return diffs;
|
|
3672
|
-
}
|
|
3673
|
-
|
|
3674
|
-
let best = '';
|
|
3675
|
-
let bestScore = Infinity;
|
|
3676
|
-
|
|
3677
|
-
for (let i = 0; i < samples; i++) {
|
|
3678
|
-
const cand = mutate(current);
|
|
3679
|
-
const candScore = score(cand);
|
|
3680
|
-
if (candScore < bestScore) {
|
|
3681
|
-
best = cand;
|
|
3682
|
-
bestScore = candScore;
|
|
3683
|
-
}
|
|
3684
|
-
}
|
|
3685
|
-
|
|
3686
|
-
const outList = new ListTerm([
|
|
3687
|
-
makeStringLiteral(best),
|
|
3688
|
-
internLiteral(String(bestScore)),
|
|
3689
|
-
internLiteral(state.toString()),
|
|
3690
|
-
]);
|
|
3691
|
-
|
|
3692
|
-
const s2 = unifyTerm(g.o, outList, subst);
|
|
3693
|
-
return s2 !== null ? [s2] : [];
|
|
3694
|
-
}
|
|
3695
|
-
|
|
3696
3613
|
// Unknown builtin
|
|
3697
3614
|
return [];
|
|
3698
3615
|
}
|
|
@@ -3962,7 +3879,7 @@ function main() {
|
|
|
3962
3879
|
` -e, --enforce-https Rewrite http:// IRIs to https:// for log dereferencing builtins.\n` +
|
|
3963
3880
|
` -h, --help Show this help and exit.\n` +
|
|
3964
3881
|
` -p, --proof-comments Enable proof explanations.\n` +
|
|
3965
|
-
` -r, --strings Print log:outputString strings (ordered by key)
|
|
3882
|
+
` -r, --strings Print log:outputString strings (ordered by key), including via log:query.\n` +
|
|
3966
3883
|
` -s, --super-restricted Disable all builtins except => and <=.\n` +
|
|
3967
3884
|
` -t, --stream Stream derived triples as soon as they are derived.\n` +
|
|
3968
3885
|
` -v, --version Print version and exit.\n`;
|
|
@@ -4072,8 +3989,21 @@ function main() {
|
|
|
4072
3989
|
// If requested, print log:outputString values (ordered by subject key) and exit.
|
|
4073
3990
|
// Note: log:outputString values may depend on derived facts, so we must saturate first.
|
|
4074
3991
|
if (outputStringsMode) {
|
|
4075
|
-
|
|
4076
|
-
|
|
3992
|
+
const hasQueries = Array.isArray(qrules) && qrules.length;
|
|
3993
|
+
|
|
3994
|
+
// If log:query directives are present, the intended output may not be part of the
|
|
3995
|
+
// saturated fact store (queries are output-selection statements). In that case,
|
|
3996
|
+
// collect log:outputString triples from the instantiated query conclusions.
|
|
3997
|
+
let outTriples;
|
|
3998
|
+
if (hasQueries) {
|
|
3999
|
+
const res = engine.forwardChainAndCollectLogQueryConclusions(facts, frules, brules, qrules);
|
|
4000
|
+
outTriples = res.queryTriples;
|
|
4001
|
+
} else {
|
|
4002
|
+
engine.forwardChain(facts, frules, brules);
|
|
4003
|
+
outTriples = facts;
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
const out = engine.collectOutputStringsFromFacts(outTriples, prefixes);
|
|
4077
4007
|
if (out) process.stdout.write(out);
|
|
4078
4008
|
process.exit(0);
|
|
4079
4009
|
}
|
package/lib/builtins.js
CHANGED
|
@@ -3598,89 +3598,6 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3598
3598
|
return s2 !== null ? [s2] : [];
|
|
3599
3599
|
}
|
|
3600
3600
|
|
|
3601
|
-
// string:mutateSelectBest
|
|
3602
|
-
// Schema:
|
|
3603
|
-
// ( $samples $current $target $mutProb $seedState )
|
|
3604
|
-
// string:mutateSelectBest
|
|
3605
|
-
// ( $best $bestScore $seedState2 )
|
|
3606
|
-
//
|
|
3607
|
-
// Deterministic 31-bit LCG (same as the GA demo):
|
|
3608
|
-
// state = (1103515245 * state + 12345) % 2^31
|
|
3609
|
-
// and randRound(N) = Math.round((state/2^31) * N) with state advanced once per draw.
|
|
3610
|
-
if (pv === STRING_NS + 'mutateSelectBest') {
|
|
3611
|
-
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 5) return [];
|
|
3612
|
-
|
|
3613
|
-
const samplesNum = parseNum(g.s.elems[0]);
|
|
3614
|
-
const current = termToJsXsdStringNoLang(g.s.elems[1]);
|
|
3615
|
-
const target = termToJsXsdStringNoLang(g.s.elems[2]);
|
|
3616
|
-
const mutProbNum = parseNum(g.s.elems[3]);
|
|
3617
|
-
const seedNum = parseNum(g.s.elems[4]);
|
|
3618
|
-
|
|
3619
|
-
if (samplesNum === null || current === null || target === null || mutProbNum === null || seedNum === null)
|
|
3620
|
-
return [];
|
|
3621
|
-
if (current.length !== target.length) return [];
|
|
3622
|
-
|
|
3623
|
-
const samples = Math.max(1, Math.trunc(samplesNum));
|
|
3624
|
-
const mutProb = Math.max(0, Math.trunc(mutProbNum));
|
|
3625
|
-
const seed0 = Math.trunc(seedNum);
|
|
3626
|
-
|
|
3627
|
-
let state = BigInt(seed0);
|
|
3628
|
-
const MOD = 2147483648n; // 2^31
|
|
3629
|
-
const A = 1103515245n;
|
|
3630
|
-
const C = 12345n;
|
|
3631
|
-
|
|
3632
|
-
function rnd() {
|
|
3633
|
-
state = (A * state + C) % MOD;
|
|
3634
|
-
return Number(state) / 2147483648;
|
|
3635
|
-
}
|
|
3636
|
-
|
|
3637
|
-
function randRound(N) {
|
|
3638
|
-
return Math.round(rnd() * N);
|
|
3639
|
-
}
|
|
3640
|
-
|
|
3641
|
-
function randomAlpha() {
|
|
3642
|
-
const p = randRound(26);
|
|
3643
|
-
return p === 0 ? ' ' : String.fromCharCode(64 + p);
|
|
3644
|
-
}
|
|
3645
|
-
|
|
3646
|
-
function mutate(str) {
|
|
3647
|
-
let out = '';
|
|
3648
|
-
for (let i = 0; i < str.length; i++) {
|
|
3649
|
-
const p = randRound(100);
|
|
3650
|
-
if (p > mutProb) out += str[i];
|
|
3651
|
-
else out += randomAlpha();
|
|
3652
|
-
}
|
|
3653
|
-
return out;
|
|
3654
|
-
}
|
|
3655
|
-
|
|
3656
|
-
function score(str) {
|
|
3657
|
-
let diffs = 0;
|
|
3658
|
-
for (let i = 0; i < target.length; i++) if (str[i] !== target[i]) diffs++;
|
|
3659
|
-
return diffs;
|
|
3660
|
-
}
|
|
3661
|
-
|
|
3662
|
-
let best = '';
|
|
3663
|
-
let bestScore = Infinity;
|
|
3664
|
-
|
|
3665
|
-
for (let i = 0; i < samples; i++) {
|
|
3666
|
-
const cand = mutate(current);
|
|
3667
|
-
const candScore = score(cand);
|
|
3668
|
-
if (candScore < bestScore) {
|
|
3669
|
-
best = cand;
|
|
3670
|
-
bestScore = candScore;
|
|
3671
|
-
}
|
|
3672
|
-
}
|
|
3673
|
-
|
|
3674
|
-
const outList = new ListTerm([
|
|
3675
|
-
makeStringLiteral(best),
|
|
3676
|
-
internLiteral(String(bestScore)),
|
|
3677
|
-
internLiteral(state.toString()),
|
|
3678
|
-
]);
|
|
3679
|
-
|
|
3680
|
-
const s2 = unifyTerm(g.o, outList, subst);
|
|
3681
|
-
return s2 !== null ? [s2] : [];
|
|
3682
|
-
}
|
|
3683
|
-
|
|
3684
3601
|
// Unknown builtin
|
|
3685
3602
|
return [];
|
|
3686
3603
|
}
|
package/lib/cli.js
CHANGED
|
@@ -71,7 +71,7 @@ function main() {
|
|
|
71
71
|
` -e, --enforce-https Rewrite http:// IRIs to https:// for log dereferencing builtins.\n` +
|
|
72
72
|
` -h, --help Show this help and exit.\n` +
|
|
73
73
|
` -p, --proof-comments Enable proof explanations.\n` +
|
|
74
|
-
` -r, --strings Print log:outputString strings (ordered by key)
|
|
74
|
+
` -r, --strings Print log:outputString strings (ordered by key), including via log:query.\n` +
|
|
75
75
|
` -s, --super-restricted Disable all builtins except => and <=.\n` +
|
|
76
76
|
` -t, --stream Stream derived triples as soon as they are derived.\n` +
|
|
77
77
|
` -v, --version Print version and exit.\n`;
|
|
@@ -181,8 +181,21 @@ function main() {
|
|
|
181
181
|
// If requested, print log:outputString values (ordered by subject key) and exit.
|
|
182
182
|
// Note: log:outputString values may depend on derived facts, so we must saturate first.
|
|
183
183
|
if (outputStringsMode) {
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
const hasQueries = Array.isArray(qrules) && qrules.length;
|
|
185
|
+
|
|
186
|
+
// If log:query directives are present, the intended output may not be part of the
|
|
187
|
+
// saturated fact store (queries are output-selection statements). In that case,
|
|
188
|
+
// collect log:outputString triples from the instantiated query conclusions.
|
|
189
|
+
let outTriples;
|
|
190
|
+
if (hasQueries) {
|
|
191
|
+
const res = engine.forwardChainAndCollectLogQueryConclusions(facts, frules, brules, qrules);
|
|
192
|
+
outTriples = res.queryTriples;
|
|
193
|
+
} else {
|
|
194
|
+
engine.forwardChain(facts, frules, brules);
|
|
195
|
+
outTriples = facts;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const out = engine.collectOutputStringsFromFacts(outTriples, prefixes);
|
|
186
199
|
if (out) process.stdout.write(out);
|
|
187
200
|
process.exit(0);
|
|
188
201
|
}
|