Tensor Evaluation Reference

A tensor is a set of named dimensions defining its order and a set of values located in the space of those dimensions:

  • Cell: A value located in the dimension space. Consists of a cell address and the floating point value at that address.
  • Address: A set of key-values where each key is a dimension from the set of dimensions of the tensor, and each value is a label (integer or identifier) determining the cells location in that dimension.
The set of dimensions, cell values and cell address key-values can be of any size including zero. A dimension can be either mapped or indexed. Mapped dimensions use string identifiers as labels in the cell addresses (like a map), while indexed dimensions use integers in the range [0,N> (like an array), where N is the size of the dimension. The dimensions of a tensor defines its type, see the tensor type spec.

Literal Form

The standard literal form of tensors specified verbatim in ranking expressions is as follows (expressed in EBNF):
literal tensor = "{" cells "}" ;
cells = | cell , { "," cell } ;
cell = "{" address "}:" scalar ;
address = | element, { "," element } ;
element = dimension ":" label ;
dimension = integer | string ;
label = integer | string ;

Literal form examples

An empty tensor:

{}
A single value tensor with a single mapped dimension x:
{ {x:foo}:5.0 }
A tensor with multiple values and mapped dimensions x and y:
{ {x:foo, y:bar}:5.0, {x:foo, y:baz}:7.0 }
A tensor with a single indexed dimension x representing a vector:
{ {x:0}:3.0, {x:1}:5.0, {x:2}:7.0 }

Operations

The following set of tensors operations are available to use in ranking expressions. We group the operations in primitive functions and convenience functions that can be implemented by primitive functions.

Primitive functions

map map(tensor, f(x)(expr)) Returns a new tensor with the lambda function defined in f(x)(expr) applied to each cell.
reduce reduce(tensor, aggregator, dim1, dim2, ...) Returns a new tensor with the aggregator applied across dimensions dim1, dim2, etc. If no dimensions are specified, reduce over all dimensions.
join join(tensor1, tensor2, f(x,y)(expr)) Returns a new tensor constructed from the natural join between tensor1 and tensor2, with the resulting cells having the value as calculated from f(x,y)(expr), where x is the cell value from tensor1 and y from tensor2.
tensor tensor(tensor-type-spec)(expr) Generates new tensors according to type specification and expression expr.
rename rename(tensor, dimensions-to-rename, new-names) Renames one or more dimensions in the tensor.
concat concat(tensor1, tensor2, dim) Concatenates two tensors along dimension dim.

Lambda functions in primitive functions

Some of the primitive functions accept lambda functions that are evaluated and applied to a set of tensor cells. The functions contain a single expression that have the same format and built-in functions as general ranking expressions. However, the atoms are the arguments defined in the argument list of the lambda.

The expression cannot access variables or data structures outside of the lambda, i.e. they are not closures.

Examples:

f(x)(abs(x))
f(x,y)(if(x < y, 0, 1))

- Map

Arguments:

Returns a new tensor where the expression in the lambda function is evaluated in each cell in tensor.

Examples:

map(t, f(x)(abs(x)))
map(t, f(i)(if(i < 0, 0, i)))

- Reduce

Arguments:

  • tensor: a tensor.
  • aggregator: the aggregator to use. See below.
  • dim1, dim2, ...: the dimensions to reduce over. Optional.

Returns a new tensor with the aggregator applied across dimensions dim1, dim2, etc. If no dimensions are specified, reduce over all dimensions.

Available aggregators are:

  • avg: arithmetic mean
  • count: number of elements
  • prod: product of all values
  • sum: sum of all values
  • max: maximum value
  • min: minimum value

Examples:

reduce(t, sum)         # Sum all values in tensor
reduce(t, count, x)    # Count number of cells along dimension x

- Join

Arguments:

  • tensor1: a tensor.
  • tensor2: a tensor.
  • f(x,y)(expr): a lambda function with two arguments.

Returns a new tensor constructed from the natural join between tensor1 and tensor2, with the resulting cells having the value as calculated from f(x,y)(expr), where x is the cell value from tensor1 and y from tensor2.

Formally, the result of the join is a new tensor with dimensions the union of dimension between tensor1 and tensor2. The cells are the set of all combinations of cells that have equal values on their common dimensions.

Examples:

t1 = {{x:0}: 1.0, {x:1}: 2.0}
t2 = {{x:0,y:0}: 3.0, {x:0,y:1}: 4.0, {x:1,y:0}: 5.0, {x:1,y:1}: 6.0}

join(t1, t2, f(x,y)(x * y)) = {{x:0,y:0}: 3.0, {x:0,y:1}: 4.0, {x:1,y:0}: 10.0, {x:1,y:1}: 12.0}

reduce(join(t1, t2, f(x,y)(x * y)), sum) = 29.0

- tensor

Arguments:

Generates new tensors according to the type specification and expression expr. The tensor type must be a bound indexed tensor (e.g. tensor(x[10])) for Vespa to be able to generate the tensor. The expression in expr will be evaluated for each cell. The arguments in the expression is implicitly the names of the dimensions defined in the type spec.

Useful for creating transformation tensors.

Examples:

tensor(x[3])(x) = {{x:0}: 0.0, {x:1}: 1.0, {x:2}: 2.0}
tensor(x[2],y[2])(x == y) = {{x:0,y:0}: 1.0, {x:0,y:1}: 0.0, {x:1,y:0}: 0.0, {x:1,y:1}: 1.0}

- rename

Arguments:

  • tensor: a tensor.
  • dim-to-rename: a dimension, or list of dimensions, to rename.
  • new-names: new names for the dimensions listed above.

Returns a new tensor with one or more dimension renamed.

Examples:

t1 = {{x:0,y:0}: 1.0, {x:0,y:1}: 0.0, {x:1,y:0}: 0.0, {x:1,y:1}: 1.0}

rename(t1,x,z) = {{z:0,y:0}: 1.0, {z:0,y:1}: 0.0, {z:1,y:0}: 0.0, {z:1,y:1}: 1.0}
rename(t1,(x,y),(i,j)) = {{i:0,j:0}: 1.0, {i:0,j:1}: 0.0, {i:1,j:0}: 0.0, {i:1,j:1}: 1.0}

- concat

Arguments:

  • tensor1: a tensor or scalar.
  • tensor2: a tensor or scalar.
  • dim: the dimension to concatenate along.

Returns a new tensor with the two tensors tensor1 and tensor2 concatenated along dimension dim. The tensors can also be scalars.

Examples:

t1 = {{x:0}: 0.0, {x:1}: 1.0}
t2 = {{x:0}: 2.0, {x:1}: 3.0}

concat(t1,t2,x) = {{x:0}: 0.0, {x:1}: 1.0}, {x:2}: 2.0, {x:3}: 3.0}}

