-
Notifications
You must be signed in to change notification settings - Fork 0
Sequences
- The sequence files in javascript/sequences/ are parallel to modules in their structure, they represent the builtInSequences we've provided. We can initialize a sequence file:
$ npm run init_sequence
Enter sequence name: some sequence
Enter brief sequence description: a new sequence
1. Generating source..
2. Creating file: javascript/sequences/sequenceSomesequence.js
3. Adding sequence entry in javascript/sequences/sequences.js
Done!
Lets look at our new file sequenceSomesequence.js:
function GEN_Somesequence({}) {
const Somesequence = function(n, cache){
//define your function here
}
return Somesequence
}
const SCHEMA_Somesequence = {
}
const SEQ_Somesequence = {
generator: GEN_Somesequence,
name: 'Some sequence',
description: 'a new sequence',
paramsSchema: SCHEMA_Somesequence
}
module.exports = SEQ_SomesequenceThe structure looks almost exactly like the modules except that we have a function in the beginning rather than a class. The function GEN_Somesequence does not return a number, rather it returns a function Somesequence which is the actual function that generates elements of the sequence. The reason we structure the sequence files this way is so we can capture a larger class of sequences, if you take a look at the linear recurrence sequence, you'll see how the function GEN_linearRecurrence returns a linear recurrence parameterized by the following arguments (Which have to be defined by the paramsSchema just like with the drawing modules and their configs):
{
coefficientList,
seedList,
m
}We even use GEN_linearRecurrence to create the fibonacci and lucas sequences
SEQ_linearRecurrence = require('./sequenceLinRec.js')
function GEN_fibonacci({
m
}) {
return SEQ_linearRecurrence.generator({
coefficientList: [1, 1],
seedList: [1, 1],
m
});
}The SEQ objects isn't passed to modules, rather the SequenceGenerator class is. NScore converts built-in sequences, sequences from the sage server and plain old lists into SequenceGenerators so modules can expect a uniform interface. The basic abstraction over a sequence is the SequenceGenerator class (in javascript/sequences/sequence.js). The class has 4 properties
this.generator = generator;
this.ID = ID;
this.cache = [];
this.newSize = 1;- generator: A function that generates the nth number of the sequence. (Must be an N->N function)
- ID: ID is used for bookkeeping by NScore.
- cache: A cache to store previously calculated values, useful to avoid expensive calculations and allow memoization.
- newSize: Helps resizing the cache.
Methods:
- resizeCache(n) -> void: Either doubles the newSize or sets it to n.
- fillCache(n) -> void: Fills the cache from the last element this.cache.length - 1 up to n.
- getElement(n) -> number (or undefined): This is really the only method modules should call, it returns the nth element of the sequence. It checks the cache and if it misses it populates the cache.
There is no way to get the length of the sequence since some (or really most that we care about) sequences are infinite. When getElement returns undefined that means there are no more elements to return.
Additionally the OEISSequenceGenerator defines additional methods since it depends on an external sage server:
- oeisFetch(n) -> Array(number): Sends sage code to http://aleph.sagemath.org/service that returns a list numbers for the given OEIS sequence.
- async prefillCache() -> void: When the sequence is instantiated, we use this method to fill the cache asynchronously since a getting the code from another server is a very expensive operation, right now we prefill with 3000 elements.
Unfortunately if we get a cache miss (3001th element) we have to make a blocking request to get more elements. Otherwise we'd have to make getElement an asynchronously method which would make it more complicated to implement modules. Some possible alternatives:
- Making an async call when we're near but no at the cache's end, for example cache.length - 500, that way modules can continue to call getElement since we have 500 caches hits before missing (this is assuming the module is calling getElement in normal order 1,2,3,4,..), and we'd be pulling more elements in the background. There is an edge case where the module goes through the 500 elements before the request to the sage server is completed but we can probably block until it completes if we hit this case.
- Prefill the cache with huge amount of elements (20000-30000).
The valid OEIS codes are the ones defined in validOEIS scraped from https://github.com/sagemath/sagelib/blob/master/sage/combinat/sloane_functions.py The list method is the only one that seems to be consistently working for the different sequences defined in the file.