RTensor is the sixth-best formula calculator in the world. (I wrote that slogan a long while ago. Don't ask what the five better ones are.)
All valid code in the RTensor language gets parsed into an abstract syntax tree (AST). The AST consists of nodes, which may or may not reference subnodes, making them "branch nodes" (as e.g. operations and function calls) and "leaf nodes" (as primitives) respectively.
ASTs are common to many languages, and you don't really need to know about them for basic RTensor use.
Consider the following progression:
All items in that progression qualify as tensors of various ranks; ranks of tensors continue beyond 2 as far as you want, tho ranks from 0 to 2 occur most commonly.
The most developed interface for RTensor is the webpage, which consists of some documentative content, followed by a space for alternating lines of input and output.
To run a line of RTensor code, type it (or copy it) into the current input line and press
You can also use the up and down arrow keys to navigate thru the current session's history of earlier lines.
Whilst not in the RTensor language, you can combine multiple lines of code into one input by joining them with semicolons (
When no error occurs whilst processing the input code, RTensor will write as output the code's return value. Preceding this return value, RTensor indicates a variable name (an underscore followed by a serial number) to which it saved that return value. If the input code consists of multiple semicolon-separated statements, RTensor will evaluate all of the statements, but will only present the last one's return value, and the serial variable will only receive the final statement's return value.
RTensor also silently saves the final return value to the variable
_ (underscore), but leaves its previous value if an error occurred.
Some code (such as syntactic functions) produces extra output, in varying forms, before the main output.
When an error occurs whilst parsing or evaluating the input code, RTensor will display a relevant error message.
If the error starts with a
If the error occurs when evaluating, RTensor will not execute code after the part that triggered the error; errors propagate up thru a program.
For some code (such as
3+), RTensor successfully parses and evaluates it, but notices that not all of the code corresponds to part of a valid AST.
In addition to typical output, RTensor leaves the message "possible syntax error", which almost always means "actual syntax error".
A numeric literal directly represents a real number (a scalar), and consists of one or both of an integer component and a fractional component.
Each component consists of digits (in base ten).
The fractional component, if present, must begin with a dot (
Valid numeric literals include
For now, you can use
. to mean
NaN, but this may change.
An identifier consists of a sequence of letters, digits, underscores, or (unlike most other languages) apostrophes (akin to prime symbols), and does not start with a digit. An identifier, when interpreted to evaluating code, references a variable, which can hold a value of any type. Shadowing/function scope may affect what variable an identifier references.
Operators combine one or two subexpressions (making the operation unary or binary, respectively) and describe a computation based on the subexpressions. Unary operators go before their subexpression. Binary operators go between their subexpressions.
Each operator has a precedence, which may group it more tightly or loosely than other operators, and each precedence has an associativity. Unary operators all have tighter precedence than binary operators, regardless of their relative precedence as binary operators. You can use parentheses to group operations differently from what their precedence and associativity would normally imply.
In operator descriptions,
b represent subexpressions and their results.
When used on a scalar and a tensor, the result comes from the tensor, with each element replaced by the result of the operation between the scalar and each scalar in the tensor.
3 * ((7, 2), (-1, 3), (-8, -2)) returns
[[21, 6], [-3, 9], [-24, -6]].
When used on two tensors, the result comes from the elementwise combination of the two tensors, or an error if the tensors have different structures.
a + b, or
a - b, or
a * b
a / b, or
a ^ b, or
^a(exp(a) = ea)
a b(base-a logarithm of b), or
a == b(true or false), or
== a(equal to zero)
a -/ b(ath root of b), or
a < b(true or false), or
< a(negative) You cannot chain comparisons, as in
a < b < c; RTensor will interpret that as
(a < b) < c, which reduces to either
true < cor
false < c. RTensor has no
>operator. Instead, swap operands as appropriate.
a, b gives an output which depends on the relative rank of
ahas greater rank, RTensor appends
bto the end of
a, returning a new tensor
bas its elements
These semantics let you chain the appending operator (as in
a, b, c, d) to build a many-element tensor.
a .. b gives an output which depends on the ranks of
bto the end of
a, returning a new tensor
bare scalars), RTensor generates a vector by iterating thru the numbers from
b(inclusive) with a step size of 1
a .. bis an error
When using the concatenation operator, carefully distinguish it from a decimal point;
3..5 will result in an error, unlike the valid options
3 .. 5.
.. a encapsulates
a as a one-element tensor, effectively incrementing the rank without adding any new data.
a => b constructs and returns an anonymous function.
a may consist of a comma-separated list, each of which labels a separate argument for the function;
it gets tranlated into a pattern, but syntactically forms as an expression.
b must consist of some expression, possibly using variables defined in
x, y => x + y makes addition into a two-input function.
The assignment operation,
a = b, saves the value from
b into the location described by
a, and returns the new value of
a (or something closely related to
a, by undocumented semantics).
a may consist of an identifier — in which case,
b replaces the value already stored in that variable.
a may have a function call or index suffix (the two have the same form):
aalready references a function, the assignment adds a new case (the pattern consisting of the argument list, the expression coming from
b) to that function (at top priority)
aalready references a tensor, the assignment replaces the element at the given index with the value of
b, or causes an error if
adoes not already contain an element at that index
adoes not reference anything, or references a scalar, the assignment replaces the value with a new function, adding a single case as if
aalready referenced a function
From tightest to loosest, L = left-associative, R = right-associative:
An expression suffixed with a parenthesised expression (
v(i)), or the equivalent bracketed form (
v[i]), forms an indexing expression (or a function call, depending on
v refers to a tensor, the index expression returns the
ith element of
v (a tensor of rank at most one less than that of
Indexing in RTensor starts at 1 and not 0.
Attempts to access
i exceeds the length of
i does not equal an integer, or
i subceeds 1 will cause an error.
i component of the indexing expression can include extra comma-separated indices, but RTensor currently ignores all indices after the first.
An expression suffixed with a parenthesised expression-list (
f(a, b, c, ...)) forms a function call (or an indexing expression, depending on
f refers to a function, the function call returns the value
f provides when evaluated with the given argument list.
The argument list may contain any number of arguments, including 0 (i.e.
, normally acts as an operator, the argument-list syntax suppresses this behaviour.
In an argument list,
, acts as an operator iff it occurs in parentheses, as in the first argument of
f((a, b), c).
Operators with precedence looser than that of
=) will not work at all unless parenthesised.
When evaluating a function, RTensor first evaluates all the arguments, then searches for a pattern (going in descending priority) that matches the arguments. If RTensor could not find a matching pattern, an error occurs. Upon finding a matching pattern, RTensor evaluates the expression or internal function corresponding to that pattern in the function's definition, using the argument-name correspondences given from the pattern to make local variables for that expression.
If a function is called with bracket syntax (
f[a, b, c, ...]) rather than the parenthetical syntax, the arguments will be given to the function as expressions (syntax), not values;
they will be left unevaluated.
Whether this is useful depends on the function.
Typically, a function is only usable with either evaluated arguments (the more common case) or syntactic arguments.
Functions, as objects in RTensor (or function definitions), consist of a list (arranged by priority) of pattern-expression pairs, or cases. Some functions (those predefined by the code which sets up RTensor) have, in place of an expression defined from RTensor, a function in the language implementing RTensor.
A pattern consists of a list of zero or more parameters. A parameter consists of either an identifier or some other expression:
When checking if a pattern matches a list of arguments, the arguments get aligned to the parameters; if the number of arguments differs from the number of parameters, the pattern does not match without any further checks.
To make a parameter that matches only values which equal an existing variable, wrap the variable identifier in an identity (such as
x + 0 or
x * 1).
RTensor currently does not provide pattern-matching mechanisms more complex than equality checks against constant values; you must do so yourself with explicit checks.
RTensor provides both an assignment-based syntax and an anonymous maplet-based syntax for defining functions. The former works with (named) variables, but the latter constructs a function without saving it to a variable (anonymous). A maplet operation takes its left operand as a pattern and its right operand as an unevaluated sub-expression, constructing and returning a single-case function, usable in the same contexts as any other function.
In that single case's expression, one can use variables defined in the scope which defines the anonymous function, creating a sort of closure.
If the left operand has commas, RTensor will break it down into a list of patterns, allowing for more than one parameter, rather than a single pattern.
Using closures, one can make multi-argument anonymous functions a different way:
make a function that returns another function, as a partially-evaluated form of the desired multi-argument function — for example,
x => (y => x + y) instead of
x, y => x + y.
RTensor does not have a special syntax to facilitate the requisite chained method calls, so one would use the curried version not as
f(a, b) but
In descriptions of functions, identifiers in parentheses or brackets (as appropriate) correspond to arguments for valid ways to call a function. The exact identifier used indicates the expected type:
c: number (in some contexts, specifically integer)
t: tensor (any rank above 0)
v: vector (rank 1 tensor)
y: number (rank 0 tensor)
z: any type
Some functions work on a tensor, but only look one rank deep, using the tensor in place of a vector.
In this case, the parameter uses
t for the identifier.
All functions in this list that accept a real number (so not
random) will accept, in place of that number, a tensor, and will perform the same operation on each number in the tensor, returning an equivalently-structured tensor.
abs(x), the absolute value or magnitude of
floor(x), the integer rounded down (towards negative infinity) from
random(), a random number in
[0, 1), or
random(m, n), a random integer in
re(x), the real component of complex number
im(x), the imaginary component of complex number
conj(x), the complex conjugate of
n-element vector of zeroes
len(t), the number of elements in the top level of
tThis does not count the number of numbers in
t, but merely the number of tensors of lesser rank immediately composing
t. For example,
len(((4, 7), (2, 3), (8, 5)))return 3 rather than 6.
any(v), the first nonzero value in
falsevalues, this functions as an inclusive OR.
all(v), the first zero-equivalent value in
falsevalues, this functions as an AND.
map(t, f), a new tensor (same rank as
t, greater than 0) with each element
t(in the top level) replaced with
faccepts two arguments (binary function),
f(x, i), where
imatches the index of
fdoes not accept any arguments (nullary function),
f(). Notice that
maponly acts on top-level elements; e.g.
map(((4, 7), (2, 3), (8, 5)), f)applies
(2, 3), and
(8, 5), not
5. To apply a function to replace every number in a tensor, use
filter(t, f), a new tensor (same rank as
t, greater than 0) containing all elements
t(in the top level) for which
f(x)returns a value equivalent to
faccepts two arguments (binary function),
f(x, i)returns a value equivalent to
imatches the index of
t. As with
filteronly acts on top-level elements. RTensor currently does not have any
f(x)for every number in a tensor.
reduce(t, f), a value computed by repeatedly applying
x = f(x, y)for each element
t(at the top level). The initial value of
xcomes from the first element of
reducedoes not apply
f(x, y)using that initial value for
y. As with
reduceonly acts on top-level elements. RTensor currently does not have any
deepreduceto combine all individual numbers in a tensor.
tail(t), a new tensor consisting of all the elements of
t, in the same order, except for the first
trim(t), a new tensor consisting of all the elements of
t, in the same order, except for the last
deepmap(t, f), a new tensor with the same structure as
t, but with every number
x(down thru all ranks) replaced with
if(c, w, z)or
if[c, w, z], a selection between
ifaccepts up to two extra arguments to automatically apply to the selected value as arguments (assuming that value has callable behaviour). For more than two arguments (and recommended in general, as we may deprecate that feature), we suggest directly calling the result instead, as in
if(c, w, z)(a, b, x, y).
for(init, cond, step), the final value of
x— which starts as
x = init— after repeatedly applying
x = step(x), until
cond(x)returns a value equivalent to
clear(), which clears display but not history or variables
lhs[expr], the left side of
rhs[expr], the right side of
operator[expr], a number corresponding to the type of operator in
sum[(var = expr), n, expr], corresponding to typical summation notation
product[(var = expr), n, expr], likewise
render[expr], which (crudely) renders the expression in the webpage version
html[expr], which outputs HTML code to display the expression
latex[expr], which outputs LATEX code to display the expression
i, the imaginary unit
phi, the golden ratio 1.618
If you want