Non-primitive functions

Non-primitive functions can be implemented by primitive functions, but are not necessarily so for performance reasons.

abs(t) map(t, f(x)(abs(x)))
Absolute value of all elements.
acos(t) map(t, f(x)(acos(x)))
Arc cosine of all elements.
t1 + t2 (add) join(t1, t2, f(x,y)(x + y))
Join and sum tensors t1 and t2.
argmax(t) join(t, max(t), f(x,y)(if (x == y, 1, 0)))
Returns a tensor with cell(s) of the highest value(s) in the tensor set to 1.
argmin(t) join(t, min(t), f(x,y)(if (x >= y, 0, 1)))
Returns a tensor with cell(s) of the lowest value(s) in the tensor set to 1.
asin(t) map(t, f(x)(asin(x)))
Arc sine of all elements.
atan(t) map(t, f(x)(atan(x)))
Arc tangent of all elements.
atan2(t1,t2) join(t1, t2, f(x,y)(atan2(x,y)))
Arctangent of t1 and t2/
avg(t, dim) reduce(t, avg, dim)
Reduce the tensor with the average aggregator along dimension dim.
ceil(t) map(t, f(x)(ceil(x)))
Ceiling of all elements.
count(t, dim) reduce(t, count, dim)
Reduce the tensor with the count aggregator along dimension dim.
cos(t) map(t, f(x)(cos(x)))
Cosine of all elements.
cosh(t) map(t, f(x)(cosh(x)))
Hyperbolic cosine of all elements.
diag(n1,n2) tensor(i[n1],j[n2])(if (i==j, 1.0, 0.0)))
Returns a tensor with the diagonal set to 1.0.
t1 / t2 (div) join(t1, t2, f(x,y)(x / y))
Join and divide tensors t1 and t2.
elu(t) map(t, f(x)(if(x < 0, exp(x)-1, x)))
Exponential linear unit.
t1 == t2 (equal) join(t1, t2, f(x,y)(x == y))
Join and determine if each element in t1 and t2 are equal.
exp(t) map(t, f(x)(exp(x)))
Exponential function (e^x) of each element.
floor(t) map(t, f(x)(floor(x)))
Floor of each element.
t1 > t2 (greater) join(t1, t2, f(x,y)(x > y))
Join and determine if each element in t1 is greater than t2.
t1 >= t2 (greater or equals) join(t1, t2, f(x,y)(x >= y))
Join and determine if each element in t1 is greater than or equals t2.
t1 < t2 (less) join(t1, t2, f(x,y)(x < y))
Join and determine if each element in t1 is less than t2.
t1 <= t2 (less equals) join(t1, t2, f(x,y)(x <= y))
Join and determine if each element in t1 is less than or equals t2.
l1_normalize(t, dim) join(t, reduce(t, sum, dim), f(x,y) (x / y))
L1 normalization: t / sum(t, dim).
l2_normalize(t, dim) join(t, map(reduce(map(t, f(x)(x * x)), sum, dim), f(x)(sqrt(x))), f(x,y)(x / y))
L2 normalization: t / sqrt(sum(t^2, dim).
log(t) map(t, f(x)(log(x)))
Natural logarithm of each element.
log10(t) map(t, f(x)(log10(x)))
Logarithm with base 10 of each element.
matmul(t1, t2, dim) reduce(join(t1, t2, f(x,y)(x * y)), sum, dim)
Matrix multiplication of two tensors. This is the product of the two tensors summed along a shared dimension.
max(t, dim) reduce(t, max, dim)
Reduce the tensor with the max aggregator along dimension dim.
max(t1,t2) join(t1, t2, f(x,y)(max(x,y)))
Join and return the max of t1 or t2. Arguments can be scalars.
min(t, dim) reduce(t, min, dim)
Reduce the tensor with the min aggregator along dimension dim.
min(t1,t2) join(t1, t2, f(x,y)(min(x,y)))
Join and return the minimum of t1 or t2. Arguments can be scalars.
mod(t,constant) map(t, f(x)(mod(x,constant)))
Modulus of constant with each element.
t1 * t2 (mul) join(t1, t2, f(x,y)(x * y))
Join and multiply tensors t1 and t2.
t1 != t2 (not equal) join(t1, t2, f(x,y)(x != y))
Join and determine if each element in t1 and t2 are not equal.
pow(t,constant) map(t, f(x)(pow(x,constant)))
Raise each element to the power of constant.
prod(t, dim) reduce(t, prod, dim)
Reduce the tensor with the product aggregator along dimension dim.
random(n1, n2, ...) tensor(i1[n1],i2[n2],...)(random(1.0))
Returns a tensor with random values between 0.0 and 1.0, uniform distribution.
range(n) tensor(i[n])(i)
Returns a tensor with increasing values.
relu(t) map(t, f(x)(max(0,x)))
Rectified linear unit.
round(t) map(t, f(x)(round(x)))
Round each element.
sigmoid(t) map(t, f(x)(1.0 / (1.0 + exp(0.0-x))))
Returns the sigmoid of each element.
sin(t) map(t, f(x)(sin(x)))
Sinus of each element.
sinh(t) map(t, f(x)(sinh(x)))
Hyperbolic sinus of each element.
sign(t) map(t, f(x)(if(x < 0, -1.0, 1.0)))
The sign of each element.
softmax(t, dim) join(map(t, f(x)(exp(x))), reduce(map(t, f(x)(exp(x))), sum, dim), f(x,y)(x / y))
The softmax of the tensor, e.g. e^x / sum(e^x).
sqrt(t) map(t, f(x)(sqrt(x)))
The square root of each element.
square(t) map(t, f(x)(square(x)))
The square of each element.
t1 - t2 (subtract) join(t1, t2, f(x,y)(x - y))
Join and subtract tensors t1 and t2.
sum(t, dim) reduce(t, sum, dim)
Reduce the tensor with the summation aggregator along dimension dim.
tan(t) map(t, f(x)(tan(x)))
The tangent of each element.
tanh(t) map(t, f(x)(tanh(x)))
The hyperbolic tangent of each element.
xw_plus_b(x,w,b,dim) join(reduce(join(x, w, f(x,y)(x * y)), sum, dim), b, f(x,y)(x+y))
Matrix multiplication of x (usually a vector) and w (weights), with b added (bias). A typical operation for activations in a neural network layer, e.g. sigmoid(xw_plus_b(x,w,b))).

