Predicting sequences

In this example we first learn four sample integer sequences, Fibonacci, factorial, counting numbers and primes. And then we use the smap() operator to build up a large collection of if-then machines that learn fragments of these sequences. We can then query those if-then machines with an integer sequence fragment (using the similar-input[op] operator), and have it predict or fuzzy-predict which integers come next. Note that with not much work the below could be modified to handle sequences of other types, not just integers. But the hard part would be defining a suitable encoder. Integer encoders are easy, just apply a Gaussian around an integer.

Here is the code:

-- use smap and if-then machines to identify and predict next digits from an integer sequence fragment:
-- updated to the version 3.1.1 language
-- 2021/11/26 fixed a bug that caused a crash when trying to create if then machines.


-- min and max ngram lenghts for the prediction if-then machines:
min |ngram len> => |3>
max |ngram len> => |9>

-- min and max fragment lengths for id-sequence operator:
min |fragment len> => |3>
max |fragment len> => |3>



-- Fibonacci:
fib |0> => |0>
fib |1> => |1>
-- fib |*> !=> arithmetic(fib minus[1] |_self>, |+>, fib minus[2] |_self>) |>
fib |*> !=> fib minus[1] |_self> ++ fib minus[2] |_self>

-- factorial:
fact |0> => |1>
-- fact |*> !=> arithmetic(|_self>, |*>, fact minus[1] |_self>) |>
fact |*> !=> |_self> ** fact minus[1] |_self>


-- now learn our four sequences, counting, Fibonacci, factorial, primes:
print (| > . |Learning our sequences ... >)

int-seq |count> => srange(|1>, |100>) |>
int-seq |fib> => fib srange(|1>, |30>) |>
int-seq |fact> => fact srange(|1>, |15>) |>
int-seq |primes> => such-that[is-prime] srange(|1>, |200>) |>


-- learn what is a digit:
list-of |digits> => |0> + |1> + |2> + |3> + |4> + |5> + |6> + |7> + |8> + |9>
is-digit |*> #=> is-mbr(|_self>) list-of |digits>
is-integer |*> #=> is-subset(clean split |_self>) list-of |digits>


-- define a digit encoder:
-- digit-encoder |*> #=> op-if(is-digit |_self>, |op: Gaussian-0.6>, |op: identity>) |_self>
--
-- define an integer encoder:
-- integer-encoder |*> #=> op-if(is-integer |_self>, |op: Gaussian-1>, |op: identity>) |_self>
--
-- define our helper operators:
-- Gaussian-0.6 |*> #=> Gaussian[0.6] |_self>
-- Gaussian-1 |*> #=> Gaussian[1] |_self>
-- identity |*> #=> |_self>

-- define a digit encoder:
digit-encoder |*> #=>
    if( is-digit |__self> ):
        Gaussian[0.6] |__self>
    else:
        |__self>
    end:

-- define an integer encoder:
integer-encoder |*> #=>
    if( is-integer |__self> ):
        Gaussian[1] |__self>
    else:
        |__self>
    end:

-- define the if-then machine create-rule for the id-sequence code:
create-single-rule (*) #=>
    node |label> => |node:> __ node |number> _ |:> __ node |idx>
    int-seq-pattern node |label> => |__self>
    node |idx> => plus[1] node |idx>


