Tensor Filtering with Dynamic Thresholds
TL;DR:
function filter_subspaces_above_threshold(t, threshold) {
expression: filter_subspaces(t - threshold, f(x)(x > 0)) + threshold
}
function filter_subspaces_below_threshold(t, threshold) {
expression: filter_subspaces(t - threshold, f(x)(x < 0)) + threshold
}The Issue¶
The somewhat new builtin filter_subspaces function
promises to produce a new tensor containing only the subspaces that match the filter.
The main limitation is that filtering is done with a lambda
and in Vespa lambda expressions
cannot access variables or data structures outside the lambda, i.e., they are not closures.
i.e., the filter cannot access neither document attributes, nor query inputs, nor constants, and not even other functions, i.e., you need to know the filter value at application build time, i.e., the threshold can’t be dynamic[1].
The Workaround¶
There is a neat trick to filter on a dynamic scalar value:
subtract the threshold value from the tensor
filter_subspaceson (1) for being larger or smaller than 0add the threshold value back to (2)[2].
Or one-liner:
filter_subspaces(t - threshold, f(x)(x > 0)) + thresholdExample:
filter_subspaces(
tensor<float>(chunk{}):{0:13,1:7,2:5,3:15,4:30,5:2} - 10,
f(x)(x > 0)
) + 10
=> tensor<float>(chunk{}):{0:13.0, 3:15.0, 4:30.0}Check the Tensor Playground runnable example.
The Discussion¶
The inconvenient part is that we need two functions for filtering above or below the threshold.
If your threshold is another tensor[3],
make sure it has all the same dimensions as the tensor you want to filter
because the - is join’ed on the target tensor, i.e., non-common dimensions are dropped.
What if the tensor has an indexed dimension?
filter_subspaces(tensor(a{},x[2]):{"q": [1,5], "w": [10,20]}, f(x)(x > 5))
=> tensor(a{},x[2]):{w:[10.0, 20.0]}i.e., at least one value within the indexed dimension should pass the threshold.
Final Thoughts¶
I believe filtering on some threshold is a pretty common use case of filter_subspaces,
and this trick should be mentioned somewhere.
Or maybe even become a built-in function.
P.S.¶
For more tensor fun, check The Advent fo Tensors
Also, there is another approach of filtering by a dynamic value in the Tensor Playground with masking.
Yeah, like back in the calculus class, we’ve been adding and then subtracting 1 to rewrite the expression.
i.e., a collection of thresholds.