Skip to main content

Sequences

Mathematical sequences like the Fibonacci numbers, arithmetic progressions, or Pascal's triangle can be defined declaratively using recurrence relations. The Compute Engine provides tools to define, evaluate, and analyze sequences.

Sequences are defined by specifying:

  • Base cases: Initial values that anchor the sequence
  • Recurrence relation: A formula expressing each term in terms of previous terms

Once defined, sequences can be evaluated at any index, used in summations and products, and even compared against the Online Encyclopedia of Integer Sequences (OEIS).

Defining Sequences

There are two ways to define sequences: using the declareSequence() method or using LaTeX assignment notation.

Using declareSequence()

The declareSequence() method provides a structured way to define sequences:

// Fibonacci sequence: F_n = F_{n-1} + F_{n-2}
ce.declareSequence('F', {
base: { 0: 0, 1: 1 },
recurrence: 'F_{n-1} + F_{n-2}',
});

ce.parse('F_{10}').evaluate();
// ➔ 55

ce.parse('F_{20}').evaluate();
// ➔ 6765

Options

The declareSequence() method accepts the following options:

OptionTypeDescription
baseRecord<number, number>Initial values as index → value pairs
recurrencestring | ExpressionThe recurrence formula
variablestringIndex variable name (default: 'n')
memoizebooleanEnable caching of computed values (default: true)
domain{ min?: number, max?: number }Valid index range

Examples

Arithmetic sequence (adding a constant):

ce.declareSequence('A', {
base: { 0: 1 },
recurrence: 'A_{n-1} + 2',
});

ce.parse('A_{5}').evaluate();
// ➔ 11 (1, 3, 5, 7, 9, 11)

Geometric sequence (multiplying by a constant):

ce.declareSequence('G', {
base: { 0: 1 },
recurrence: '2 \\cdot G_{n-1}',
});

ce.parse('G_{5}').evaluate();
// ➔ 32 (1, 2, 4, 8, 16, 32)

Factorial via recurrence:

ce.declareSequence('H', {
base: { 0: 1 },
recurrence: 'n \\cdot H_{n-1}',
});

ce.parse('H_{5}').evaluate();
// ➔ 120

Using LaTeX Assignment Notation

Sequences can also be defined naturally using LaTeX assignment syntax:

// Define base cases
ce.parse('F_0 := 0').evaluate();
ce.parse('F_1 := 1').evaluate();

// Define recurrence
ce.parse('F_n := F_{n-1} + F_{n-2}').evaluate();

// Use the sequence
ce.parse('F_{10}').evaluate();
// ➔ 55

Base cases and the recurrence can be defined in any order. The sequence becomes usable once all necessary components are present.

// Define recurrence first
ce.parse('L_n := L_{n-1} + 2').evaluate();

// Then add base case
ce.parse('L_0 := 1').evaluate();

// Now it works
ce.parse('L_{5}').evaluate();
// ➔ 11

Using Sequences

Evaluation

Subscripted sequence symbols evaluate to their computed values:

ce.declareSequence('F', {
base: { 0: 0, 1: 1 },
recurrence: 'F_{n-1} + F_{n-2}',
});

// Single value
ce.parse('F_{10}').evaluate();
// ➔ 55

// In expressions
ce.parse('F_{10} + F_{5}').evaluate();
// ➔ 60

// Symbolic subscripts stay symbolic
ce.parse('F_k').evaluate();
// ➔ F_k (unevaluated)

Sum and Product

Sequences work with Sum and Product:

// Sum of first 11 Fibonacci numbers
ce.parse('\\sum_{k=0}^{10} F_k').evaluate();
// ➔ 143

// Product of sequence terms
ce.parse('\\prod_{k=1}^{5} A_k').evaluate();

Generating Terms

Use getSequenceTerms() to generate a list of terms:

ce.declareSequence('F', {
base: { 0: 0, 1: 1 },
recurrence: 'F_{n-1} + F_{n-2}',
});

ce.getSequenceTerms('F', 0, 10);
// ➔ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

// With step parameter (every other term)
ce.getSequenceTerms('F', 0, 10, 2);
// ➔ [0, 1, 3, 8, 21, 55]

Custom Evaluation with subscriptEvaluate

For sequences that don't fit a simple recurrence pattern, use subscriptEvaluate to define a custom evaluation function:

// Define a sequence with custom evaluation logic
ce.declare('P', {
subscriptEvaluate: (subscript, { engine }) => {
const n = subscript.re;
if (!Number.isInteger(n) || n < 0) return undefined;

// Return nth prime number
return engine.number(getNthPrime(n));
},
});

ce.parse('P_{5}').evaluate();
// ➔ 11 (the 5th prime)