-- define the if-then machine create-rule for the predict-next and fuzzy-predict-next code:
create-next-rules (*) #=>
    node |label> => |node:> __ node |number> _ |:> __ node |idx>
    pattern node |label> => sselect[1,-2] |__self>
    fuzzy-pattern node |label> => integer-encoder sselect[1,-2] |__self>
    next-1 node |label> => sselect[-1,-1] |__self>
    node |idx> => plus[1] node |idx>
    --
    node |label> => |node:> __ node |number> _ |:> __ node |idx>
    pattern node |label> => sselect[1,-3] |__self>
    fuzzy-pattern node |label> => integer-encoder sselect[1,-3] |__self>
    next-2 node |label> => sselect[-2,-1] |__self>
    node |idx> => plus[1] node |idx>
    --
    node |label> => |node:> __ node |number> _ |:> __ node |idx>
    pattern node |label> => sselect[1,-4] |__self>
    fuzzy-pattern node |label> => integer-encoder sselect[1,-4] |__self>
    next-3 node |label> => sselect[-3,-1] |__self>
    node |idx> => plus[1] node |idx>
    --
    node |label> => |node:> __ node |number> _ |:> __ node |idx>
    pattern node |label> => sselect[1,-5] |__self>
    fuzzy-pattern node |label> => integer-encoder sselect[1,-5] |__self>
    next-4 node |label> => sselect[-4,-1] |__self>
    node |idx> => plus[1] node |idx>



-- a helper operator:
-- extract-node-number |*> #=> sselect[2,2] ssplit[": "] |_self>
extract-node-number |*> #=> extract-value extract-category |_self>
not |no> => |yes>
not |yes> => |no>


-- define our if-then machine creation operator:
create-if-then-machine (*,*) #=>
    node |number> => plus[1] clean select[-1,-1] ket-sort extract-node-number rel-kets[then] |>
    if( not do-you-know node |number> ):
        node |number> => |1>
    end:
    node |idx> => |1>
    smap(min |ngram len>, max |ngram len>, |__self1>) |__self0>
    node |label> => |node:> __ node |number> _ |: *>
    then node |label> => |__self2>


-- print out start learning message:
print (| > . |Starting to learn our sequences ... >)


-- now use it to create the next-k if-then machines:
create-if-then-machine(|op: create-next-rules>, |integer sequence: counting>) int-seq |count>
create-if-then-machine(|op: create-next-rules>, |integer sequence: fibonacci>) int-seq |fib>
create-if-then-machine(|op: create-next-rules>, |integer sequence: factorial>) int-seq |fact>
create-if-then-machine(|op: create-next-rules>, |integer sequence: primes>) int-seq |primes>


-- now create the id-sequence if-then machines:
min |ngram len> => |3>
max |ngram len> => |3>

create-if-then-machine(|op: create-single-rule>, |integer sequence: counting>) int-seq |count>
create-if-then-machine(|op: create-single-rule>, |integer sequence: fibonacci>) int-seq |fib>
create-if-then-machine(|op: create-single-rule>, |integer sequence: factorial>) int-seq |fact>
create-if-then-machine(|op: create-single-rule>, |integer sequence: primes>) int-seq |primes>


-- print out finished learning message:
print (|Finished learning> __ extract-value to-comma-number how-many rel-kets[*] |> __ |rules.> . | >)


-- define the id-sequence operator:
simm-pattern (*) #=> then drop-below[0.8] similar-input[int-seq-pattern] |__self>
id-sequence |*> #=> smap(min |fragment len>, max |fragment len>, |op: simm-pattern>) ssplit[" "] |_self>


-- define the predict-next operator:
predict-nodes |*> #=> natural-sort drop-below[0.97] similar-input[pattern] ssplit[" "] |_self>
do-you-know-prediction |*> #=> do-you-know predict-nodes |_self>


predict-next |*> #=>
    unlearn[the] |result>
    the |result> => predict-nodes |__self>
    -- print-result the |result>
    if( do-you-know the |result> ):
        print-next the |result>
    else:
        |Anomaly, no sequence detected ... >
    end:

print-result |#EMPTY#> => |Anomaly, no sequence detected ... >
print-result |*> #=> print-next |_self>