Tensor Rank Features

The following rank features can be used to reference tensors when doing tensor operations in ranking expressions. The tensors can come from the document, the query or be constant for a deployment of your application.

Please take a look at the following reference documentations on how use tensors in documents:

- attribute(tensor_attribute)

Returns the tensor value found in the given tensor attribute.

Take a look at tensor type and tensor-type-spec reference doc for how to setup a tensor attribute in your search definition.

Example tensor attribute field in a sd-file where the tensor has 2 mapped dimensions, x and y:

field tensor_attribute type tensor(x{},y{}) {
    indexing: attribute | summary
    attribute: tensor(x{},y{})
}

- query(tensor_feature)

Returns the tensor value passed down with the query as a feature.

In order to use this feature you must define the tensor type of the query feature in a query profile type. In the following example the tensor type is defined to have one mapped dimension x:

<query-profile-type id="myProfileType">
  <field name="ranking.features.query(tensor_feature)" type="tensor(x{})" />
</query-profile-type>
The tensor value itself must be set in a searcher using the com.yahoo.search.query.ranking.RankFeatures instance that is associated with an instance of com.yahoo.search.Query. In the following example we create a tensor with a single cell with value 500:
package com.yahoo.example;

import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.Searcher;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.tensor.MappedTensor;
import com.yahoo.tensor.TensorType;