ce.parse('P_k').evaluate();
// ➔ P_k (stays symbolic when subscript is not numeric)

When subscriptEvaluate returns undefined, the expression stays symbolic.

Subscripted expressions with subscriptEvaluate have type number and can participate in arithmetic:

ce.parse('P_{5} + P_{3}').evaluate();
// ➔ 16 (11 + 5)

Multi-Index Sequences

Sequences can have multiple indices, useful for structures like Pascal's triangle or grid-based recurrences:

// Pascal's Triangle: P_{n,k} = P_{n-1,k-1} + P_{n-1,k}
ce.declareSequence('P', {
variables: ['n', 'k'],
base: { 'n,0': 1, 'n,n': 1 }, // Pattern-based base cases
recurrence: 'P_{n-1,k-1} + P_{n-1,k}',
domain: { n: { min: 0 }, k: { min: 0 } },
constraints: 'k <= n',
});

ce.parse('P_{5,2}').evaluate();
// ➔ 10

ce.parse('P_{10,5}').evaluate();
// ➔ 252

Multi-Index Options

OptionTypeDescription
variablesstring[]Names of index variables (e.g., ['n', 'k'])
baseRecord<string, number>Pattern-based base cases
constraintsstringConstraint expression (e.g., 'k <= n')

Pattern-Based Base Cases

Base case keys support patterns:

PatternMatches
'0,0'Only (0, 0)
'n,0'Any (n, 0) - first column
'n,n'Any (n, n) - diagonal
'0,k'Any (0, k) - first row

Exact matches take priority over patterns.

Sequence Introspection

Checking Sequence Status

Use getSequenceStatus() to check if a sequence definition is complete:

ce.parse('F_0 := 0').evaluate();
ce.getSequenceStatus('F');
// ➔ { status: 'pending', hasBase: true, hasRecurrence: false, baseIndices: [0] }

ce.parse('F_n := F_{n-1} + F_{n-2}').evaluate();
ce.getSequenceStatus('F');
// ➔ { status: 'complete', hasBase: true, hasRecurrence: true, baseIndices: [0] }

ce.getSequenceStatus('x');
// ➔ { status: 'not-a-sequence', hasBase: false, hasRecurrence: false }

Sequence Information

// Get sequence details
ce.getSequence('F');
// ➔ { name: 'F', variable: 'n', baseIndices: [0, 1], memoize: true, cacheSize: 5 }

// List all defined sequences
ce.listSequences();
// ➔ ['F', 'A', 'H']

// Check if a symbol is a sequence
ce.isSequence('F');
// ➔ true

ce.isSequence('x');
// ➔ false

Cache Management

Sequences with memoize: true (the default) cache computed values:

// View cached values
ce.getSequenceCache('F');
// ➔ Map { 2 => 1, 3 => 2, 4 => 3, ... }

// Clear cache for a specific sequence
ce.clearSequenceCache('F');

// Clear all sequence caches
ce.clearSequenceCache();

OEIS Integration

The Compute Engine can look up sequences in the Online Encyclopedia of Integer Sequences (OEIS).

Looking Up Sequences

// Look up a sequence by its terms
const results = await ce.lookupOEIS([0, 1, 1, 2, 3, 5, 8, 13]);
// ➔ [{ id: 'A000045', name: 'Fibonacci numbers', terms: [...], url: '...' }]

Verifying Your Sequences

Check if your defined sequence matches a known OEIS sequence:

ce.declareSequence('F', {
base: { 0: 0, 1: 1 },
recurrence: 'F_{n-1} + F_{n-2}',
});

const result = await ce.checkSequenceOEIS('F', 10);
// ➔ {
// matches: [{ id: 'A000045', name: 'Fibonacci numbers', ... }],
// terms: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// }
Note

OEIS lookups require network access to oeis.org.

Text Subscripts

For descriptive subscript names, use \text{} in LaTeX:

ce.parse('x_{\\text{max}}');
// ➔ symbol "x_max"

ce.parse('v_{\\text{initial}}');
// ➔ symbol "v_initial"

This creates a single symbol with the text as part of its name.

Type-Aware Subscripts

When a symbol is declared as a collection type, subscripts automatically convert to At() indexing operations:

ce.declare('v', 'list<number>');
ce.parse('v_n');
// ➔ ["At", "v", "n"]

ce.parse('v_{n+1}');
// ➔ ["At", "v", ["Add", "n", 1]]

ce.parse('v_{i,j}');
// ➔ ["At", "v", ["Tuple", "i", "j"]]

The type of the resulting expression is inferred from the collection's element type, allowing subscripted elements to be used in arithmetic.