-- define the print-next operators:
print-next-1 |yes> #=> print (extract-value round[1] push-float 100 tmp |var> _ | %     > __ then tmp |var> __ |     pattern:     > _ smerge[" "] pattern tmp |var> _ |      next-1:     > _ smerge[" "] next-1 tmp |var>)
print-next-2 |yes> #=> print (extract-value round[1] push-float 100 tmp |var> _ | %     > __ then tmp |var> __ |     pattern:     > _ smerge[" "] pattern tmp |var> _ |      next-2:     > _ smerge[" "] next-2 tmp |var>)
print-next-3 |yes> #=> print (extract-value round[1] push-float 100 tmp |var> _ | %     > __ then tmp |var> __ |     pattern:     > _ smerge[" "] pattern tmp |var> _ |      next-3:     > _ smerge[" "] next-3 tmp |var>)
print-next-4 |yes> #=> print (extract-value round[1] push-float 100 tmp |var> _ | %     > __ then tmp |var> __ |     pattern:     > _ smerge[" "] pattern tmp |var> _ |      next-4:     > _ smerge[" "] next-4 tmp |var>)


print-next |*> #=>
    tmp |var> => |__self>
    print-next-1 do-you-know next-1 |__self>
    print-next-2 do-you-know next-2 |__self>
    print-next-3 do-you-know next-3 |__self>
    print-next-4 do-you-know next-4 |__self>
    |results>


-- define the fuzzy-predict-next operator:
-- fuzzy-predict version that matches sequences even if they are different lengths:
-- fuzzy-predict-nodes |*> #=> drop-below[0.5] similar-input[fuzzy-pattern] integer-encoder ssplit[" "] |_self>

-- fuzzy-predict version that only matches sequences of exactly the same length:
fuzzy-predict-nodes |*> #=> drop-below[0.5] strict-similar-input[fuzzy-pattern] integer-encoder ssplit[" "] |_self>

do-you-know-fuzzy-prediction |*> #=> do-you-know fuzzy-predict-nodes |_self>

fuzzy-predict-next |*> #=>
    unlearn[the] |result>
    the |result> => fuzzy-predict-nodes |__self>
    -- print-result the |result>
    if( do-you-know the |result> ):
        print-next the |result>
    else:
        |Anomaly, no sequence detected ... >
    end:



print-usage |*> #=>
    print | >
    print |Usage:>
    print |    Split the input sequence on the space char, and identify which sequence it belongs to:>
    print |        id-sequence ket(1 2 3 4)>
    print |        id-sequence ket(2 3 5 8)>
    print | >
    print |    Given a sequence, return matching nodes:>
    print |        predict-nodes ket(2 3 5 8)>
    print | >
    print |    Given a sequence, predict the next elements:>
    print |        predict-next ket(1 2 3)>
    print |        predict-next ket(1 2 3 4 5)>
    print |        predict-next ket(2 3 5 8)>
    print |        predict-next ket(2 6 24)>
    print |        predict-next ket(2 3 5 7)>
    print |        predict-next ket(9 9 9)>
    print | >    
    print |    Given a sequence, test if it is recognized:>
    print |        do-you-know-prediction ket(2 6 24)>
    print |        do-you-know-prediction ket(9 9 9)>
    print | >
    print | >
    print |    Given a sequence, return fuzzy matching nodes:>
    print |        fuzzy-predict-nodes ket(11 12 13 14)>
    print | >
    print |    Given a sequence, fuzzy-predict the next elements:>
    print |        fuzzy-predict-next ket(2 3 5 7 11)>
    print |        fuzzy-predict-next ket(9 9 9)>
    print | >
    print |    Given a sequence, test if it is fuzzy-recognized:>
    print |        do-you-know-fuzzy-prediction ket(9 9 9)>
    print | >


print-usage

Here are some examples of the predict-next operator:

sa: predict-next |1 2 3 4 5>
100 %      integer sequence: counting      pattern:     1 2 3 4 5      next-1:     6
100 %      integer sequence: counting      pattern:     1 2 3 4 5      next-2:     6 7
100 %      integer sequence: counting      pattern:     1 2 3 4 5      next-3:     6 7 8
100 %      integer sequence: counting      pattern:     1 2 3 4 5      next-4:     6 7 8 9
4|results>