public class TensorInQuerySearcher extends Searcher {
    @Override
    public Result search(Query query, Execution execution) {
        query.getRanking().getFeatures().put("query(tensor_feature)",
            new MappedTensor.Builder(TensorType.fromSpec("tensor(x{})")).cell().label("x", "foo").value(500).build());
        return execution.search(query);
    }
}

Take a look at query profile field type reference doc for more information on how to specify a field as a tensor in a query profile type.

- constant(tensor_constant)

Returns the constant tensor value with the given name as specified in your sd-file.

Take a look at constant reference documentation for how to specify constant tensors in your sd-file.

- tensorFromWeightedSet(source, dimension)

Creates a tensor with one mapped dimension from the given integer or string weighted set source. The source can be either an attribute field or a query parameter. The source parameter is required and must be specified as follows:

  • attribute(attributeName): The tensor is created based on the content of the weighted set attribute attributeName.
  • query(propertyName): The tensor is created based on the weighted set passed down with the query using &ranking.properties.propertyName={k1:w1,k2:w2,...,kN:wN}.
The dimension parameter is optional, where the default value is the parameter name from the source parameter.

Example:
Assume we have the following weighted set with keys and corresponding weights, and the dimension dim:

{k1:w1,k2:w1,...,kN:wN}
The tensor representation of this weighted set has the dimension dim with the following content:
{ {dim:k1}:w1, {dim:k2}:w2, ..., {dim:kN}:wN} }

- tensorFromLabels(source, dimension)

Creates a tensor with one mapped dimension from the given integer or string array source. The source can be either an attribute field or a query parameter. The source parameter is required and must be specified as follows:

  • attribute(attributeName): The tensor is created based on the content of the array attribute attributeName.
  • query(propertyName): The tensor is created based on the array passed down with the query using &ranking.properties.propertyName=[v1 v2 ... vN].
The dimension parameter is optional, where the default value is the parameter name from the source parameter.

Example:
Assume we have the following array with values and the dimension dim:

[v1 v2 ... vN]
The tensor representation of this array has the dimension dim with the following content:
{ {dim:v1}:1.0, {dim:v2}:1.0, ..., {dim:vN}:1.0} }