sa: predict-next |2 3 5 8>
100 %      integer sequence: fibonacci      pattern:     2 3 5 8      next-1:     13
100 %      integer sequence: fibonacci      pattern:     2 3 5 8      next-2:     13 21
100 %      integer sequence: fibonacci      pattern:     2 3 5 8      next-3:     13 21 34
100 %      integer sequence: fibonacci      pattern:     2 3 5 8      next-4:     13 21 34 55
4|results>

sa: predict-next |2 6 24>
100 %      integer sequence: factorial      pattern:     2 6 24      next-1:     120
100 %      integer sequence: factorial      pattern:     2 6 24      next-2:     120 720
100 %      integer sequence: factorial      pattern:     2 6 24      next-3:     120 720 5040
100 %      integer sequence: factorial      pattern:     2 6 24      next-4:     120 720 5040 40320
4|results>

sa: predict-next |2 3 5 7>
100 %      integer sequence: primes      pattern:     2 3 5 7      next-1:     11
100 %      integer sequence: primes      pattern:     2 3 5 7      next-2:     11 13
100 %      integer sequence: primes      pattern:     2 3 5 7      next-3:     11 13 17
100 %      integer sequence: primes      pattern:     2 3 5 7      next-4:     11 13 17 19
4|results>

sa: predict-next |9 9 9>
|Anomaly, no sequence detected ... >

Here are some examples of the fuzzy-predict-next operator:

sa: fuzzy-predict-next |2 3 5 7 11>
100 %      integer sequence: primes      pattern:     2 3 5 7 11      next-1:     13
100 %      integer sequence: primes      pattern:     2 3 5 7 11      next-2:     13 17
100 %      integer sequence: primes      pattern:     2 3 5 7 11      next-3:     13 17 19
100 %      integer sequence: primes      pattern:     2 3 5 7 11      next-4:     13 17 19 23
87.1 %      integer sequence: fibonacci      pattern:     2 3 5 8 13      next-4:     21 34 55 89
87.1 %      integer sequence: fibonacci      pattern:     2 3 5 8 13      next-3:     21 34 55
87.1 %      integer sequence: fibonacci      pattern:     2 3 5 8 13      next-2:     21 34
87.1 %      integer sequence: fibonacci      pattern:     2 3 5 8 13      next-1:     21
72.6 %      integer sequence: counting      pattern:     2 3 4 5 6      next-4:     7 8 9 10
72.6 %      integer sequence: counting      pattern:     2 3 4 5 6      next-3:     7 8 9
72.6 %      integer sequence: counting      pattern:     2 3 4 5 6      next-2:     7 8
72.6 %      integer sequence: counting      pattern:     2 3 4 5 6      next-1:     7
72.4 %      integer sequence: counting      pattern:     3 4 5 6 7      next-4:     8 9 10 11
72.4 %      integer sequence: counting      pattern:     3 4 5 6 7      next-2:     8 9
72.4 %      integer sequence: counting      pattern:     3 4 5 6 7      next-3:     8 9 10
72.4 %      integer sequence: counting      pattern:     3 4 5 6 7      next-1:     8
68.3 %      integer sequence: counting      pattern:     4 5 6 7 8      next-2:     9 10
68.3 %      integer sequence: counting      pattern:     4 5 6 7 8      next-4:     9 10 11 12
68.3 %      integer sequence: counting      pattern:     4 5 6 7 8      next-3:     9 10 11
68.3 %      integer sequence: counting      pattern:     4 5 6 7 8      next-1:     9
63.3 %      integer sequence: fibonacci      pattern:     1 2 3 5 8      next-3:     13 21 34
63.3 %      integer sequence: fibonacci      pattern:     1 2 3 5 8      next-2:     13 21
63.3 %      integer sequence: fibonacci      pattern:     1 2 3 5 8      next-1:     13
63.3 %      integer sequence: fibonacci      pattern:     1 2 3 5 8      next-4:     13 21 34 55
58.5 %      integer sequence: primes      pattern:     3 5 7 11 13      next-2:     17 19
58.5 %      integer sequence: primes      pattern:     3 5 7 11 13      next-1:     17
58.5 %      integer sequence: primes      pattern:     3 5 7 11 13      next-3:     17 19 23
58.5 %      integer sequence: primes      pattern:     3 5 7 11 13      next-4:     17 19 23 29
57.3 %      integer sequence: counting      pattern:     5 6 7 8 9      next-3:     10 11 12
57.3 %      integer sequence: counting      pattern:     5 6 7 8 9      next-4:     10 11 12 13
57.3 %      integer sequence: counting      pattern:     5 6 7 8 9      next-2:     10 11
57.3 %      integer sequence: counting      pattern:     5 6 7 8 9      next-1:     10
55.6 %      integer sequence: counting      pattern:     1 2 3 4 5      next-2:     6 7
55.6 %      integer sequence: counting      pattern:     1 2 3 4 5      next-4:     6 7 8 9
55.6 %      integer sequence: counting      pattern:     1 2 3 4 5      next-1:     6
55.6 %      integer sequence: counting      pattern:     1 2 3 4 5      next-3:     6 7 8
50.7 %      integer sequence: counting      pattern:     6 7 8 9 10      next-2:     11 12
50.7 %      integer sequence: counting      pattern:     6 7 8 9 10      next-3:     11 12 13
50.7 %      integer sequence: counting      pattern:     6 7 8 9 10      next-1:     11
50.7 %      integer sequence: counting      pattern:     6 7 8 9 10      next-4:     11 12 13 14
40|results>

sa: fuzzy-predict-next |9 9 9>
83.5 %      integer sequence: counting      pattern:     8 9 10      next-2:     11 12
83.5 %      integer sequence: counting      pattern:     8 9 10      next-3:     11 12 13
83.5 %      integer sequence: counting      pattern:     8 9 10      next-4:     11 12 13 14
83.5 %      integer sequence: counting      pattern:     8 9 10      next-1:     11
78.5 %      integer sequence: counting      pattern:     9 10 11      next-2:     12 13
78.5 %      integer sequence: counting      pattern:     7 8 9      next-3:     10 11 12
78.5 %      integer sequence: counting      pattern:     9 10 11      next-3:     12 13 14
78.5 %      integer sequence: counting      pattern:     7 8 9      next-2:     10 11
78.5 %      integer sequence: counting      pattern:     9 10 11      next-4:     12 13 14 15
78.5 %      integer sequence: counting      pattern:     9 10 11      next-1:     12
78.5 %      integer sequence: counting      pattern:     7 8 9      next-4:     10 11 12 13
78.5 %      integer sequence: counting      pattern:     7 8 9      next-1:     10
60.3 %      integer sequence: counting      pattern:     10 11 12      next-1:     13
60.3 %      integer sequence: counting      pattern:     6 7 8      next-2:     9 10
60.3 %      integer sequence: counting      pattern:     6 7 8      next-4:     9 10 11 12
60.3 %      integer sequence: counting      pattern:     10 11 12      next-4:     13 14 15 16
60.3 %      integer sequence: counting      pattern:     10 11 12      next-3:     13 14 15
60.3 %      integer sequence: counting      pattern:     10 11 12      next-2:     13 14
60.3 %      integer sequence: counting      pattern:     6 7 8      next-3:     9 10 11
60.3 %      integer sequence: counting      pattern:     6 7 8      next-1:     9
52.3 %      integer sequence: primes      pattern:     7 11 13      next-4:     17 19 23 29
52.3 %      integer sequence: primes      pattern:     5 7 11      next-3:     13 17 19
52.3 %      integer sequence: primes      pattern:     7 11 13      next-3:     17 19 23
52.3 %      integer sequence: primes      pattern:     5 7 11      next-4:     13 17 19 23
52.3 %      integer sequence: primes      pattern:     7 11 13      next-2:     17 19
52.3 %      integer sequence: primes      pattern:     5 7 11      next-2:     13 17
52.3 %      integer sequence: primes      pattern:     5 7 11      next-1:     13
52.3 %      integer sequence: primes      pattern:     7 11 13      next-1:     17
28|results>

Raw file here.