From 852108790beef2df7707ebcfa1706afd326dd9ad Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Fri, 10 Aug 2018 13:13:38 +0200 Subject: Rename (#6543) --- container-dev/pom.xml | 2 +- model-evaluation/pom.xml | 96 ++++++ .../vespa/models/evaluation/FunctionEvaluator.java | 57 ++++ .../vespa/models/evaluation/FunctionReference.java | 76 +++++ .../vespa/models/evaluation/LazyArrayContext.java | 213 ++++++++++++++ .../java/ai/vespa/models/evaluation/LazyValue.java | 154 ++++++++++ .../java/ai/vespa/models/evaluation/Model.java | 132 +++++++++ .../vespa/models/evaluation/ModelsEvaluator.java | 46 +++ .../evaluation/RankProfilesConfigImporter.java | 86 ++++++ .../models/handler/ModelsEvaluationHandler.java | 57 ++++ .../models/evaluation/ModelsEvaluatorTest.java | 51 ++++ .../evaluation/RankProfilesImporterTest.java | 60 ++++ .../config/rankexpression/rank-profiles.cfg | 296 +++++++++++++++++++ .../config/rankexpression/rankexpression.sd | 327 +++++++++++++++++++++ model-inference/pom.xml | 96 ------ .../vespa/models/evaluation/FunctionEvaluator.java | 57 ---- .../vespa/models/evaluation/FunctionReference.java | 76 ----- .../vespa/models/evaluation/LazyArrayContext.java | 213 -------------- .../java/ai/vespa/models/evaluation/LazyValue.java | 154 ---------- .../java/ai/vespa/models/evaluation/Model.java | 132 --------- .../vespa/models/evaluation/ModelsEvaluator.java | 46 --- .../evaluation/RankProfilesConfigImporter.java | 86 ------ .../models/handler/ModelsEvaluationHandler.java | 57 ---- .../models/evaluation/ModelsEvaluatorTest.java | 51 ---- .../evaluation/RankProfilesImporterTest.java | 60 ---- .../config/rankexpression/rank-profiles.cfg | 296 ------------------- .../config/rankexpression/rankexpression.sd | 327 --------------------- pom.xml | 2 +- 28 files changed, 1653 insertions(+), 1653 deletions(-) create mode 100644 model-evaluation/pom.xml create mode 100644 model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java create mode 100644 model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionReference.java create mode 100644 model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java create mode 100644 model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyValue.java create mode 100644 model-evaluation/src/main/java/ai/vespa/models/evaluation/Model.java create mode 100644 model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java create mode 100644 model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java create mode 100644 model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java create mode 100644 model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java create mode 100644 model-evaluation/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java create mode 100644 model-evaluation/src/test/resources/config/rankexpression/rank-profiles.cfg create mode 100644 model-evaluation/src/test/resources/config/rankexpression/rankexpression.sd delete mode 100644 model-inference/pom.xml delete mode 100644 model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java delete mode 100644 model-inference/src/main/java/ai/vespa/models/evaluation/FunctionReference.java delete mode 100644 model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java delete mode 100644 model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java delete mode 100644 model-inference/src/main/java/ai/vespa/models/evaluation/Model.java delete mode 100644 model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java delete mode 100644 model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java delete mode 100644 model-inference/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java delete mode 100644 model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java delete mode 100644 model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java delete mode 100644 model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg delete mode 100644 model-inference/src/test/resources/config/rankexpression/rankexpression.sd diff --git a/container-dev/pom.xml b/container-dev/pom.xml index 3b0d7a25ece..c4c93563026 100644 --- a/container-dev/pom.xml +++ b/container-dev/pom.xml @@ -88,7 +88,7 @@ com.yahoo.vespa - model-inference + model-evaluation ${project.version} diff --git a/model-evaluation/pom.xml b/model-evaluation/pom.xml new file mode 100644 index 00000000000..6fdc25f3786 --- /dev/null +++ b/model-evaluation/pom.xml @@ -0,0 +1,96 @@ + + + + 4.0.0 + + com.yahoo.vespa + parent + 6-SNAPSHOT + ../parent/pom.xml + + model-evaluation + 6-SNAPSHOT + container-plugin + + + junit + junit + test + + + com.yahoo.vespa + component + ${project.version} + provided + + + com.yahoo.vespa + jdisc_core + ${project.version} + provided + + + com.yahoo.vespa + container-core + ${project.version} + provided + + + com.yahoo.vespa + config + ${project.version} + provided + + + com.yahoo.vespa + configdefinitions + ${project.version} + provided + + + com.yahoo.vespa + vespajlib + ${project.version} + provided + + + com.yahoo.vespa + searchlib + ${project.version} + provided + + + com.google.guava + guava + provided + + + + + + com.yahoo.vespa + bundle-plugin + true + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${project.artifactId} + ${project.version} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java new file mode 100644 index 00000000000..4acd6e483b4 --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java @@ -0,0 +1,57 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.yahoo.searchlib.rankingexpression.ExpressionFunction; +import com.yahoo.searchlib.rankingexpression.evaluation.TensorValue; +import com.yahoo.tensor.Tensor; +import com.yahoo.tensor.TensorType; + +/** + * An evaluator which can be used to evaluate a single function once. + * + * @author bratseth + */ +// This wraps all access to the context and the ranking expression to avoid incorrect usage +public class FunctionEvaluator { + + private final ExpressionFunction function; + private final LazyArrayContext context; + private boolean evaluated = false; + + FunctionEvaluator(ExpressionFunction function, LazyArrayContext context) { + this.function = function; + this.context = context; + } + + /** + * Binds the given variable referred in this expression to the given value. + * + * @param name the variable to bind + * @param value the value this becomes bound to + * @return this for chaining + */ + public FunctionEvaluator bind(String name, Tensor value) { + if (evaluated) + throw new IllegalStateException("You cannot bind a value in a used evaluator"); + context.put(name, new TensorValue(value)); + return this; + } + + /** + * Binds the given variable referred in this expression to the given value. + * This is equivalent to bind(name, Tensor.Builder.of(TensorType.empty).cell(value).build()) + * + * @param name the variable to bind + * @param value the value this becomes bound to + * @return this for chaining + */ + public FunctionEvaluator bind(String name, double value) { + return bind(name, Tensor.Builder.of(TensorType.empty).cell(value).build()); + } + + public Tensor evaluate() { + evaluated = true; + return function.getBody().evaluate(context).asTensor(); + } + +} diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionReference.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionReference.java new file mode 100644 index 00000000000..3b50cef6e2e --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/FunctionReference.java @@ -0,0 +1,76 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A reference to a function. + * The function may be + * - free: Callable from users of models, or + * - bound: Representing a specific invocation from another ranking expression. + * In bound functions, any arguments are replaced by the values supplied in the function invocation. + * Function references has a serial form (textual representation) used in ranking expressions received in ranking + * expression configurations. + * + * This is immutable. + * + * @author bratseth + */ +class FunctionReference { + + private static final Pattern referencePattern = + Pattern.compile("rankingExpression\\(([a-zA-Z0-9_]+)(@[a-f0-9]+\\.[a-f0-9]+)?\\)(\\.rankingScript)?"); + + /** The name of the function referenced */ + private final String name; + + /** The id of the specific invocation of the function, or null if it is free */ + private final String instance; + + private FunctionReference(String name, String instance) { + this.name = name; + this.instance = instance; + } + + /** Returns the name of the function referenced */ + String functionName() { return name; } + + boolean isFree() { + return instance == null; + } + + String serialForm() { + return "rankingExpression(" + name + (instance != null ? instance : "") + ")"; + } + + @Override + public String toString() { return "reference to function '" + name + "'" + + ( instance != null ? " instance '" + instance + "'" : ""); } + + @Override + public int hashCode() { return Objects.hash(name, instance); } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if ( ! (o instanceof FunctionReference)) return false; + FunctionReference other = (FunctionReference)o; + if ( ! Objects.equals(this.name, other.name)) return false; + if ( ! Objects.equals(this.instance, other.instance)) return false; + return true; + } + + /** Returns a function reference from the given serial form, or empty if the string is not a valid reference */ + static Optional fromSerial(String serialForm) { + Matcher expressionMatcher = referencePattern.matcher(serialForm); + if ( ! expressionMatcher.matches()) return Optional.empty(); + + String name = expressionMatcher.group(1); + String instance = expressionMatcher.group(2); + return Optional.of(new FunctionReference(name, instance)); + } + +} diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java new file mode 100644 index 00000000000..2dcfd204077 --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java @@ -0,0 +1,213 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.google.common.collect.ImmutableMap; +import com.yahoo.searchlib.rankingexpression.ExpressionFunction; +import com.yahoo.searchlib.rankingexpression.RankingExpression; +import com.yahoo.searchlib.rankingexpression.Reference; +import com.yahoo.searchlib.rankingexpression.evaluation.Context; +import com.yahoo.searchlib.rankingexpression.evaluation.ContextIndex; +import com.yahoo.searchlib.rankingexpression.evaluation.DoubleValue; +import com.yahoo.searchlib.rankingexpression.evaluation.Value; +import com.yahoo.searchlib.rankingexpression.rule.CompositeNode; +import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode; +import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode; +import com.yahoo.tensor.TensorType; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * An array context supporting functions invocations implemented as lazy values. + * + * @author bratseth + */ +final class LazyArrayContext extends Context implements ContextIndex { + + private final IndexedBindings indexedBindings; + + private LazyArrayContext(IndexedBindings indexedBindings) { + this.indexedBindings = indexedBindings.copy(this); + } + + /** + * Create a fast lookup, lazy context for an expression. + * + * @param expression the expression to create a context for + */ + LazyArrayContext(RankingExpression expression, Map functions, Model model) { + this.indexedBindings = new IndexedBindings(expression, functions, this, model); + } + + /** + * Puts a value by name. + * The value will be frozen if it isn't already. + * + * @throws IllegalArgumentException if the name is not present in the ranking expression this was created with, and + * ignoredUnknownValues is false + */ + @Override + public void put(String name, Value value) { + put(requireIndexOf(name), value); + } + + /** Same as put(index,DoubleValue.frozen(value)) */ + public final void put(int index, double value) { + put(index, DoubleValue.frozen(value)); + } + + /** + * Puts a value by index. + * The value will be frozen if it isn't already. + */ + public void put(int index, Value value) { + indexedBindings.set(index, value.freeze()); + } + + @Override + public TensorType getType(Reference reference) { + // TODO: Add type information so we do not need to evaluate to get this + return get(requireIndexOf(reference.toString())).type(); + } + + /** Perform a slow lookup by name */ + @Override + public Value get(String name) { + return get(requireIndexOf(name)); + } + + /** Perform a fast lookup by index */ + @Override + public Value get(int index) { + return indexedBindings.get(index); + } + + @Override + public double getDouble(int index) { + double value = get(index).asDouble(); + if (value == Double.NaN) + throw new UnsupportedOperationException("Value at " + index + " has no double representation"); + return value; + } + + @Override + public int getIndex(String name) { + return requireIndexOf(name); + } + + @Override + public int size() { + return indexedBindings.names().size(); + } + + @Override + public Set names() { return indexedBindings.names(); } + + private Integer requireIndexOf(String name) { + Integer index = indexedBindings.indexOf(name); + if (index == null) + throw new IllegalArgumentException("Value '" + name + "' can not be bound in " + this); + return index; + } + + /** + * Creates a copy of this context suitable for evaluating against the same ranking expression + * in a different thread or for re-binding free variables. + */ + LazyArrayContext copy() { + return new LazyArrayContext(indexedBindings); + } + + private static class IndexedBindings { + + /** The mapping from variable name to index */ + private final ImmutableMap nameToIndex; + + /** The current values set, pre-converted to doubles */ + private final Value[] values; + + private IndexedBindings(ImmutableMap nameToIndex, Value[] values) { + this.nameToIndex = nameToIndex; + this.values = values; + } + + /** + * Creates indexed bindings for the given expressions. + * The given expression and functions may be inspected but cannot be stored. + */ + IndexedBindings(RankingExpression expression, + Map functions, + LazyArrayContext owner, + Model model) { + Set bindTargets = new LinkedHashSet<>(); + extractBindTargets(expression.getRoot(), functions, bindTargets); + + values = new Value[bindTargets.size()]; + Arrays.fill(values, DoubleValue.zero); + + int i = 0; + ImmutableMap.Builder nameToIndexBuilder = new ImmutableMap.Builder<>(); + for (String variable : bindTargets) + nameToIndexBuilder.put(variable,i++); + nameToIndex = nameToIndexBuilder.build(); + + for (Map.Entry function : functions.entrySet()) { + Integer index = nameToIndex.get(function.getKey().serialForm()); + if (index != null) // Referenced in this, so bind it + values[index] = new LazyValue(function.getKey(), owner, model); + } + } + + private void extractBindTargets(ExpressionNode node, + Map functions, + Set bindTargets) { + if (isFunctionReference(node)) { + FunctionReference reference = FunctionReference.fromSerial(node.toString()).get(); + bindTargets.add(reference.serialForm()); + + extractBindTargets(functions.get(reference).getBody().getRoot(), functions, bindTargets); + } + else if (isConstant(node)) { + // Ignore + } + else if (node instanceof ReferenceNode) { + bindTargets.add(node.toString()); + } + else if (node instanceof CompositeNode) { + CompositeNode cNode = (CompositeNode)node; + for (ExpressionNode child : cNode.children()) + extractBindTargets(child, functions, bindTargets); + } + } + + private boolean isFunctionReference(ExpressionNode node) { + if ( ! (node instanceof ReferenceNode)) return false; + + ReferenceNode reference = (ReferenceNode)node; + return reference.getName().equals("rankingExpression") && reference.getArguments().size() == 1; + } + + private boolean isConstant(ExpressionNode node) { + if ( ! (node instanceof ReferenceNode)) return false; + + ReferenceNode reference = (ReferenceNode)node; + return reference.getName().equals("value") && reference.getArguments().size() == 1; + } + + Value get(int index) { return values[index]; } + void set(int index, Value value) { values[index] = value; } + Set names() { return nameToIndex.keySet(); } + Integer indexOf(String name) { return nameToIndex.get(name); } + + IndexedBindings copy(Context context) { + Value[] valueCopy = new Value[values.length]; + for (int i = 0; i < values.length; i++) + valueCopy[i] = values[i] instanceof LazyValue ? ((LazyValue)values[i]).copyFor(context) : values[i]; + return new IndexedBindings(nameToIndex, valueCopy); + } + + } + +} diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyValue.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyValue.java new file mode 100644 index 00000000000..4a1ee22d288 --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/LazyValue.java @@ -0,0 +1,154 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.yahoo.searchlib.rankingexpression.ExpressionFunction; +import com.yahoo.searchlib.rankingexpression.evaluation.Context; +import com.yahoo.searchlib.rankingexpression.evaluation.Value; +import com.yahoo.searchlib.rankingexpression.rule.Function; +import com.yahoo.searchlib.rankingexpression.rule.TruthOperator; +import com.yahoo.tensor.Tensor; +import com.yahoo.tensor.TensorType; + +/** + * A Value which is computed from an expression when first requested. + * This is not multithread safe. + * + * @author bratseth + */ +class LazyValue extends Value { + + /** The reference to the function computing the value of this */ + private final FunctionReference function; + + /** The context used to compute the function of this */ + private final Context context; + + /** The model this is part of */ + private final Model model; + + private Value computedValue = null; + + public LazyValue(FunctionReference function, Context context, Model model) { + this.function = function; + this.context = context; + this.model = model; + } + + private Value computedValue() { + if (computedValue == null) + computedValue = model.requireReferencedFunction(function).getBody().evaluate(context); + return computedValue; + } + + @Override + public TensorType type() { + return computedValue().type(); // TODO: Keep type information in this/ExpressionFunction to avoid computing here + } + + @Override + public double asDouble() { + return computedValue().asDouble(); + } + + @Override + public Tensor asTensor() { + return computedValue().asTensor(); + } + + @Override + public boolean hasDouble() { + return type().rank() == 0; + } + + @Override + public boolean asBoolean() { + return computedValue().asBoolean(); + } + + @Override + public Value negate() { + return computedValue().negate(); + } + + @Override + public Value add(Value value) { + return computedValue().add(value); + } + + @Override + public Value subtract(Value value) { + return computedValue().subtract(value); + } + + @Override + public Value multiply(Value value) { + return computedValue().multiply(value); + } + + @Override + public Value divide(Value value) { + return computedValue().divide(value); + } + + @Override + public Value modulo(Value value) { + return computedValue().modulo(value); + } + + @Override + public Value and(Value value) { + return computedValue().and(value); + } + + @Override + public Value or(Value value) { + return computedValue().or(value); + } + + @Override + public Value not() { + return computedValue().not(); + } + + @Override + public Value power(Value value) { + return computedValue().power(value); + } + + @Override + public Value compare(TruthOperator operator, Value value) { + return computedValue().compare(operator, value); + } + + @Override + public Value function(Function function, Value value) { + return computedValue().function(function, value); + } + + @Override + public Value asMutable() { + return computedValue().asMutable(); + } + + @Override + public String toString() { + return "value of " + function; + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof Value)) return false; + return computedValue().equals(other); + } + + @Override + public int hashCode() { + return computedValue().hashCode(); + } + + LazyValue copyFor(Context context) { + return new LazyValue(this.function, context, model); + } + +} diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/Model.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/Model.java new file mode 100644 index 00000000000..ca739195867 --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/Model.java @@ -0,0 +1,132 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.yahoo.searchlib.rankingexpression.ExpressionFunction; +import com.yahoo.searchlib.rankingexpression.evaluation.ContextIndex; +import com.yahoo.searchlib.rankingexpression.evaluation.ExpressionOptimizer; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * A named collection of functions + * + * @author bratseth + */ +public class Model { + + private final String name; + + /** Free functions */ + private final ImmutableList functions; + + /** Instances of each usage of the above function, where variables (if any) are replaced by their bindings */ + private final ImmutableMap referencedFunctions; + + /** Context prototypes, indexed by function name (as all invocations of the same function share the same context prototype) */ + private final ImmutableMap contextPrototypes; + + private final ExpressionOptimizer expressionOptimizer = new ExpressionOptimizer(); + + public Model(String name, Collection functions) { + this(name, functions, Collections.emptyMap()); + } + + Model(String name, Collection functions, Map referencedFunctions) { + // TODO: Optimize functions + this.name = name; + this.functions = ImmutableList.copyOf(functions); + + ImmutableMap.Builder contextBuilder = new ImmutableMap.Builder<>(); + for (ExpressionFunction function : functions) { + try { + contextBuilder.put(function.getName(), new LazyArrayContext(function.getBody(), referencedFunctions, this)); + } + catch (RuntimeException e) { + throw new IllegalArgumentException("Could not prepare an evaluation context for " + function, e); + } + } + this.contextPrototypes = contextBuilder.build(); + + ImmutableMap.Builder functionsBuilder = new ImmutableMap.Builder<>(); + for (Map.Entry function : referencedFunctions.entrySet()) { + ExpressionFunction optimizedFunction = optimize(function.getValue(), + contextPrototypes.get(function.getKey().functionName())); + functionsBuilder.put(function.getKey(), optimizedFunction); + } + this.referencedFunctions = functionsBuilder.build(); + } + + /** Returns an optimized version of the given function */ + private ExpressionFunction optimize(ExpressionFunction function, ContextIndex context) { + // Note: Optimization is in-place but we do not depend on that outside this method + expressionOptimizer.optimize(function.getBody(), context); + return function; + } + + public String name() { return name; } + + /** Returns an immutable list of the free functions of this */ + public List functions() { return functions; } + + /** Returns the given function, or throws a IllegalArgumentException if it does not exist */ + ExpressionFunction requireFunction(String name) { + ExpressionFunction function = function(name); + if (function == null) + throw new IllegalArgumentException("No function named '" + name + "' in " + this + ". Available functions: " + + functions.stream().map(f -> f.getName()).collect(Collectors.joining(", "))); + return function; + } + + /** Returns the given function, or throws a IllegalArgumentException if it does not exist */ + private LazyArrayContext requireContextProprotype(String name) { + LazyArrayContext context = contextPrototypes.get(name); + if (context == null) // Implies function is not present + throw new IllegalArgumentException("No function named '" + name + "' in " + this + ". Available functions: " + + functions.stream().map(f -> f.getName()).collect(Collectors.joining(", "))); + return context; + } + + /** Returns the function withe the given name, or null if none */ // TODO: Parameter overloading? + ExpressionFunction function(String name) { + for (ExpressionFunction function : functions) + if (function.getName().equals(name)) + return function; + return null; + } + + /** Returns an immutable map of the referenced function instances of this */ + Map referencedFunctions() { return referencedFunctions; } + + /** Returns the given referred function, or throws a IllegalArgumentException if it does not exist */ + ExpressionFunction requireReferencedFunction(FunctionReference reference) { + ExpressionFunction function = referencedFunctions.get(reference); + if (function == null) + throw new IllegalArgumentException("No " + reference + " in " + this + ". References: " + + referencedFunctions.keySet().stream() + .map(FunctionReference::serialForm) + .collect(Collectors.joining(", "))); + return function; + } + + /** + * Returns an evaluator which can be used to evaluate the given function in a single thread once. + + * Usage: + * Tensor result = model.evaluatorOf("myFunction").bind("foo", value).bind("bar", value).evaluate() + * + * @throws IllegalArgumentException if the function is not present + */ + public FunctionEvaluator evaluatorOf(String function) { // TODO: Parameter overloading? + return new FunctionEvaluator(requireFunction(function), requireContextProprotype(function).copy()); + } + + @Override + public String toString() { return "model '" + name + "'"; } + +} diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java new file mode 100644 index 00000000000..b36e06e5505 --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java @@ -0,0 +1,46 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.google.common.collect.ImmutableMap; +import com.yahoo.vespa.config.search.RankProfilesConfig; + +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Evaluates machine-learned models added to Vespa applications and available as config form. + * Usage: + * Tensor result = evaluator.bind("foo", value).bind("bar", value").evaluate() + * + * @author bratseth + */ +public class ModelsEvaluator { + + private final ImmutableMap models; + + public ModelsEvaluator(RankProfilesConfig config) { + models = ImmutableMap.copyOf(new RankProfilesConfigImporter().importFrom(config)); + } + + /** Returns the models of this as an immutable map */ + public Map models() { return models; } + + /** + * Returns a function which can be used to evaluate the given function in the given model + * + * @throws IllegalArgumentException if the function or model is not present + */ + public FunctionEvaluator evaluatorOf(String modelName, String functionName) { + return requireModel(modelName).evaluatorOf(functionName); + } + + /** Returns the given model, or throws a IllegalArgumentException if it does not exist */ + Model requireModel(String name) { + Model model = models.get(name); + if (model == null) + throw new IllegalArgumentException("No model named '" + name + ". Available models: " + + models.keySet().stream().collect(Collectors.joining(", "))); + return model; + } + +} diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java new file mode 100644 index 00000000000..bfd6342218a --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java @@ -0,0 +1,86 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.yahoo.searchlib.rankingexpression.ExpressionFunction; +import com.yahoo.searchlib.rankingexpression.RankingExpression; +import com.yahoo.searchlib.rankingexpression.parser.ParseException; +import com.yahoo.vespa.config.search.RankProfilesConfig; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Converts RankProfilesConfig instances to RankingExpressions for evaluation + * + * @author bratseth + */ +class RankProfilesConfigImporter { + + /** + * Returns a map of the models contained in this config, indexed on name. + * The map is modifiable and owned by the caller. + */ + Map importFrom(RankProfilesConfig config) { + try { + Map models = new HashMap<>(); + for (RankProfilesConfig.Rankprofile profile : config.rankprofile()) { + Model model = importProfile(profile); + models.put(model.name(), model); + } + return models; + } + catch (ParseException e) { + throw new IllegalArgumentException("Could not read rank profiles config - version mismatch?", e); + } + } + + private Model importProfile(RankProfilesConfig.Rankprofile profile) throws ParseException { + List functions = new ArrayList<>(); + Map referencedFunctions = new HashMap<>(); + ExpressionFunction firstPhase = null; + ExpressionFunction secondPhase = null; + for (RankProfilesConfig.Rankprofile.Fef.Property property : profile.fef().property()) { + Optional reference = FunctionReference.fromSerial(property.name()); + if ( reference.isPresent()) { + List arguments = new ArrayList<>(); // TODO: Arguments? + RankingExpression expression = new RankingExpression(reference.get().functionName(), property.value()); + + if (reference.get().isFree()) // make available in model under configured name + functions.add(new ExpressionFunction(reference.get().functionName(), arguments, expression)); // + + // Make all functions, bound or not available under the name they are referenced by in expressions + referencedFunctions.put(reference.get(), new ExpressionFunction(reference.get().serialForm(), arguments, expression)); + } + else if (property.name().equals("vespa.rank.firstphase")) { // Include in addition to macros + firstPhase = new ExpressionFunction("firstphase", new ArrayList<>(), + new RankingExpression("first-phase", property.value())); + } + else if (property.name().equals("vespa.rank.secondphase")) { // Include in addition to macros + secondPhase = new ExpressionFunction("secondphase", new ArrayList<>(), + new RankingExpression("second-phase", property.value())); + } + } + if (functionByName("firstphase", functions) == null && firstPhase != null) // may be already included, depending on body + functions.add(firstPhase); + if (functionByName("secondphase", functions) == null && secondPhase != null) // may be already included, depending on body + functions.add(secondPhase); + + try { + return new Model(profile.name(), functions, referencedFunctions); + } + catch (RuntimeException e) { + throw new IllegalArgumentException("Could not load model '" + profile.name() + "'", e); + } + } + + private ExpressionFunction functionByName(String name, List functions) { + for (ExpressionFunction function : functions) + if (function.getName().equals(name)) + return function; + return null; + } + +} diff --git a/model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java b/model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java new file mode 100644 index 00000000000..78c46864d7b --- /dev/null +++ b/model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java @@ -0,0 +1,57 @@ +package ai.vespa.models.handler; + +import ai.vespa.models.evaluation.ModelsEvaluator; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.tensor.Tensor; +import com.yahoo.tensor.serialization.JsonFormat; + +import java.io.IOException; +import java.io.OutputStream; + +public class ModelsEvaluationHandler extends LoggingRequestHandler { + + private final ModelsEvaluator modelsEvaluator; + + public ModelsEvaluationHandler(ModelsEvaluator modelsEvaluator, Context context) { + super(context); + this.modelsEvaluator = modelsEvaluator; + } + + @Override + public HttpResponse handle(HttpRequest request) { + Tensor result = modelsEvaluator.evaluatorOf(property("model", "serving_default", request), + request.getProperty("function")) + .evaluate(); + return new RawResponse(JsonFormat.encode(result)); + } + + private String property(String name, String defaultValue, HttpRequest request) { + String value = request.getProperty(name); + if (value == null) return defaultValue; + return value; + } + + private static class RawResponse extends HttpResponse { + + private final byte[] data; + + RawResponse(byte[] data) { + super(200); + this.data = data; + } + + @Override + public String getContentType() { + return "application/json"; + } + + @Override + public void render(OutputStream outputStream) throws IOException { + outputStream.write(data); + } + } + +} + diff --git a/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java b/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java new file mode 100644 index 00000000000..60cf0d25ded --- /dev/null +++ b/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java @@ -0,0 +1,51 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.yahoo.config.subscription.ConfigGetter; +import com.yahoo.config.subscription.FileSource; +import com.yahoo.tensor.Tensor; +import com.yahoo.vespa.config.search.RankProfilesConfig; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.assertEquals; + +/** + * @author bratseth + */ +public class ModelsEvaluatorTest { + + private static final double delta = 0.00000000001; + + private ModelsEvaluator createModels() { + String configPath = "src/test/resources/config/rankexpression/rank-profiles.cfg"; + RankProfilesConfig config = new ConfigGetter<>(new FileSource(new File(configPath)), RankProfilesConfig.class).getConfig(""); + return new ModelsEvaluator(config); + } + + @Test + public void testTensorEvaluation() { + ModelsEvaluator models = createModels(); + FunctionEvaluator function = models.evaluatorOf("macros", "fourtimessum"); + function.bind("var1", Tensor.from("{{x:0}:3,{x:1}:5}")); + function.bind("var2", Tensor.from("{{x:0}:7,{x:1}:11}")); + assertEquals(Tensor.from("{{x:0}:40.0,{x:1}:64.0}"), function.evaluate()); + } + + @Test + public void testEvaluationDependingOnMacroTakingArguments() { + ModelsEvaluator models = createModels(); + FunctionEvaluator function = models.evaluatorOf("macros", "secondphase"); + function.bind("match", 3); + function.bind("rankBoost", 5); + assertEquals(32.0, function.evaluate().asDouble(), delta); + } + + // TODO: Test argument-less function + // TODO: Test that binding nonexisting variable doesn't work + // TODO: Test that rebinding doesn't work + // TODO: Test with nested macros + // TODO: Test TF/ONNX model + +} diff --git a/model-evaluation/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java b/model-evaluation/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java new file mode 100644 index 00000000000..d45372fc7da --- /dev/null +++ b/model-evaluation/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java @@ -0,0 +1,60 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package ai.vespa.models.evaluation; + +import com.yahoo.config.subscription.ConfigGetter; +import com.yahoo.config.subscription.FileSource; +import com.yahoo.searchlib.rankingexpression.ExpressionFunction; +import com.yahoo.vespa.config.search.RankProfilesConfig; +import org.junit.Test; + +import java.io.File; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Tests instantiating models from rank-profiles configs. + * + * @author bratseth + */ +public class RankProfilesImporterTest { + + @Test + public void testImporting() { + String configPath = "src/test/resources/config/rankexpression/rank-profiles.cfg"; + RankProfilesConfig config = new ConfigGetter<>(new FileSource(new File(configPath)), RankProfilesConfig.class).getConfig(""); + Map models = new RankProfilesConfigImporter().importFrom(config); + assertEquals(18, models.size()); + + Model macros = models.get("macros"); + assertNotNull(macros); + assertEquals("macros", macros.name()); + assertEquals(4, macros.functions().size()); + assertFunction("fourtimessum", "4 * (var1 + var2)", macros); + assertFunction("firstphase", "match + fieldMatch(title) + rankingExpression(myfeature)", macros); + assertFunction("secondphase", "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)", macros); + assertFunction("myfeature", + "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + " + + "30 * pow(0 - fieldMatch(description).earliness,2)", + macros); + assertEquals(4, macros.referencedFunctions().size()); + assertBoundFunction("rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)", + "4 * (match + rankBoost)", macros); + } + + private void assertFunction(String name, String expression, Model model) { + ExpressionFunction function = model.function(name); + assertNotNull(function); + assertEquals(name, function.getName()); + assertEquals(expression, function.getBody().getRoot().toString()); + } + + private void assertBoundFunction(String name, String expression, Model model) { + ExpressionFunction function = model.referencedFunctions().get(FunctionReference.fromSerial(name).get()); + assertNotNull("Function '" + name + "' is present", function); + assertEquals(name, function.getName()); + assertEquals(expression, function.getBody().getRoot().toString()); + } + +} diff --git a/model-evaluation/src/test/resources/config/rankexpression/rank-profiles.cfg b/model-evaluation/src/test/resources/config/rankexpression/rank-profiles.cfg new file mode 100644 index 00000000000..f5652c31d2a --- /dev/null +++ b/model-evaluation/src/test/resources/config/rankexpression/rank-profiles.cfg @@ -0,0 +1,296 @@ +rankprofile[0].name "default" +rankprofile[0].fef.property[0].name "foo" +rankprofile[0].fef.property[0].value "bar, baz" +rankprofile[0].fef.property[1].name "foo" +rankprofile[0].fef.property[1].value "foobar" +rankprofile[0].fef.property[2].name "qux" +rankprofile[0].fef.property[2].value "quux" +rankprofile[0].fef.property[3].name "foo.bar" +rankprofile[0].fef.property[3].value "foo.bar" +rankprofile[0].fef.property[4].name "foo.bar.baz" +rankprofile[0].fef.property[4].value "123" +rankprofile[0].fef.property[5].name "foo(bar).baz.2" +rankprofile[0].fef.property[5].value "123.4" +rankprofile[0].fef.property[6].name "foo(bar).baz.qux" +rankprofile[0].fef.property[6].value "foo(bar)" +rankprofile[0].fef.property[7].name "nud" +rankprofile[0].fef.property[7].value "ity" +rankprofile[0].fef.property[8].name "vespa.rank.firstphase" +rankprofile[0].fef.property[8].value "classicRank" +rankprofile[0].fef.property[9].name "vespa.rank.secondphase" +rankprofile[0].fef.property[9].value "rankingExpression(secondphase)" +rankprofile[0].fef.property[10].name "rankingExpression(secondphase).rankingScript" +rankprofile[0].fef.property[10].value "4" +rankprofile[0].fef.property[11].name "vespa.dump.feature" +rankprofile[0].fef.property[11].value "attribute(foo1).out" +rankprofile[0].fef.property[12].name "vespa.dump.feature" +rankprofile[0].fef.property[12].value "attribute(bar1)" +rankprofile[0].fef.property[13].name "vespa.dump.feature" +rankprofile[0].fef.property[13].value "attribute(foo2).out" +rankprofile[0].fef.property[14].name "vespa.dump.feature" +rankprofile[0].fef.property[14].value "attribute(bar2).out" +rankprofile[0].fef.property[15].name "vespa.dump.feature" +rankprofile[0].fef.property[15].value "attribute(foo3).out" +rankprofile[0].fef.property[16].name "vespa.dump.feature" +rankprofile[0].fef.property[16].value "attribute(bar3).out" +rankprofile[0].fef.property[17].name "vespa.dump.feature" +rankprofile[0].fef.property[17].value "attribute(foo4).out" +rankprofile[0].fef.property[18].name "vespa.dump.feature" +rankprofile[0].fef.property[18].value "attribute(bar4).out" +rankprofile[0].fef.property[19].name "vespa.hitcollector.heapsize" +rankprofile[0].fef.property[19].value "10" +rankprofile[0].fef.property[20].name "vespa.hitcollector.arraysize" +rankprofile[0].fef.property[20].value "20" +rankprofile[0].fef.property[21].name "vespa.hitcollector.rankscoredroplimit" +rankprofile[0].fef.property[21].value "-0.5" +rankprofile[0].fef.property[22].name "vespa.dump.ignoredefaultfeatures" +rankprofile[0].fef.property[22].value "true" +rankprofile[1].name "unranked" +rankprofile[1].fef.property[0].name "vespa.rank.firstphase" +rankprofile[1].fef.property[0].value "value(0)" +rankprofile[1].fef.property[1].name "vespa.hitcollector.heapsize" +rankprofile[1].fef.property[1].value "0" +rankprofile[1].fef.property[2].name "vespa.hitcollector.arraysize" +rankprofile[1].fef.property[2].value "0" +rankprofile[1].fef.property[3].name "vespa.dump.ignoredefaultfeatures" +rankprofile[1].fef.property[3].value "true" +rankprofile[2].name "static" +rankprofile[2].fef.property[0].name "vespa.rank.firstphase" +rankprofile[2].fef.property[0].value "attribute" +rankprofile[2].fef.property[1].name "vespa.rank.secondphase" +rankprofile[2].fef.property[1].value "rankingExpression(secondphase)" +rankprofile[2].fef.property[2].name "rankingExpression(secondphase).rankingScript" +rankprofile[2].fef.property[2].value "10 + feature(arg1).out.out" +rankprofile[2].fef.property[3].name "vespa.summary.feature" +rankprofile[2].fef.property[3].value "attribute(foo1).out" +rankprofile[2].fef.property[4].name "vespa.summary.feature" +rankprofile[2].fef.property[4].value "attribute(bar1)" +rankprofile[2].fef.property[5].name "vespa.summary.feature" +rankprofile[2].fef.property[5].value "attribute(foo2).out" +rankprofile[2].fef.property[6].name "vespa.summary.feature" +rankprofile[2].fef.property[6].value "attribute(bar2).out" +rankprofile[2].fef.property[7].name "vespa.summary.feature" +rankprofile[2].fef.property[7].value "attribute(foo3).out" +rankprofile[2].fef.property[8].name "vespa.summary.feature" +rankprofile[2].fef.property[8].value "attribute(bar3).out" +rankprofile[2].fef.property[9].name "vespa.summary.feature" +rankprofile[2].fef.property[9].value "attribute(foo4).out" +rankprofile[2].fef.property[10].name "vespa.summary.feature" +rankprofile[2].fef.property[10].value "attribute(bar4).out" +rankprofile[3].name "overflow" +rankprofile[3].fef.property[0].name "vespa.rank.firstphase" +rankprofile[3].fef.property[0].value "rankingExpression(firstphase)" +rankprofile[3].fef.property[1].name "rankingExpression(firstphase).rankingScript" +rankprofile[3].fef.property[1].value "feature1(argument1,argument2,argument3,argument4).output + feature2(argument1,argument2,argument3,argument4).output + feature3(argument1,argument2,argument3,argument4).output + feature4(argument1,argument2,argument3,argument4).output + feature5(argument1,argument2,argument3,argument4).output + feature6(argument1,argument2,argument3,argument4).output + feature7(argument1,argument2,argument3,argument4).output + feature8(argument1,argument2,argument3,argument4).output + feature9(argument1,argument2,argument3,argument4).output + feature10(argument1,argument2,argument3,argument4).output + feature11(argument1,argument2,argument3,argument4).output + feature12(argument1,argument2,argument3,argument4).output + feature13(argument1,argument2,argument3,argument4).output + feature14(argument1,argument2,argument3,argument4).output + feature15(argument1,argument2,argument3,argument4).output + feature16(argument1,argument2,argument3,argument4).output + feature17(argument1,argument2,argument3,argument4).output + feature18(argument1,argument2,argument3,argument4).output + feature19(argument1,argument2,argument3,argument4).output + feature20(argument1,argument2,argument3,argument4).output + feature21(argument1,argument2,argument3,argument4).output + feature22(argument1,argument2,argument3,argument4).output + feature23(argument1,argument2,argument3,argument4).output + feature24(argument1,argument2,argument3,argument4).output + feature25(argument1,argument2,argument3,argument4).output + feature26(argument1,argument2,argument3,argument4).output + feature27(argument1,argument2,argument3,argument4).output + feature28(argument1,argument2,argument3,argument4).output + feature29(argument1,argument2,argument3,argument4).output + feature30(argument1,argument2,argument3,argument4).output + feature31(argument1,argument2,argument3,argument4).output + feature32(argument1,argument2,argument3,argument4).output + feature33(argument1,argument2,argument3,argument4).output + feature34(argument1,argument2,argument3,argument4).output + feature35(argument1,argument2,argument3,argument4).output + feature36(argument1,argument2,argument3,argument4).output + feature37(argument1,argument2,argument3,argument4).output + feature38(argument1,argument2,argument3,argument4).output + feature39(argument1,argument2,argument3,argument4).output + feature40(argument1,argument2,argument3,argument4).output + feature41(argument1,argument2,argument3,argument4).output + feature42(argument1,argument2,argument3,argument4).output + feature43(argument1,argument2,argument3,argument4).output + feature44(argument1,argument2,argument3,argument4).output + feature45(argument1,argument2,argument3,argument4).output + feature46(argument1,argument2,argument3,argument4).output + feature47(argument1,argument2,argument3,argument4).output + feature48(argument1,argument2,argument3,argument4).output + feature49(argument1,argument2,argument3,argument4).output + feature50(argument1,argument2,argument3,argument4).output + feature51(argument1,argument2,argument3,argument4).output + feature52(argument1,argument2,argument3,argument4).output + feature53(argument1,argument2,argument3,argument4).output + feature54(argument1,argument2,argument3,argument4).output + feature55(argument1,argument2,argument3,argument4).output + feature56(argument1,argument2,argument3,argument4).output + feature57(argument1,argument2,argument3,argument4).output + feature58(argument1,argument2,argument3,argument4).output + feature59(argument1,argument2,argument3,argument4).output + feature60(argument1,argument2,argument3,argument4).output + feature61(argument1,argument2,argument3,argument4).output + feature62(argument1,argument2,argument3,argument4).output + feature63(argument1,argument2,argument3,argument4).output + feature64(argument1,argument2,argument3,argument4).output + feature65(argument1,argument2,argument3,argument4).output + feature66(argument1,argument2,argument3,argument4).output + feature67(argument1,argument2,argument3,argument4).output + feature68(argument1,argument2,argument3,argument4).output + feature69(argument1,argument2,argument3,argument4).output + feature70(argument1,argument2,argument3,argument4).output + feature71(argument1,argument2,argument3,argument4).output + feature72(argument1,argument2,argument3,argument4).output + feature73(argument1,argument2,argument3,argument4).output + feature74(argument1,argument2,argument3,argument4).output + feature75(argument1,argument2,argument3,argument4).output + feature76(argument1,argument2,argument3,argument4).output + feature77(argument1,argument2,argument3,argument4).output + feature78(argument1,argument2,argument3,argument4).output + feature79(argument1,argument2,argument3,argument4).output + feature80(argument1,argument2,argument3,argument4).output + feature81(argument1,argument2,argument3,argument4).output + feature82(argument1,argument2,argument3,argument4).output + feature83(argument1,argument2,argument3,argument4).output + feature84(argument1,argument2,argument3,argument4).output + feature85(argument1,argument2,argument3,argument4).output + feature86(argument1,argument2,argument3,argument4).output + feature87(argument1,argument2,argument3,argument4).output + feature88(argument1,argument2,argument3,argument4).output + feature89(argument1,argument2,argument3,argument4).output + feature90(argument1,argument2,argument3,argument4).output + feature91(argument1,argument2,argument3,argument4).output + feature92(argument1,argument2,argument3,argument4).output + feature93(argument1,argument2,argument3,argument4).output + feature94(argument1,argument2,argument3,argument4).output + feature95(argument1,argument2,argument3,argument4).output + feature96(argument1,argument2,argument3,argument4).output + feature97(argument1,argument2,argument3,argument4).output + feature98(argument1,argument2,argument3,argument4).output + feature99(argument1,argument2,argument3,argument4).output + feature100(argument1,argument2,argument3,argument4).output + feature101(argument1,argument2,argument3,argument4).output + feature102(argument1,argument2,argument3,argument4).output + feature103(argument1,argument2,argument3,argument4).output + feature104(argument1,argument2,argument3,argument4).output + feature105(argument1,argument2,argument3,argument4).output + feature106(argument1,argument2,argument3,argument4).output + feature107(argument1,argument2,argument3,argument4).output + feature108(argument1,argument2,argument3,argument4).output + feature109(argument1,argument2,argument3,argument4).output + feature110(argument1,argument2,argument3,argument4).output + feature111(argument1,argument2,argument3,argument4).output + feature112(argument1,argument2,argument3,argument4).output + feature113(argument1,argument2,argument3,argument4).output + feature114(argument1,argument2,argument3,argument4).output + feature115(argument1,argument2,argument3,argument4).output + feature116(argument1,argument2,argument3,argument4).output + feature117(argument1,argument2,argument3,argument4).output + feature118(argument1,argument2,argument3,argument4).output + feature119(argument1,argument2,argument3,argument4).output + feature120(argument1,argument2,argument3,argument4).output + feature121(argument1,argument2,argument3,argument4).output + feature122(argument1,argument2,argument3,argument4).output + feature123(argument1,argument2,argument3,argument4).output + feature124(argument1,argument2,argument3,argument4).output + feature125(argument1,argument2,argument3,argument4).output + feature126(argument1,argument2,argument3,argument4).output + feature127(argument1,argument2,argument3,argument4).output + feature128(argument1,argument2,argument3,argument4).output + feature129(argument1,argument2,argument3,argument4).output + feature130(argument1,argument2,argument3,argument4).output + feature131(argument1,argument2,argument3,argument4).output + feature132(argument1,argument2,argument3,argument4).output + feature133(argument1,argument2,argument3,argument4).output + feature134(argument1,argument2,argument3,argument4).output + feature135(argument1,argument2,argument3,argument4).output + feature136(argument1,argument2,argument3,argument4).output + feature137(argument1,argument2,argument3,argument4).output + feature138(argument1,argument2,argument3,argument4).output + feature139(argument1,argument2,argument3,argument4).output + feature140(argument1,argument2,argument3,argument4).output + feature141(argument1,argument2,argument3,argument4).output + feature142(argument1,argument2,argument3,argument4).output + feature143(argument1,argument2,argument3,argument4).output + feature144(argument1,argument2,argument3,argument4).output + feature145(argument1,argument2,argument3,argument4).output + feature146(argument1,argument2,argument3,argument4).output + feature147(argument1,argument2,argument3,argument4).output + feature148(argument1,argument2,argument3,argument4).output + feature149(argument1,argument2,argument3,argument4).output + feature150(argument1,argument2,argument3,argument4).output + feature151(argument1,argument2,argument3,argument4).output + feature152(argument1,argument2,argument3,argument4).output + feature153(argument1,argument2,argument3,argument4).output + feature154(argument1,argument2,argument3,argument4).output + feature155(argument1,argument2,argument3,argument4).output + feature156(argument1,argument2,argument3,argument4).output + feature157(argument1,argument2,argument3,argument4).output + feature158(argument1,argument2,argument3,argument4).output + feature159(argument1,argument2,argument3,argument4).output + feature160(argument1,argument2,argument3,argument4).output + feature161(argument1,argument2,argument3,argument4).output + feature162(argument1,argument2,argument3,argument4).output + feature163(argument1,argument2,argument3,argument4).output + feature164(argument1,argument2,argument3,argument4).output + feature165(argument1,argument2,argument3,argument4).output + feature166(argument1,argument2,argument3,argument4).output + feature167(argument1,argument2,argument3,argument4).output + feature168(argument1,argument2,argument3,argument4).output + feature169(argument1,argument2,argument3,argument4).output + feature170(argument1,argument2,argument3,argument4).output + feature171(argument1,argument2,argument3,argument4).output + feature172(argument1,argument2,argument3,argument4).output + feature173(argument1,argument2,argument3,argument4).output + feature174(argument1,argument2,argument3,argument4).output + feature175(argument1,argument2,argument3,argument4).output + feature176(argument1,argument2,argument3,argument4).output + feature177(argument1,argument2,argument3,argument4).output + feature178(argument1,argument2,argument3,argument4).output + feature179(argument1,argument2,argument3,argument4).output + feature180(argument1,argument2,argument3,argument4).output + feature181(argument1,argument2,argument3,argument4).output + feature182(argument1,argument2,argument3,argument4).output + feature183(argument1,argument2,argument3,argument4).output + feature184(argument1,argument2,argument3,argument4).output + feature185(argument1,argument2,argument3,argument4).output + feature186(argument1,argument2,argument3,argument4).output + feature187(argument1,argument2,argument3,argument4).output + feature188(argument1,argument2,argument3,argument4).output + feature189(argument1,argument2,argument3,argument4).output + feature190(argument1,argument2,argument3,argument4).output + feature191(argument1,argument2,argument3,argument4).output + feature192(argument1,argument2,argument3,argument4).output + feature193(argument1,argument2,argument3,argument4).output + feature194(argument1,argument2,argument3,argument4).output + feature195(argument1,argument2,argument3,argument4).output + feature196(argument1,argument2,argument3,argument4).output + feature197(argument1,argument2,argument3,argument4).output + feature198(argument1,argument2,argument3,argument4).output + feature199(argument1,argument2,argument3,argument4).output + feature200(argument1,argument2,argument3,argument4).output + feature201(argument1,argument2,argument3,argument4).output + feature202(argument1,argument2,argument3,argument4).output + feature203(argument1,argument2,argument3,argument4).output + feature204(argument1,argument2,argument3,argument4).output + feature205(argument1,argument2,argument3,argument4).output + feature206(argument1,argument2,argument3,argument4).output + feature207(argument1,argument2,argument3,argument4).output + feature208(argument1,argument2,argument3,argument4).output + feature209(argument1,argument2,argument3,argument4).output + feature210(argument1,argument2,argument3,argument4).output + feature211(argument1,argument2,argument3,argument4).output + feature212(argument1,argument2,argument3,argument4).output + feature213(argument1,argument2,argument3,argument4).output + feature214(argument1,argument2,argument3,argument4).output + feature215(argument1,argument2,argument3,argument4).output + feature216(argument1,argument2,argument3,argument4).output + feature217(argument1,argument2,argument3,argument4).output + feature218(argument1,argument2,argument3,argument4).output + feature219(argument1,argument2,argument3,argument4).output + feature220(argument1,argument2,argument3,argument4).output + feature221(argument1,argument2,argument3,argument4).output + feature222(argument1,argument2,argument3,argument4).output + feature223(argument1,argument2,argument3,argument4).output + feature224(argument1,argument2,argument3,argument4).output + feature225(argument1,argument2,argument3,argument4).output + feature226(argument1,argument2,argument3,argument4).output + feature227(argument1,argument2,argument3,argument4).output + feature228(argument1,argument2,argument3,argument4).output + feature229(argument1,argument2,argument3,argument4).output + feature230(argument1,argument2,argument3,argument4).output + feature231(argument1,argument2,argument3,argument4).output + feature232(argument1,argument2,argument3,argument4).output + feature233(argument1,argument2,argument3,argument4).output + feature234(argument1,argument2,argument3,argument4).output + feature235(argument1,argument2,argument3,argument4).output + feature236(argument1,argument2,argument3,argument4).output + feature237(argument1,argument2,argument3,argument4).output + feature238(argument1,argument2,argument3,argument4).output + feature239(argument1,argument2,argument3,argument4).output + feature240(argument1,argument2,argument3,argument4).output + feature241(argument1,argument2,argument3,argument4).output + feature242(argument1,argument2,argument3,argument4).output + feature243(argument1,argument2,argument3,argument4).output + feature244(argument1,argument2,argument3,argument4).output + feature245(argument1,argument2,argument3,argument4).output + feature246(argument1,argument2,argument3,argument4).output + feature247(argument1,argument2,argument3,argument4).output + feature248(argument1,argument2,argument3,argument4).output + feature249(argument1,argument2,argument3,argument4).output + feature250(argument1,argument2,argument3,argument4).output + feature251(argument1,argument2,argument3,argument4).output + feature252(argument1,argument2,argument3,argument4).output + feature253(argument1,argument2,argument3,argument4).output + feature254(argument1,argument2,argument3,argument4).output + feature255(argument1,argument2,argument3,argument4).output + feature256(argument1,argument2,argument3,argument4).output + feature257(argument1,argument2,argument3,argument4).output + feature258(argument1,argument2,argument3,argument4).output + feature259(argument1,argument2,argument3,argument4).output + feature260(argument1,argument2,argument3,argument4).output + feature261(argument1,argument2,argument3,argument4).output + feature262(argument1,argument2,argument3,argument4).output + feature263(argument1,argument2,argument3,argument4).output + feature264(argument1,argument2,argument3,argument4).output + feature265(argument1,argument2,argument3,argument4).output + feature266(argument1,argument2,argument3,argument4).output + feature267(argument1,argument2,argument3,argument4).output + feature268(argument1,argument2,argument3,argument4).output + feature269(argument1,argument2,argument3,argument4).output + feature270(argument1,argument2,argument3,argument4).output + feature271(argument1,argument2,argument3,argument4).output + feature272(argument1,argument2,argument3,argument4).output + feature273(argument1,argument2,argument3,argument4).output + feature274(argument1,argument2,argument3,argument4).output + feature275(argument1,argument2,argument3,argument4).output + feature276(argument1,argument2,argument3,argument4).output + feature277(argument1,argument2,argument3,argument4).output + feature278(argument1,argument2,argument3,argument4).output + feature279(argument1,argument2,argument3,argument4).output + feature280(argument1,argument2,argument3,argument4).output + feature281(argument1,argument2,argument3,argument4).output + feature282(argument1,argument2,argument3,argument4).output + feature283(argument1,argument2,argument3,argument4).output + feature284(argument1,argument2,argument3,argument4).output + feature285(argument1,argument2,argument3,argument4).output + feature286(argument1,argument2,argument3,argument4).output + feature287(argument1,argument2,argument3,argument4).output + feature288(argument1,argument2,argument3,argument4).output + feature289(argument1,argument2,argument3,argument4).output + feature290(argument1,argument2,argument3,argument4).output + feature291(argument1,argument2,argument3,argument4).output + feature292(argument1,argument2,argument3,argument4).output + feature293(argument1,argument2,argument3,argument4).output + feature294(argument1,argument2,argument3,argument4).output + feature295(argument1,argument2,argument3,argument4).output + feature296(argument1,argument2,argument3,argument4).output + feature297(argument1,argument2,argument3,argument4).output + feature298(argument1,argument2,argument3,argument4).output + feature299(argument1,argument2,argument3,argument4).output + feature300(argument1,argument2,argument3,argument4).output" +rankprofile[3].fef.property[2].name "vespa.rank.secondphase" +rankprofile[3].fef.property[2].value "rankingExpression(secondphase)" +rankprofile[3].fef.property[3].name "rankingExpression(secondphase).rankingScript" +rankprofile[3].fef.property[3].value "exp(0) + mysum(attribute(foo),\"attribute( bar )\",\"attribute( \\\"baz\\\" )\")" +rankprofile[3].fef.property[4].name "vespa.hitcollector.heapsize" +rankprofile[3].fef.property[4].value "101" +rankprofile[3].fef.property[5].name "vespa.hitcollector.arraysize" +rankprofile[3].fef.property[5].value "201" +rankprofile[3].fef.property[6].name "vespa.hitcollector.rankscoredroplimit" +rankprofile[3].fef.property[6].value "501.5" +rankprofile[4].name "duplicates" +rankprofile[4].fef.property[0].name "fieldMatch(a).proximityLimit" +rankprofile[4].fef.property[0].value "4" +rankprofile[4].fef.property[1].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[1].value "0.2" +rankprofile[4].fef.property[2].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[2].value "0.4" +rankprofile[4].fef.property[3].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[3].value "0.6" +rankprofile[4].fef.property[4].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[4].value "0.8" +rankprofile[4].fef.property[5].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[5].value "1" +rankprofile[4].fef.property[6].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[6].value "0.8" +rankprofile[4].fef.property[7].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[7].value "0.6" +rankprofile[4].fef.property[8].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[8].value "0.4" +rankprofile[4].fef.property[9].name "fieldMatch(a).proximityTable" +rankprofile[4].fef.property[9].value "0.2" +rankprofile[5].name "whitespace1" +rankprofile[5].fef.property[0].name "vespa.rank.firstphase" +rankprofile[5].fef.property[0].value "rankingExpression(firstphase)" +rankprofile[5].fef.property[1].name "rankingExpression(firstphase).rankingScript" +rankprofile[5].fef.property[1].value "1" +rankprofile[6].name "whitespace2" +rankprofile[6].fef.property[0].name "vespa.rank.firstphase" +rankprofile[6].fef.property[0].value "rankingExpression(firstphase)" +rankprofile[6].fef.property[1].name "rankingExpression(firstphase).rankingScript" +rankprofile[6].fef.property[1].value "1" +rankprofile[7].name "macros" +rankprofile[7].fef.property[0].name "rankingExpression(fourtimessum).rankingScript" +rankprofile[7].fef.property[0].value "4 * (var1 + var2)" +rankprofile[7].fef.property[1].name "rankingExpression(myfeature).rankingScript" +rankprofile[7].fef.property[1].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" +rankprofile[7].fef.property[2].name "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86).rankingScript" +rankprofile[7].fef.property[2].value "4 * (match + rankBoost)" +rankprofile[7].fef.property[3].name "vespa.rank.firstphase" +rankprofile[7].fef.property[3].value "rankingExpression(firstphase)" +rankprofile[7].fef.property[4].name "rankingExpression(firstphase).rankingScript" +rankprofile[7].fef.property[4].value "match + fieldMatch(title) + rankingExpression(myfeature)" +rankprofile[7].fef.property[5].name "vespa.rank.secondphase" +rankprofile[7].fef.property[5].value "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)" +rankprofile[7].fef.property[6].name "vespa.summary.feature" +rankprofile[7].fef.property[6].value "fieldMatch(title)" +rankprofile[8].name "macros2" +rankprofile[8].fef.property[0].name "foo" +rankprofile[8].fef.property[0].value "some, list" +rankprofile[8].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" +rankprofile[8].fef.property[1].value "4 * (var1 + var2)" +rankprofile[8].fef.property[2].name "rankingExpression(myfeature).rankingScript" +rankprofile[8].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" +rankprofile[8].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" +rankprofile[8].fef.property[3].value "70 * fieldMatch(title).completeness" +rankprofile[8].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" +rankprofile[8].fef.property[4].value "71 * fieldMatch(title).completeness" +rankprofile[8].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript" +rankprofile[8].fef.property[5].value "4 * (match + match)" +rankprofile[8].fef.property[6].name "vespa.rank.firstphase" +rankprofile[8].fef.property[6].value "classicRank" +rankprofile[8].fef.property[7].name "vespa.rank.secondphase" +rankprofile[8].fef.property[7].value "rankingExpression(secondphase)" +rankprofile[8].fef.property[8].name "rankingExpression(secondphase).rankingScript" +rankprofile[8].fef.property[8].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" +rankprofile[8].fef.property[9].name "vespa.summary.feature" +rankprofile[8].fef.property[9].value "rankingExpression(mysummaryfeature2)" +rankprofile[8].fef.property[10].name "vespa.summary.feature" +rankprofile[8].fef.property[10].value "rankingExpression(mysummaryfeature)" +rankprofile[9].name "macros3" +rankprofile[9].fef.property[0].name "rankingExpression(onlyusedinsummaryfeature).rankingScript" +rankprofile[9].fef.property[0].value "5" +rankprofile[9].fef.property[1].name "vespa.summary.feature" +rankprofile[9].fef.property[1].value "rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))" +rankprofile[10].name "macros3-inherited" +rankprofile[10].fef.property[0].name "rankingExpression(onlyusedinsummaryfeature).rankingScript" +rankprofile[10].fef.property[0].value "5" +rankprofile[10].fef.property[1].name "vespa.summary.feature" +rankprofile[10].fef.property[1].value "rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))" +rankprofile[11].name "macros-inherited" +rankprofile[11].fef.property[0].name "foo" +rankprofile[11].fef.property[0].value "some, list" +rankprofile[11].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" +rankprofile[11].fef.property[1].value "4 * (var1 + var2)" +rankprofile[11].fef.property[2].name "rankingExpression(myfeature).rankingScript" +rankprofile[11].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" +rankprofile[11].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" +rankprofile[11].fef.property[3].value "80 * fieldMatch(title).completeness" +rankprofile[11].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" +rankprofile[11].fef.property[4].value "71 * fieldMatch(title).completeness" +rankprofile[11].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript" +rankprofile[11].fef.property[5].value "4 * (match + match)" +rankprofile[11].fef.property[6].name "vespa.rank.firstphase" +rankprofile[11].fef.property[6].value "rankingExpression(firstphase)" +rankprofile[11].fef.property[7].name "rankingExpression(firstphase).rankingScript" +rankprofile[11].fef.property[7].value "20000 * rankingExpression(myfeature) + rankingExpression(mysummaryfeature)" +rankprofile[11].fef.property[8].name "vespa.rank.secondphase" +rankprofile[11].fef.property[8].value "rankingExpression(secondphase)" +rankprofile[11].fef.property[9].name "rankingExpression(secondphase).rankingScript" +rankprofile[11].fef.property[9].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" +rankprofile[11].fef.property[10].name "vespa.summary.feature" +rankprofile[11].fef.property[10].value "rankingExpression(mysummaryfeature2)" +rankprofile[11].fef.property[11].name "vespa.summary.feature" +rankprofile[11].fef.property[11].value "rankingExpression(mysummaryfeature)" +rankprofile[12].name "macros-inherited2" +rankprofile[12].fef.property[0].name "foo" +rankprofile[12].fef.property[0].value "some, list" +rankprofile[12].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" +rankprofile[12].fef.property[1].value "4 * (var1 + var2)" +rankprofile[12].fef.property[2].name "rankingExpression(myfeature).rankingScript" +rankprofile[12].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" +rankprofile[12].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" +rankprofile[12].fef.property[3].value "80 * fieldMatch(title).completeness" +rankprofile[12].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" +rankprofile[12].fef.property[4].value "71 * fieldMatch(title).completeness" +rankprofile[12].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript" +rankprofile[12].fef.property[5].value "4 * (match + match)" +rankprofile[12].fef.property[6].name "vespa.rank.firstphase" +rankprofile[12].fef.property[6].value "rankingExpression(firstphase)" +rankprofile[12].fef.property[7].name "rankingExpression(firstphase).rankingScript" +rankprofile[12].fef.property[7].value "30000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" +rankprofile[12].fef.property[8].name "vespa.rank.secondphase" +rankprofile[12].fef.property[8].value "rankingExpression(secondphase)" +rankprofile[12].fef.property[9].name "rankingExpression(secondphase).rankingScript" +rankprofile[12].fef.property[9].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" +rankprofile[12].fef.property[10].name "vespa.summary.feature" +rankprofile[12].fef.property[10].value "rankingExpression(mysummaryfeature2)" +rankprofile[12].fef.property[11].name "vespa.summary.feature" +rankprofile[12].fef.property[11].value "rankingExpression(mysummaryfeature)" +rankprofile[13].name "macros-inherited3" +rankprofile[13].fef.property[0].name "foo" +rankprofile[13].fef.property[0].value "some, list" +rankprofile[13].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" +rankprofile[13].fef.property[1].value "4 * (var1 + var2)" +rankprofile[13].fef.property[2].name "rankingExpression(myfeature).rankingScript" +rankprofile[13].fef.property[2].value "700 * fieldMatch(title).completeness" +rankprofile[13].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" +rankprofile[13].fef.property[3].value "80 * fieldMatch(title).completeness" +rankprofile[13].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" +rankprofile[13].fef.property[4].value "71 * fieldMatch(title).completeness" +rankprofile[13].fef.property[5].name "vespa.rank.firstphase" +rankprofile[13].fef.property[5].value "rankingExpression(firstphase)" +rankprofile[13].fef.property[6].name "rankingExpression(firstphase).rankingScript" +rankprofile[13].fef.property[6].value "30000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" +rankprofile[13].fef.property[7].name "vespa.rank.secondphase" +rankprofile[13].fef.property[7].value "rankingExpression(secondphase)" +rankprofile[13].fef.property[8].name "rankingExpression(secondphase).rankingScript" +rankprofile[13].fef.property[8].value "40000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" +rankprofile[13].fef.property[9].name "vespa.summary.feature" +rankprofile[13].fef.property[9].value "rankingExpression(mysummaryfeature2)" +rankprofile[13].fef.property[10].name "vespa.summary.feature" +rankprofile[13].fef.property[10].value "rankingExpression(mysummaryfeature)" +rankprofile[14].name "macros-refering-macros" +rankprofile[14].fef.property[0].name "rankingExpression(m1).rankingScript" +rankprofile[14].fef.property[0].value "700 * fieldMatch(title).completeness" +rankprofile[14].fef.property[1].name "rankingExpression(m2).rankingScript" +rankprofile[14].fef.property[1].value "rankingExpression(m1) * 67" +rankprofile[14].fef.property[2].name "rankingExpression(m4).rankingScript" +rankprofile[14].fef.property[2].value "703 * fieldMatch(fromfile).completeness" +rankprofile[14].fef.property[3].name "vespa.rank.secondphase" +rankprofile[14].fef.property[3].value "rankingExpression(secondphase)" +rankprofile[14].fef.property[4].name "rankingExpression(secondphase).rankingScript" +rankprofile[14].fef.property[4].value "40000 * rankingExpression(m2)" +rankprofile[15].name "macros-refering-macros-inherited" +rankprofile[15].fef.property[0].name "rankingExpression(m1).rankingScript" +rankprofile[15].fef.property[0].value "700 * fieldMatch(title).completeness" +rankprofile[15].fef.property[1].name "rankingExpression(m2).rankingScript" +rankprofile[15].fef.property[1].value "rankingExpression(m1) * 67" +rankprofile[15].fef.property[2].name "rankingExpression(m4).rankingScript" +rankprofile[15].fef.property[2].value "701 * fieldMatch(title).completeness" +rankprofile[15].fef.property[3].name "rankingExpression(m3).rankingScript" +rankprofile[15].fef.property[3].value "if (isNan(attribute(nrtgmp)) == 1, 0.0, rankingExpression(m2))" +rankprofile[15].fef.property[4].name "vespa.rank.secondphase" +rankprofile[15].fef.property[4].value "rankingExpression(secondphase)" +rankprofile[15].fef.property[5].name "rankingExpression(secondphase).rankingScript" +rankprofile[15].fef.property[5].value "3000 * rankingExpression(m2)" +rankprofile[16].name "macros-refering-macros-inherited2" +rankprofile[16].fef.property[0].name "rankingExpression(m1).rankingScript" +rankprofile[16].fef.property[0].value "700 * fieldMatch(title).completeness" +rankprofile[16].fef.property[1].name "rankingExpression(m2).rankingScript" +rankprofile[16].fef.property[1].value "rankingExpression(m1) * 67" +rankprofile[16].fef.property[2].name "rankingExpression(m4).rankingScript" +rankprofile[16].fef.property[2].value "703 * fieldMatch(fromfile).completeness" +rankprofile[16].fef.property[3].name "vespa.rank.secondphase" +rankprofile[16].fef.property[3].value "rankingExpression(secondphase)" +rankprofile[16].fef.property[4].name "rankingExpression(secondphase).rankingScript" +rankprofile[16].fef.property[4].value "3002 * rankingExpression(m2)" +rankprofile[17].name "macros-refering-macros-inherited-two-levels" +rankprofile[17].fef.property[0].name "rankingExpression(m1).rankingScript" +rankprofile[17].fef.property[0].value "700 * fieldMatch(title).completeness" +rankprofile[17].fef.property[1].name "rankingExpression(m2).rankingScript" +rankprofile[17].fef.property[1].value "rankingExpression(m1) * 67" +rankprofile[17].fef.property[2].name "rankingExpression(m4).rankingScript" +rankprofile[17].fef.property[2].value "701 * fieldMatch(title).completeness" +rankprofile[17].fef.property[3].name "rankingExpression(m3).rankingScript" +rankprofile[17].fef.property[3].value "if (isNan(attribute(nrtgmp)) == 1, 0.0, rankingExpression(m2))" +rankprofile[17].fef.property[4].name "rankingExpression(m5).rankingScript" +rankprofile[17].fef.property[4].value "if (isNan(attribute(glmpfw)) == 1, rankingExpression(m1), rankingExpression(m4))" +rankprofile[17].fef.property[5].name "vespa.rank.secondphase" +rankprofile[17].fef.property[5].value "rankingExpression(secondphase)" +rankprofile[17].fef.property[6].name "rankingExpression(secondphase).rankingScript" +rankprofile[17].fef.property[6].value "3000 * rankingExpression(m2)" \ No newline at end of file diff --git a/model-evaluation/src/test/resources/config/rankexpression/rankexpression.sd b/model-evaluation/src/test/resources/config/rankexpression/rankexpression.sd new file mode 100644 index 00000000000..d3e0057cfe1 --- /dev/null +++ b/model-evaluation/src/test/resources/config/rankexpression/rankexpression.sd @@ -0,0 +1,327 @@ +# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +search rankexpression { + + document rankexpression { + + field artist type string { + indexing: summary | index + } + + field title type string { + indexing: summary | index + } + + field surl type string { + indexing: summary + } + + field year type int { + indexing: summary | attribute + } + + field foo1 type int { + indexing: attribute + } + + field foo2 type int { + indexing: attribute + } + + field foo3 type int { + indexing: attribute + } + + field foo4 type int { + indexing: attribute + } + + field bar1 type int { + indexing: attribute + } + + field bar2 type int { + indexing: attribute + } + + field bar3 type int { + indexing: attribute + } + + field bar4 type int { + indexing: attribute + } + + } + + rank-profile default { + first-phase { + expression: classicRank + keep-rank-count: 20 + rank-score-drop-limit: -0.5 + } + second-phase { + expression: if(3>2,4,2) + rerank-count: 10 + } + rank-features: attribute(foo1).out attribute(bar1) + rank-features { attribute(foo2).out attribute(bar2).out } + rank-features { + attribute(foo3).out attribute(bar3).out } + rank-features { + attribute(foo4).out + attribute(bar4).out + } + ignore-default-rank-features + + rank-properties { + foo: "bar, baz" + qux: "quux" + foo: "foobar" + foo.bar: "foo.bar" + foo.bar.baz: 123 + foo ( bar ) . baz.2 : 123.4 + foo(bar).baz.qux: "foo(bar)" + "nud":"ity" + } + + } + + rank-profile static { + first-phase { + expression { attribute } + } + second-phase { + expression { + file:rankexpression + } + } + summary-features: attribute(foo1).out attribute(bar1) + summary-features { attribute(foo2).out attribute(bar2).out } + summary-features { + attribute(foo3).out attribute(bar3).out } + summary-features { + attribute(foo4).out + attribute(bar4).out + } + } + + rank-profile overflow { + first-phase { + expression: file:overflow.expression + keep-rank-count: 201 + rank-score-drop-limit: 501.5 + } + second-phase { + expression { + exp(0) + + mysum(attribute(foo), + "attribute( bar )", + "attribute( \"baz\" )") + } + rerank-count: 101 + } + } + + rank-profile duplicates { + rank-properties { + fieldMatch(a).proximityLimit: 4 + fieldMatch(a).proximityTable: 0.2 + fieldMatch(a).proximityTable: 0.4 + fieldMatch(a).proximityTable: 0.6 + fieldMatch(a).proximityTable: 0.8 + fieldMatch(a).proximityTable: 1 + fieldMatch(a).proximityTable: 0.8 + fieldMatch(a).proximityTable: 0.6 + fieldMatch(a).proximityTable: 0.4 + fieldMatch(a).proximityTable: 0.2 + } + } + + rank-profile whitespace1 { + first-phase { + expression + { + + 1 + }}} + + rank-profile whitespace2 { + first-phase + { + expression { 1 } + } + } + + rank-profile macros { + first-phase { + expression: match + fieldMatch(title) + myfeature + } + second-phase { + expression: fourtimessum(match,rankBoost) + } + macro fourtimessum(var1, var2) { + expression: 4*(var1+var2) + } + macro myfeature() { + expression { + 70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) + + 30 * pow(0 - fieldMatch(description).earliness, 2) + } + } + summary-features { + fieldMatch(title) + } + } + + rank-profile macros2 { + first-phase { + expression: classicRank + } + rank-properties { + foo: "some, list" + } + + second-phase { + expression: fourtimessum(match,match) + mysummaryfeature + myfeature + } + macro fourtimessum(var1, var2) { + expression: 4*(var1+var2) + } + macro myfeature() { + expression { + 70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) + + 30 * pow(0 - fieldMatch(description).earliness, 2) + } + } + macro mysummaryfeature() { + expression { + 70 * fieldMatch(title).completeness + } + } + macro mysummaryfeature2() { + expression { + 71 * fieldMatch(title).completeness + } + } + summary-features { + mysummaryfeature + rankingExpression(mysummaryfeature2) # Required form earlier + } + } + + rank-profile macros3 { + macro onlyusedinsummaryfeature() { + expression: 5 + } + summary-features { + rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature))) + } + + } + + rank-profile macros3-inherited inherits macros3 { + summary-features { + rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature))) + } + } + + rank-profile macros-inherited inherits macros2 { + macro mysummaryfeature() { + expression { + 80 * fieldMatch(title).completeness + } + } + first-phase { + expression { + 20000 * myfeature + mysummaryfeature + } + } + } + + rank-profile macros-inherited2 inherits macros-inherited { + first-phase { + expression { + 30000 * mysummaryfeature + myfeature + } + } + } + + rank-profile macros-inherited3 inherits macros-inherited2 { + macro myfeature() { + expression { + 700 * fieldMatch(title).completeness + } + } + second-phase { + expression { + 40000 * mysummaryfeature + myfeature + } + } + } + + rank-profile macros-refering-macros { + macro m2() { + expression: m1 * 67 + } + + macro m1() { + expression { + 700 * fieldMatch(title).completeness + } + } + + macro m4() { + expression: file:macro.expression + } + + second-phase { + expression { + 40000 * m2 + } + } + + } + + rank-profile macros-refering-macros-inherited inherits macros-refering-macros { + macro m3() { + expression { + if(isNan(attribute(nrtgmp))==1, + 0.0, + (m2) + ) + } + } + macro m4() { + expression { + 701 * fieldMatch(title).completeness + } + } + second-phase { + expression { + 3000 * m2 + } + } + } + + rank-profile macros-refering-macros-inherited2 inherits macros-refering-macros { + second-phase { + expression { + 3002 * m2 + } + } + } + + rank-profile macros-refering-macros-inherited-two-levels inherits macros-refering-macros-inherited { + macro m5() { + expression { + if(isNan(attribute(glmpfw))==1, + m1, + (m4) + ) + } + } + } + +} + + diff --git a/model-inference/pom.xml b/model-inference/pom.xml deleted file mode 100644 index f97045f88ff..00000000000 --- a/model-inference/pom.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - 4.0.0 - - com.yahoo.vespa - parent - 6-SNAPSHOT - ../parent/pom.xml - - model-inference - 6-SNAPSHOT - container-plugin - - - junit - junit - test - - - com.yahoo.vespa - component - ${project.version} - provided - - - com.yahoo.vespa - jdisc_core - ${project.version} - provided - - - com.yahoo.vespa - container-core - ${project.version} - provided - - - com.yahoo.vespa - config - ${project.version} - provided - - - com.yahoo.vespa - configdefinitions - ${project.version} - provided - - - com.yahoo.vespa - vespajlib - ${project.version} - provided - - - com.yahoo.vespa - searchlib - ${project.version} - provided - - - com.google.guava - guava - provided - - - - - - com.yahoo.vespa - bundle-plugin - true - - - org.apache.maven.plugins - maven-jar-plugin - - - - ${project.artifactId} - ${project.version} - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java b/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java deleted file mode 100644 index 4acd6e483b4..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionEvaluator.java +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.yahoo.searchlib.rankingexpression.ExpressionFunction; -import com.yahoo.searchlib.rankingexpression.evaluation.TensorValue; -import com.yahoo.tensor.Tensor; -import com.yahoo.tensor.TensorType; - -/** - * An evaluator which can be used to evaluate a single function once. - * - * @author bratseth - */ -// This wraps all access to the context and the ranking expression to avoid incorrect usage -public class FunctionEvaluator { - - private final ExpressionFunction function; - private final LazyArrayContext context; - private boolean evaluated = false; - - FunctionEvaluator(ExpressionFunction function, LazyArrayContext context) { - this.function = function; - this.context = context; - } - - /** - * Binds the given variable referred in this expression to the given value. - * - * @param name the variable to bind - * @param value the value this becomes bound to - * @return this for chaining - */ - public FunctionEvaluator bind(String name, Tensor value) { - if (evaluated) - throw new IllegalStateException("You cannot bind a value in a used evaluator"); - context.put(name, new TensorValue(value)); - return this; - } - - /** - * Binds the given variable referred in this expression to the given value. - * This is equivalent to bind(name, Tensor.Builder.of(TensorType.empty).cell(value).build()) - * - * @param name the variable to bind - * @param value the value this becomes bound to - * @return this for chaining - */ - public FunctionEvaluator bind(String name, double value) { - return bind(name, Tensor.Builder.of(TensorType.empty).cell(value).build()); - } - - public Tensor evaluate() { - evaluated = true; - return function.getBody().evaluate(context).asTensor(); - } - -} diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionReference.java b/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionReference.java deleted file mode 100644 index 3b50cef6e2e..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/evaluation/FunctionReference.java +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import java.util.Objects; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A reference to a function. - * The function may be - * - free: Callable from users of models, or - * - bound: Representing a specific invocation from another ranking expression. - * In bound functions, any arguments are replaced by the values supplied in the function invocation. - * Function references has a serial form (textual representation) used in ranking expressions received in ranking - * expression configurations. - * - * This is immutable. - * - * @author bratseth - */ -class FunctionReference { - - private static final Pattern referencePattern = - Pattern.compile("rankingExpression\\(([a-zA-Z0-9_]+)(@[a-f0-9]+\\.[a-f0-9]+)?\\)(\\.rankingScript)?"); - - /** The name of the function referenced */ - private final String name; - - /** The id of the specific invocation of the function, or null if it is free */ - private final String instance; - - private FunctionReference(String name, String instance) { - this.name = name; - this.instance = instance; - } - - /** Returns the name of the function referenced */ - String functionName() { return name; } - - boolean isFree() { - return instance == null; - } - - String serialForm() { - return "rankingExpression(" + name + (instance != null ? instance : "") + ")"; - } - - @Override - public String toString() { return "reference to function '" + name + "'" + - ( instance != null ? " instance '" + instance + "'" : ""); } - - @Override - public int hashCode() { return Objects.hash(name, instance); } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - if ( ! (o instanceof FunctionReference)) return false; - FunctionReference other = (FunctionReference)o; - if ( ! Objects.equals(this.name, other.name)) return false; - if ( ! Objects.equals(this.instance, other.instance)) return false; - return true; - } - - /** Returns a function reference from the given serial form, or empty if the string is not a valid reference */ - static Optional fromSerial(String serialForm) { - Matcher expressionMatcher = referencePattern.matcher(serialForm); - if ( ! expressionMatcher.matches()) return Optional.empty(); - - String name = expressionMatcher.group(1); - String instance = expressionMatcher.group(2); - return Optional.of(new FunctionReference(name, instance)); - } - -} diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java b/model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java deleted file mode 100644 index 2dcfd204077..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/evaluation/LazyArrayContext.java +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.google.common.collect.ImmutableMap; -import com.yahoo.searchlib.rankingexpression.ExpressionFunction; -import com.yahoo.searchlib.rankingexpression.RankingExpression; -import com.yahoo.searchlib.rankingexpression.Reference; -import com.yahoo.searchlib.rankingexpression.evaluation.Context; -import com.yahoo.searchlib.rankingexpression.evaluation.ContextIndex; -import com.yahoo.searchlib.rankingexpression.evaluation.DoubleValue; -import com.yahoo.searchlib.rankingexpression.evaluation.Value; -import com.yahoo.searchlib.rankingexpression.rule.CompositeNode; -import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode; -import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode; -import com.yahoo.tensor.TensorType; - -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -/** - * An array context supporting functions invocations implemented as lazy values. - * - * @author bratseth - */ -final class LazyArrayContext extends Context implements ContextIndex { - - private final IndexedBindings indexedBindings; - - private LazyArrayContext(IndexedBindings indexedBindings) { - this.indexedBindings = indexedBindings.copy(this); - } - - /** - * Create a fast lookup, lazy context for an expression. - * - * @param expression the expression to create a context for - */ - LazyArrayContext(RankingExpression expression, Map functions, Model model) { - this.indexedBindings = new IndexedBindings(expression, functions, this, model); - } - - /** - * Puts a value by name. - * The value will be frozen if it isn't already. - * - * @throws IllegalArgumentException if the name is not present in the ranking expression this was created with, and - * ignoredUnknownValues is false - */ - @Override - public void put(String name, Value value) { - put(requireIndexOf(name), value); - } - - /** Same as put(index,DoubleValue.frozen(value)) */ - public final void put(int index, double value) { - put(index, DoubleValue.frozen(value)); - } - - /** - * Puts a value by index. - * The value will be frozen if it isn't already. - */ - public void put(int index, Value value) { - indexedBindings.set(index, value.freeze()); - } - - @Override - public TensorType getType(Reference reference) { - // TODO: Add type information so we do not need to evaluate to get this - return get(requireIndexOf(reference.toString())).type(); - } - - /** Perform a slow lookup by name */ - @Override - public Value get(String name) { - return get(requireIndexOf(name)); - } - - /** Perform a fast lookup by index */ - @Override - public Value get(int index) { - return indexedBindings.get(index); - } - - @Override - public double getDouble(int index) { - double value = get(index).asDouble(); - if (value == Double.NaN) - throw new UnsupportedOperationException("Value at " + index + " has no double representation"); - return value; - } - - @Override - public int getIndex(String name) { - return requireIndexOf(name); - } - - @Override - public int size() { - return indexedBindings.names().size(); - } - - @Override - public Set names() { return indexedBindings.names(); } - - private Integer requireIndexOf(String name) { - Integer index = indexedBindings.indexOf(name); - if (index == null) - throw new IllegalArgumentException("Value '" + name + "' can not be bound in " + this); - return index; - } - - /** - * Creates a copy of this context suitable for evaluating against the same ranking expression - * in a different thread or for re-binding free variables. - */ - LazyArrayContext copy() { - return new LazyArrayContext(indexedBindings); - } - - private static class IndexedBindings { - - /** The mapping from variable name to index */ - private final ImmutableMap nameToIndex; - - /** The current values set, pre-converted to doubles */ - private final Value[] values; - - private IndexedBindings(ImmutableMap nameToIndex, Value[] values) { - this.nameToIndex = nameToIndex; - this.values = values; - } - - /** - * Creates indexed bindings for the given expressions. - * The given expression and functions may be inspected but cannot be stored. - */ - IndexedBindings(RankingExpression expression, - Map functions, - LazyArrayContext owner, - Model model) { - Set bindTargets = new LinkedHashSet<>(); - extractBindTargets(expression.getRoot(), functions, bindTargets); - - values = new Value[bindTargets.size()]; - Arrays.fill(values, DoubleValue.zero); - - int i = 0; - ImmutableMap.Builder nameToIndexBuilder = new ImmutableMap.Builder<>(); - for (String variable : bindTargets) - nameToIndexBuilder.put(variable,i++); - nameToIndex = nameToIndexBuilder.build(); - - for (Map.Entry function : functions.entrySet()) { - Integer index = nameToIndex.get(function.getKey().serialForm()); - if (index != null) // Referenced in this, so bind it - values[index] = new LazyValue(function.getKey(), owner, model); - } - } - - private void extractBindTargets(ExpressionNode node, - Map functions, - Set bindTargets) { - if (isFunctionReference(node)) { - FunctionReference reference = FunctionReference.fromSerial(node.toString()).get(); - bindTargets.add(reference.serialForm()); - - extractBindTargets(functions.get(reference).getBody().getRoot(), functions, bindTargets); - } - else if (isConstant(node)) { - // Ignore - } - else if (node instanceof ReferenceNode) { - bindTargets.add(node.toString()); - } - else if (node instanceof CompositeNode) { - CompositeNode cNode = (CompositeNode)node; - for (ExpressionNode child : cNode.children()) - extractBindTargets(child, functions, bindTargets); - } - } - - private boolean isFunctionReference(ExpressionNode node) { - if ( ! (node instanceof ReferenceNode)) return false; - - ReferenceNode reference = (ReferenceNode)node; - return reference.getName().equals("rankingExpression") && reference.getArguments().size() == 1; - } - - private boolean isConstant(ExpressionNode node) { - if ( ! (node instanceof ReferenceNode)) return false; - - ReferenceNode reference = (ReferenceNode)node; - return reference.getName().equals("value") && reference.getArguments().size() == 1; - } - - Value get(int index) { return values[index]; } - void set(int index, Value value) { values[index] = value; } - Set names() { return nameToIndex.keySet(); } - Integer indexOf(String name) { return nameToIndex.get(name); } - - IndexedBindings copy(Context context) { - Value[] valueCopy = new Value[values.length]; - for (int i = 0; i < values.length; i++) - valueCopy[i] = values[i] instanceof LazyValue ? ((LazyValue)values[i]).copyFor(context) : values[i]; - return new IndexedBindings(nameToIndex, valueCopy); - } - - } - -} diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java b/model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java deleted file mode 100644 index 4a1ee22d288..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/evaluation/LazyValue.java +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.yahoo.searchlib.rankingexpression.ExpressionFunction; -import com.yahoo.searchlib.rankingexpression.evaluation.Context; -import com.yahoo.searchlib.rankingexpression.evaluation.Value; -import com.yahoo.searchlib.rankingexpression.rule.Function; -import com.yahoo.searchlib.rankingexpression.rule.TruthOperator; -import com.yahoo.tensor.Tensor; -import com.yahoo.tensor.TensorType; - -/** - * A Value which is computed from an expression when first requested. - * This is not multithread safe. - * - * @author bratseth - */ -class LazyValue extends Value { - - /** The reference to the function computing the value of this */ - private final FunctionReference function; - - /** The context used to compute the function of this */ - private final Context context; - - /** The model this is part of */ - private final Model model; - - private Value computedValue = null; - - public LazyValue(FunctionReference function, Context context, Model model) { - this.function = function; - this.context = context; - this.model = model; - } - - private Value computedValue() { - if (computedValue == null) - computedValue = model.requireReferencedFunction(function).getBody().evaluate(context); - return computedValue; - } - - @Override - public TensorType type() { - return computedValue().type(); // TODO: Keep type information in this/ExpressionFunction to avoid computing here - } - - @Override - public double asDouble() { - return computedValue().asDouble(); - } - - @Override - public Tensor asTensor() { - return computedValue().asTensor(); - } - - @Override - public boolean hasDouble() { - return type().rank() == 0; - } - - @Override - public boolean asBoolean() { - return computedValue().asBoolean(); - } - - @Override - public Value negate() { - return computedValue().negate(); - } - - @Override - public Value add(Value value) { - return computedValue().add(value); - } - - @Override - public Value subtract(Value value) { - return computedValue().subtract(value); - } - - @Override - public Value multiply(Value value) { - return computedValue().multiply(value); - } - - @Override - public Value divide(Value value) { - return computedValue().divide(value); - } - - @Override - public Value modulo(Value value) { - return computedValue().modulo(value); - } - - @Override - public Value and(Value value) { - return computedValue().and(value); - } - - @Override - public Value or(Value value) { - return computedValue().or(value); - } - - @Override - public Value not() { - return computedValue().not(); - } - - @Override - public Value power(Value value) { - return computedValue().power(value); - } - - @Override - public Value compare(TruthOperator operator, Value value) { - return computedValue().compare(operator, value); - } - - @Override - public Value function(Function function, Value value) { - return computedValue().function(function, value); - } - - @Override - public Value asMutable() { - return computedValue().asMutable(); - } - - @Override - public String toString() { - return "value of " + function; - } - - @Override - public boolean equals(Object other) { - if (other == this) return true; - if (!(other instanceof Value)) return false; - return computedValue().equals(other); - } - - @Override - public int hashCode() { - return computedValue().hashCode(); - } - - LazyValue copyFor(Context context) { - return new LazyValue(this.function, context, model); - } - -} diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/Model.java b/model-inference/src/main/java/ai/vespa/models/evaluation/Model.java deleted file mode 100644 index ca739195867..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/evaluation/Model.java +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.yahoo.searchlib.rankingexpression.ExpressionFunction; -import com.yahoo.searchlib.rankingexpression.evaluation.ContextIndex; -import com.yahoo.searchlib.rankingexpression.evaluation.ExpressionOptimizer; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * A named collection of functions - * - * @author bratseth - */ -public class Model { - - private final String name; - - /** Free functions */ - private final ImmutableList functions; - - /** Instances of each usage of the above function, where variables (if any) are replaced by their bindings */ - private final ImmutableMap referencedFunctions; - - /** Context prototypes, indexed by function name (as all invocations of the same function share the same context prototype) */ - private final ImmutableMap contextPrototypes; - - private final ExpressionOptimizer expressionOptimizer = new ExpressionOptimizer(); - - public Model(String name, Collection functions) { - this(name, functions, Collections.emptyMap()); - } - - Model(String name, Collection functions, Map referencedFunctions) { - // TODO: Optimize functions - this.name = name; - this.functions = ImmutableList.copyOf(functions); - - ImmutableMap.Builder contextBuilder = new ImmutableMap.Builder<>(); - for (ExpressionFunction function : functions) { - try { - contextBuilder.put(function.getName(), new LazyArrayContext(function.getBody(), referencedFunctions, this)); - } - catch (RuntimeException e) { - throw new IllegalArgumentException("Could not prepare an evaluation context for " + function, e); - } - } - this.contextPrototypes = contextBuilder.build(); - - ImmutableMap.Builder functionsBuilder = new ImmutableMap.Builder<>(); - for (Map.Entry function : referencedFunctions.entrySet()) { - ExpressionFunction optimizedFunction = optimize(function.getValue(), - contextPrototypes.get(function.getKey().functionName())); - functionsBuilder.put(function.getKey(), optimizedFunction); - } - this.referencedFunctions = functionsBuilder.build(); - } - - /** Returns an optimized version of the given function */ - private ExpressionFunction optimize(ExpressionFunction function, ContextIndex context) { - // Note: Optimization is in-place but we do not depend on that outside this method - expressionOptimizer.optimize(function.getBody(), context); - return function; - } - - public String name() { return name; } - - /** Returns an immutable list of the free functions of this */ - public List functions() { return functions; } - - /** Returns the given function, or throws a IllegalArgumentException if it does not exist */ - ExpressionFunction requireFunction(String name) { - ExpressionFunction function = function(name); - if (function == null) - throw new IllegalArgumentException("No function named '" + name + "' in " + this + ". Available functions: " + - functions.stream().map(f -> f.getName()).collect(Collectors.joining(", "))); - return function; - } - - /** Returns the given function, or throws a IllegalArgumentException if it does not exist */ - private LazyArrayContext requireContextProprotype(String name) { - LazyArrayContext context = contextPrototypes.get(name); - if (context == null) // Implies function is not present - throw new IllegalArgumentException("No function named '" + name + "' in " + this + ". Available functions: " + - functions.stream().map(f -> f.getName()).collect(Collectors.joining(", "))); - return context; - } - - /** Returns the function withe the given name, or null if none */ // TODO: Parameter overloading? - ExpressionFunction function(String name) { - for (ExpressionFunction function : functions) - if (function.getName().equals(name)) - return function; - return null; - } - - /** Returns an immutable map of the referenced function instances of this */ - Map referencedFunctions() { return referencedFunctions; } - - /** Returns the given referred function, or throws a IllegalArgumentException if it does not exist */ - ExpressionFunction requireReferencedFunction(FunctionReference reference) { - ExpressionFunction function = referencedFunctions.get(reference); - if (function == null) - throw new IllegalArgumentException("No " + reference + " in " + this + ". References: " + - referencedFunctions.keySet().stream() - .map(FunctionReference::serialForm) - .collect(Collectors.joining(", "))); - return function; - } - - /** - * Returns an evaluator which can be used to evaluate the given function in a single thread once. - - * Usage: - * Tensor result = model.evaluatorOf("myFunction").bind("foo", value).bind("bar", value).evaluate() - * - * @throws IllegalArgumentException if the function is not present - */ - public FunctionEvaluator evaluatorOf(String function) { // TODO: Parameter overloading? - return new FunctionEvaluator(requireFunction(function), requireContextProprotype(function).copy()); - } - - @Override - public String toString() { return "model '" + name + "'"; } - -} diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java b/model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java deleted file mode 100644 index b36e06e5505..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.google.common.collect.ImmutableMap; -import com.yahoo.vespa.config.search.RankProfilesConfig; - -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Evaluates machine-learned models added to Vespa applications and available as config form. - * Usage: - * Tensor result = evaluator.bind("foo", value).bind("bar", value").evaluate() - * - * @author bratseth - */ -public class ModelsEvaluator { - - private final ImmutableMap models; - - public ModelsEvaluator(RankProfilesConfig config) { - models = ImmutableMap.copyOf(new RankProfilesConfigImporter().importFrom(config)); - } - - /** Returns the models of this as an immutable map */ - public Map models() { return models; } - - /** - * Returns a function which can be used to evaluate the given function in the given model - * - * @throws IllegalArgumentException if the function or model is not present - */ - public FunctionEvaluator evaluatorOf(String modelName, String functionName) { - return requireModel(modelName).evaluatorOf(functionName); - } - - /** Returns the given model, or throws a IllegalArgumentException if it does not exist */ - Model requireModel(String name) { - Model model = models.get(name); - if (model == null) - throw new IllegalArgumentException("No model named '" + name + ". Available models: " + - models.keySet().stream().collect(Collectors.joining(", "))); - return model; - } - -} diff --git a/model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java b/model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java deleted file mode 100644 index bfd6342218a..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.yahoo.searchlib.rankingexpression.ExpressionFunction; -import com.yahoo.searchlib.rankingexpression.RankingExpression; -import com.yahoo.searchlib.rankingexpression.parser.ParseException; -import com.yahoo.vespa.config.search.RankProfilesConfig; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Converts RankProfilesConfig instances to RankingExpressions for evaluation - * - * @author bratseth - */ -class RankProfilesConfigImporter { - - /** - * Returns a map of the models contained in this config, indexed on name. - * The map is modifiable and owned by the caller. - */ - Map importFrom(RankProfilesConfig config) { - try { - Map models = new HashMap<>(); - for (RankProfilesConfig.Rankprofile profile : config.rankprofile()) { - Model model = importProfile(profile); - models.put(model.name(), model); - } - return models; - } - catch (ParseException e) { - throw new IllegalArgumentException("Could not read rank profiles config - version mismatch?", e); - } - } - - private Model importProfile(RankProfilesConfig.Rankprofile profile) throws ParseException { - List functions = new ArrayList<>(); - Map referencedFunctions = new HashMap<>(); - ExpressionFunction firstPhase = null; - ExpressionFunction secondPhase = null; - for (RankProfilesConfig.Rankprofile.Fef.Property property : profile.fef().property()) { - Optional reference = FunctionReference.fromSerial(property.name()); - if ( reference.isPresent()) { - List arguments = new ArrayList<>(); // TODO: Arguments? - RankingExpression expression = new RankingExpression(reference.get().functionName(), property.value()); - - if (reference.get().isFree()) // make available in model under configured name - functions.add(new ExpressionFunction(reference.get().functionName(), arguments, expression)); // - - // Make all functions, bound or not available under the name they are referenced by in expressions - referencedFunctions.put(reference.get(), new ExpressionFunction(reference.get().serialForm(), arguments, expression)); - } - else if (property.name().equals("vespa.rank.firstphase")) { // Include in addition to macros - firstPhase = new ExpressionFunction("firstphase", new ArrayList<>(), - new RankingExpression("first-phase", property.value())); - } - else if (property.name().equals("vespa.rank.secondphase")) { // Include in addition to macros - secondPhase = new ExpressionFunction("secondphase", new ArrayList<>(), - new RankingExpression("second-phase", property.value())); - } - } - if (functionByName("firstphase", functions) == null && firstPhase != null) // may be already included, depending on body - functions.add(firstPhase); - if (functionByName("secondphase", functions) == null && secondPhase != null) // may be already included, depending on body - functions.add(secondPhase); - - try { - return new Model(profile.name(), functions, referencedFunctions); - } - catch (RuntimeException e) { - throw new IllegalArgumentException("Could not load model '" + profile.name() + "'", e); - } - } - - private ExpressionFunction functionByName(String name, List functions) { - for (ExpressionFunction function : functions) - if (function.getName().equals(name)) - return function; - return null; - } - -} diff --git a/model-inference/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java b/model-inference/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java deleted file mode 100644 index 78c46864d7b..00000000000 --- a/model-inference/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -package ai.vespa.models.handler; - -import ai.vespa.models.evaluation.ModelsEvaluator; -import com.yahoo.container.jdisc.HttpRequest; -import com.yahoo.container.jdisc.HttpResponse; -import com.yahoo.container.jdisc.LoggingRequestHandler; -import com.yahoo.tensor.Tensor; -import com.yahoo.tensor.serialization.JsonFormat; - -import java.io.IOException; -import java.io.OutputStream; - -public class ModelsEvaluationHandler extends LoggingRequestHandler { - - private final ModelsEvaluator modelsEvaluator; - - public ModelsEvaluationHandler(ModelsEvaluator modelsEvaluator, Context context) { - super(context); - this.modelsEvaluator = modelsEvaluator; - } - - @Override - public HttpResponse handle(HttpRequest request) { - Tensor result = modelsEvaluator.evaluatorOf(property("model", "serving_default", request), - request.getProperty("function")) - .evaluate(); - return new RawResponse(JsonFormat.encode(result)); - } - - private String property(String name, String defaultValue, HttpRequest request) { - String value = request.getProperty(name); - if (value == null) return defaultValue; - return value; - } - - private static class RawResponse extends HttpResponse { - - private final byte[] data; - - RawResponse(byte[] data) { - super(200); - this.data = data; - } - - @Override - public String getContentType() { - return "application/json"; - } - - @Override - public void render(OutputStream outputStream) throws IOException { - outputStream.write(data); - } - } - -} - diff --git a/model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java b/model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java deleted file mode 100644 index 60cf0d25ded..00000000000 --- a/model-inference/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.yahoo.config.subscription.ConfigGetter; -import com.yahoo.config.subscription.FileSource; -import com.yahoo.tensor.Tensor; -import com.yahoo.vespa.config.search.RankProfilesConfig; -import org.junit.Test; - -import java.io.File; - -import static org.junit.Assert.assertEquals; - -/** - * @author bratseth - */ -public class ModelsEvaluatorTest { - - private static final double delta = 0.00000000001; - - private ModelsEvaluator createModels() { - String configPath = "src/test/resources/config/rankexpression/rank-profiles.cfg"; - RankProfilesConfig config = new ConfigGetter<>(new FileSource(new File(configPath)), RankProfilesConfig.class).getConfig(""); - return new ModelsEvaluator(config); - } - - @Test - public void testTensorEvaluation() { - ModelsEvaluator models = createModels(); - FunctionEvaluator function = models.evaluatorOf("macros", "fourtimessum"); - function.bind("var1", Tensor.from("{{x:0}:3,{x:1}:5}")); - function.bind("var2", Tensor.from("{{x:0}:7,{x:1}:11}")); - assertEquals(Tensor.from("{{x:0}:40.0,{x:1}:64.0}"), function.evaluate()); - } - - @Test - public void testEvaluationDependingOnMacroTakingArguments() { - ModelsEvaluator models = createModels(); - FunctionEvaluator function = models.evaluatorOf("macros", "secondphase"); - function.bind("match", 3); - function.bind("rankBoost", 5); - assertEquals(32.0, function.evaluate().asDouble(), delta); - } - - // TODO: Test argument-less function - // TODO: Test that binding nonexisting variable doesn't work - // TODO: Test that rebinding doesn't work - // TODO: Test with nested macros - // TODO: Test TF/ONNX model - -} diff --git a/model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java b/model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java deleted file mode 100644 index d45372fc7da..00000000000 --- a/model-inference/src/test/java/ai/vespa/models/evaluation/RankProfilesImporterTest.java +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package ai.vespa.models.evaluation; - -import com.yahoo.config.subscription.ConfigGetter; -import com.yahoo.config.subscription.FileSource; -import com.yahoo.searchlib.rankingexpression.ExpressionFunction; -import com.yahoo.vespa.config.search.RankProfilesConfig; -import org.junit.Test; - -import java.io.File; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -/** - * Tests instantiating models from rank-profiles configs. - * - * @author bratseth - */ -public class RankProfilesImporterTest { - - @Test - public void testImporting() { - String configPath = "src/test/resources/config/rankexpression/rank-profiles.cfg"; - RankProfilesConfig config = new ConfigGetter<>(new FileSource(new File(configPath)), RankProfilesConfig.class).getConfig(""); - Map models = new RankProfilesConfigImporter().importFrom(config); - assertEquals(18, models.size()); - - Model macros = models.get("macros"); - assertNotNull(macros); - assertEquals("macros", macros.name()); - assertEquals(4, macros.functions().size()); - assertFunction("fourtimessum", "4 * (var1 + var2)", macros); - assertFunction("firstphase", "match + fieldMatch(title) + rankingExpression(myfeature)", macros); - assertFunction("secondphase", "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)", macros); - assertFunction("myfeature", - "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + " + - "30 * pow(0 - fieldMatch(description).earliness,2)", - macros); - assertEquals(4, macros.referencedFunctions().size()); - assertBoundFunction("rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)", - "4 * (match + rankBoost)", macros); - } - - private void assertFunction(String name, String expression, Model model) { - ExpressionFunction function = model.function(name); - assertNotNull(function); - assertEquals(name, function.getName()); - assertEquals(expression, function.getBody().getRoot().toString()); - } - - private void assertBoundFunction(String name, String expression, Model model) { - ExpressionFunction function = model.referencedFunctions().get(FunctionReference.fromSerial(name).get()); - assertNotNull("Function '" + name + "' is present", function); - assertEquals(name, function.getName()); - assertEquals(expression, function.getBody().getRoot().toString()); - } - -} diff --git a/model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg b/model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg deleted file mode 100644 index f5652c31d2a..00000000000 --- a/model-inference/src/test/resources/config/rankexpression/rank-profiles.cfg +++ /dev/null @@ -1,296 +0,0 @@ -rankprofile[0].name "default" -rankprofile[0].fef.property[0].name "foo" -rankprofile[0].fef.property[0].value "bar, baz" -rankprofile[0].fef.property[1].name "foo" -rankprofile[0].fef.property[1].value "foobar" -rankprofile[0].fef.property[2].name "qux" -rankprofile[0].fef.property[2].value "quux" -rankprofile[0].fef.property[3].name "foo.bar" -rankprofile[0].fef.property[3].value "foo.bar" -rankprofile[0].fef.property[4].name "foo.bar.baz" -rankprofile[0].fef.property[4].value "123" -rankprofile[0].fef.property[5].name "foo(bar).baz.2" -rankprofile[0].fef.property[5].value "123.4" -rankprofile[0].fef.property[6].name "foo(bar).baz.qux" -rankprofile[0].fef.property[6].value "foo(bar)" -rankprofile[0].fef.property[7].name "nud" -rankprofile[0].fef.property[7].value "ity" -rankprofile[0].fef.property[8].name "vespa.rank.firstphase" -rankprofile[0].fef.property[8].value "classicRank" -rankprofile[0].fef.property[9].name "vespa.rank.secondphase" -rankprofile[0].fef.property[9].value "rankingExpression(secondphase)" -rankprofile[0].fef.property[10].name "rankingExpression(secondphase).rankingScript" -rankprofile[0].fef.property[10].value "4" -rankprofile[0].fef.property[11].name "vespa.dump.feature" -rankprofile[0].fef.property[11].value "attribute(foo1).out" -rankprofile[0].fef.property[12].name "vespa.dump.feature" -rankprofile[0].fef.property[12].value "attribute(bar1)" -rankprofile[0].fef.property[13].name "vespa.dump.feature" -rankprofile[0].fef.property[13].value "attribute(foo2).out" -rankprofile[0].fef.property[14].name "vespa.dump.feature" -rankprofile[0].fef.property[14].value "attribute(bar2).out" -rankprofile[0].fef.property[15].name "vespa.dump.feature" -rankprofile[0].fef.property[15].value "attribute(foo3).out" -rankprofile[0].fef.property[16].name "vespa.dump.feature" -rankprofile[0].fef.property[16].value "attribute(bar3).out" -rankprofile[0].fef.property[17].name "vespa.dump.feature" -rankprofile[0].fef.property[17].value "attribute(foo4).out" -rankprofile[0].fef.property[18].name "vespa.dump.feature" -rankprofile[0].fef.property[18].value "attribute(bar4).out" -rankprofile[0].fef.property[19].name "vespa.hitcollector.heapsize" -rankprofile[0].fef.property[19].value "10" -rankprofile[0].fef.property[20].name "vespa.hitcollector.arraysize" -rankprofile[0].fef.property[20].value "20" -rankprofile[0].fef.property[21].name "vespa.hitcollector.rankscoredroplimit" -rankprofile[0].fef.property[21].value "-0.5" -rankprofile[0].fef.property[22].name "vespa.dump.ignoredefaultfeatures" -rankprofile[0].fef.property[22].value "true" -rankprofile[1].name "unranked" -rankprofile[1].fef.property[0].name "vespa.rank.firstphase" -rankprofile[1].fef.property[0].value "value(0)" -rankprofile[1].fef.property[1].name "vespa.hitcollector.heapsize" -rankprofile[1].fef.property[1].value "0" -rankprofile[1].fef.property[2].name "vespa.hitcollector.arraysize" -rankprofile[1].fef.property[2].value "0" -rankprofile[1].fef.property[3].name "vespa.dump.ignoredefaultfeatures" -rankprofile[1].fef.property[3].value "true" -rankprofile[2].name "static" -rankprofile[2].fef.property[0].name "vespa.rank.firstphase" -rankprofile[2].fef.property[0].value "attribute" -rankprofile[2].fef.property[1].name "vespa.rank.secondphase" -rankprofile[2].fef.property[1].value "rankingExpression(secondphase)" -rankprofile[2].fef.property[2].name "rankingExpression(secondphase).rankingScript" -rankprofile[2].fef.property[2].value "10 + feature(arg1).out.out" -rankprofile[2].fef.property[3].name "vespa.summary.feature" -rankprofile[2].fef.property[3].value "attribute(foo1).out" -rankprofile[2].fef.property[4].name "vespa.summary.feature" -rankprofile[2].fef.property[4].value "attribute(bar1)" -rankprofile[2].fef.property[5].name "vespa.summary.feature" -rankprofile[2].fef.property[5].value "attribute(foo2).out" -rankprofile[2].fef.property[6].name "vespa.summary.feature" -rankprofile[2].fef.property[6].value "attribute(bar2).out" -rankprofile[2].fef.property[7].name "vespa.summary.feature" -rankprofile[2].fef.property[7].value "attribute(foo3).out" -rankprofile[2].fef.property[8].name "vespa.summary.feature" -rankprofile[2].fef.property[8].value "attribute(bar3).out" -rankprofile[2].fef.property[9].name "vespa.summary.feature" -rankprofile[2].fef.property[9].value "attribute(foo4).out" -rankprofile[2].fef.property[10].name "vespa.summary.feature" -rankprofile[2].fef.property[10].value "attribute(bar4).out" -rankprofile[3].name "overflow" -rankprofile[3].fef.property[0].name "vespa.rank.firstphase" -rankprofile[3].fef.property[0].value "rankingExpression(firstphase)" -rankprofile[3].fef.property[1].name "rankingExpression(firstphase).rankingScript" -rankprofile[3].fef.property[1].value "feature1(argument1,argument2,argument3,argument4).output + feature2(argument1,argument2,argument3,argument4).output + feature3(argument1,argument2,argument3,argument4).output + feature4(argument1,argument2,argument3,argument4).output + feature5(argument1,argument2,argument3,argument4).output + feature6(argument1,argument2,argument3,argument4).output + feature7(argument1,argument2,argument3,argument4).output + feature8(argument1,argument2,argument3,argument4).output + feature9(argument1,argument2,argument3,argument4).output + feature10(argument1,argument2,argument3,argument4).output + feature11(argument1,argument2,argument3,argument4).output + feature12(argument1,argument2,argument3,argument4).output + feature13(argument1,argument2,argument3,argument4).output + feature14(argument1,argument2,argument3,argument4).output + feature15(argument1,argument2,argument3,argument4).output + feature16(argument1,argument2,argument3,argument4).output + feature17(argument1,argument2,argument3,argument4).output + feature18(argument1,argument2,argument3,argument4).output + feature19(argument1,argument2,argument3,argument4).output + feature20(argument1,argument2,argument3,argument4).output + feature21(argument1,argument2,argument3,argument4).output + feature22(argument1,argument2,argument3,argument4).output + feature23(argument1,argument2,argument3,argument4).output + feature24(argument1,argument2,argument3,argument4).output + feature25(argument1,argument2,argument3,argument4).output + feature26(argument1,argument2,argument3,argument4).output + feature27(argument1,argument2,argument3,argument4).output + feature28(argument1,argument2,argument3,argument4).output + feature29(argument1,argument2,argument3,argument4).output + feature30(argument1,argument2,argument3,argument4).output + feature31(argument1,argument2,argument3,argument4).output + feature32(argument1,argument2,argument3,argument4).output + feature33(argument1,argument2,argument3,argument4).output + feature34(argument1,argument2,argument3,argument4).output + feature35(argument1,argument2,argument3,argument4).output + feature36(argument1,argument2,argument3,argument4).output + feature37(argument1,argument2,argument3,argument4).output + feature38(argument1,argument2,argument3,argument4).output + feature39(argument1,argument2,argument3,argument4).output + feature40(argument1,argument2,argument3,argument4).output + feature41(argument1,argument2,argument3,argument4).output + feature42(argument1,argument2,argument3,argument4).output + feature43(argument1,argument2,argument3,argument4).output + feature44(argument1,argument2,argument3,argument4).output + feature45(argument1,argument2,argument3,argument4).output + feature46(argument1,argument2,argument3,argument4).output + feature47(argument1,argument2,argument3,argument4).output + feature48(argument1,argument2,argument3,argument4).output + feature49(argument1,argument2,argument3,argument4).output + feature50(argument1,argument2,argument3,argument4).output + feature51(argument1,argument2,argument3,argument4).output + feature52(argument1,argument2,argument3,argument4).output + feature53(argument1,argument2,argument3,argument4).output + feature54(argument1,argument2,argument3,argument4).output + feature55(argument1,argument2,argument3,argument4).output + feature56(argument1,argument2,argument3,argument4).output + feature57(argument1,argument2,argument3,argument4).output + feature58(argument1,argument2,argument3,argument4).output + feature59(argument1,argument2,argument3,argument4).output + feature60(argument1,argument2,argument3,argument4).output + feature61(argument1,argument2,argument3,argument4).output + feature62(argument1,argument2,argument3,argument4).output + feature63(argument1,argument2,argument3,argument4).output + feature64(argument1,argument2,argument3,argument4).output + feature65(argument1,argument2,argument3,argument4).output + feature66(argument1,argument2,argument3,argument4).output + feature67(argument1,argument2,argument3,argument4).output + feature68(argument1,argument2,argument3,argument4).output + feature69(argument1,argument2,argument3,argument4).output + feature70(argument1,argument2,argument3,argument4).output + feature71(argument1,argument2,argument3,argument4).output + feature72(argument1,argument2,argument3,argument4).output + feature73(argument1,argument2,argument3,argument4).output + feature74(argument1,argument2,argument3,argument4).output + feature75(argument1,argument2,argument3,argument4).output + feature76(argument1,argument2,argument3,argument4).output + feature77(argument1,argument2,argument3,argument4).output + feature78(argument1,argument2,argument3,argument4).output + feature79(argument1,argument2,argument3,argument4).output + feature80(argument1,argument2,argument3,argument4).output + feature81(argument1,argument2,argument3,argument4).output + feature82(argument1,argument2,argument3,argument4).output + feature83(argument1,argument2,argument3,argument4).output + feature84(argument1,argument2,argument3,argument4).output + feature85(argument1,argument2,argument3,argument4).output + feature86(argument1,argument2,argument3,argument4).output + feature87(argument1,argument2,argument3,argument4).output + feature88(argument1,argument2,argument3,argument4).output + feature89(argument1,argument2,argument3,argument4).output + feature90(argument1,argument2,argument3,argument4).output + feature91(argument1,argument2,argument3,argument4).output + feature92(argument1,argument2,argument3,argument4).output + feature93(argument1,argument2,argument3,argument4).output + feature94(argument1,argument2,argument3,argument4).output + feature95(argument1,argument2,argument3,argument4).output + feature96(argument1,argument2,argument3,argument4).output + feature97(argument1,argument2,argument3,argument4).output + feature98(argument1,argument2,argument3,argument4).output + feature99(argument1,argument2,argument3,argument4).output + feature100(argument1,argument2,argument3,argument4).output + feature101(argument1,argument2,argument3,argument4).output + feature102(argument1,argument2,argument3,argument4).output + feature103(argument1,argument2,argument3,argument4).output + feature104(argument1,argument2,argument3,argument4).output + feature105(argument1,argument2,argument3,argument4).output + feature106(argument1,argument2,argument3,argument4).output + feature107(argument1,argument2,argument3,argument4).output + feature108(argument1,argument2,argument3,argument4).output + feature109(argument1,argument2,argument3,argument4).output + feature110(argument1,argument2,argument3,argument4).output + feature111(argument1,argument2,argument3,argument4).output + feature112(argument1,argument2,argument3,argument4).output + feature113(argument1,argument2,argument3,argument4).output + feature114(argument1,argument2,argument3,argument4).output + feature115(argument1,argument2,argument3,argument4).output + feature116(argument1,argument2,argument3,argument4).output + feature117(argument1,argument2,argument3,argument4).output + feature118(argument1,argument2,argument3,argument4).output + feature119(argument1,argument2,argument3,argument4).output + feature120(argument1,argument2,argument3,argument4).output + feature121(argument1,argument2,argument3,argument4).output + feature122(argument1,argument2,argument3,argument4).output + feature123(argument1,argument2,argument3,argument4).output + feature124(argument1,argument2,argument3,argument4).output + feature125(argument1,argument2,argument3,argument4).output + feature126(argument1,argument2,argument3,argument4).output + feature127(argument1,argument2,argument3,argument4).output + feature128(argument1,argument2,argument3,argument4).output + feature129(argument1,argument2,argument3,argument4).output + feature130(argument1,argument2,argument3,argument4).output + feature131(argument1,argument2,argument3,argument4).output + feature132(argument1,argument2,argument3,argument4).output + feature133(argument1,argument2,argument3,argument4).output + feature134(argument1,argument2,argument3,argument4).output + feature135(argument1,argument2,argument3,argument4).output + feature136(argument1,argument2,argument3,argument4).output + feature137(argument1,argument2,argument3,argument4).output + feature138(argument1,argument2,argument3,argument4).output + feature139(argument1,argument2,argument3,argument4).output + feature140(argument1,argument2,argument3,argument4).output + feature141(argument1,argument2,argument3,argument4).output + feature142(argument1,argument2,argument3,argument4).output + feature143(argument1,argument2,argument3,argument4).output + feature144(argument1,argument2,argument3,argument4).output + feature145(argument1,argument2,argument3,argument4).output + feature146(argument1,argument2,argument3,argument4).output + feature147(argument1,argument2,argument3,argument4).output + feature148(argument1,argument2,argument3,argument4).output + feature149(argument1,argument2,argument3,argument4).output + feature150(argument1,argument2,argument3,argument4).output + feature151(argument1,argument2,argument3,argument4).output + feature152(argument1,argument2,argument3,argument4).output + feature153(argument1,argument2,argument3,argument4).output + feature154(argument1,argument2,argument3,argument4).output + feature155(argument1,argument2,argument3,argument4).output + feature156(argument1,argument2,argument3,argument4).output + feature157(argument1,argument2,argument3,argument4).output + feature158(argument1,argument2,argument3,argument4).output + feature159(argument1,argument2,argument3,argument4).output + feature160(argument1,argument2,argument3,argument4).output + feature161(argument1,argument2,argument3,argument4).output + feature162(argument1,argument2,argument3,argument4).output + feature163(argument1,argument2,argument3,argument4).output + feature164(argument1,argument2,argument3,argument4).output + feature165(argument1,argument2,argument3,argument4).output + feature166(argument1,argument2,argument3,argument4).output + feature167(argument1,argument2,argument3,argument4).output + feature168(argument1,argument2,argument3,argument4).output + feature169(argument1,argument2,argument3,argument4).output + feature170(argument1,argument2,argument3,argument4).output + feature171(argument1,argument2,argument3,argument4).output + feature172(argument1,argument2,argument3,argument4).output + feature173(argument1,argument2,argument3,argument4).output + feature174(argument1,argument2,argument3,argument4).output + feature175(argument1,argument2,argument3,argument4).output + feature176(argument1,argument2,argument3,argument4).output + feature177(argument1,argument2,argument3,argument4).output + feature178(argument1,argument2,argument3,argument4).output + feature179(argument1,argument2,argument3,argument4).output + feature180(argument1,argument2,argument3,argument4).output + feature181(argument1,argument2,argument3,argument4).output + feature182(argument1,argument2,argument3,argument4).output + feature183(argument1,argument2,argument3,argument4).output + feature184(argument1,argument2,argument3,argument4).output + feature185(argument1,argument2,argument3,argument4).output + feature186(argument1,argument2,argument3,argument4).output + feature187(argument1,argument2,argument3,argument4).output + feature188(argument1,argument2,argument3,argument4).output + feature189(argument1,argument2,argument3,argument4).output + feature190(argument1,argument2,argument3,argument4).output + feature191(argument1,argument2,argument3,argument4).output + feature192(argument1,argument2,argument3,argument4).output + feature193(argument1,argument2,argument3,argument4).output + feature194(argument1,argument2,argument3,argument4).output + feature195(argument1,argument2,argument3,argument4).output + feature196(argument1,argument2,argument3,argument4).output + feature197(argument1,argument2,argument3,argument4).output + feature198(argument1,argument2,argument3,argument4).output + feature199(argument1,argument2,argument3,argument4).output + feature200(argument1,argument2,argument3,argument4).output + feature201(argument1,argument2,argument3,argument4).output + feature202(argument1,argument2,argument3,argument4).output + feature203(argument1,argument2,argument3,argument4).output + feature204(argument1,argument2,argument3,argument4).output + feature205(argument1,argument2,argument3,argument4).output + feature206(argument1,argument2,argument3,argument4).output + feature207(argument1,argument2,argument3,argument4).output + feature208(argument1,argument2,argument3,argument4).output + feature209(argument1,argument2,argument3,argument4).output + feature210(argument1,argument2,argument3,argument4).output + feature211(argument1,argument2,argument3,argument4).output + feature212(argument1,argument2,argument3,argument4).output + feature213(argument1,argument2,argument3,argument4).output + feature214(argument1,argument2,argument3,argument4).output + feature215(argument1,argument2,argument3,argument4).output + feature216(argument1,argument2,argument3,argument4).output + feature217(argument1,argument2,argument3,argument4).output + feature218(argument1,argument2,argument3,argument4).output + feature219(argument1,argument2,argument3,argument4).output + feature220(argument1,argument2,argument3,argument4).output + feature221(argument1,argument2,argument3,argument4).output + feature222(argument1,argument2,argument3,argument4).output + feature223(argument1,argument2,argument3,argument4).output + feature224(argument1,argument2,argument3,argument4).output + feature225(argument1,argument2,argument3,argument4).output + feature226(argument1,argument2,argument3,argument4).output + feature227(argument1,argument2,argument3,argument4).output + feature228(argument1,argument2,argument3,argument4).output + feature229(argument1,argument2,argument3,argument4).output + feature230(argument1,argument2,argument3,argument4).output + feature231(argument1,argument2,argument3,argument4).output + feature232(argument1,argument2,argument3,argument4).output + feature233(argument1,argument2,argument3,argument4).output + feature234(argument1,argument2,argument3,argument4).output + feature235(argument1,argument2,argument3,argument4).output + feature236(argument1,argument2,argument3,argument4).output + feature237(argument1,argument2,argument3,argument4).output + feature238(argument1,argument2,argument3,argument4).output + feature239(argument1,argument2,argument3,argument4).output + feature240(argument1,argument2,argument3,argument4).output + feature241(argument1,argument2,argument3,argument4).output + feature242(argument1,argument2,argument3,argument4).output + feature243(argument1,argument2,argument3,argument4).output + feature244(argument1,argument2,argument3,argument4).output + feature245(argument1,argument2,argument3,argument4).output + feature246(argument1,argument2,argument3,argument4).output + feature247(argument1,argument2,argument3,argument4).output + feature248(argument1,argument2,argument3,argument4).output + feature249(argument1,argument2,argument3,argument4).output + feature250(argument1,argument2,argument3,argument4).output + feature251(argument1,argument2,argument3,argument4).output + feature252(argument1,argument2,argument3,argument4).output + feature253(argument1,argument2,argument3,argument4).output + feature254(argument1,argument2,argument3,argument4).output + feature255(argument1,argument2,argument3,argument4).output + feature256(argument1,argument2,argument3,argument4).output + feature257(argument1,argument2,argument3,argument4).output + feature258(argument1,argument2,argument3,argument4).output + feature259(argument1,argument2,argument3,argument4).output + feature260(argument1,argument2,argument3,argument4).output + feature261(argument1,argument2,argument3,argument4).output + feature262(argument1,argument2,argument3,argument4).output + feature263(argument1,argument2,argument3,argument4).output + feature264(argument1,argument2,argument3,argument4).output + feature265(argument1,argument2,argument3,argument4).output + feature266(argument1,argument2,argument3,argument4).output + feature267(argument1,argument2,argument3,argument4).output + feature268(argument1,argument2,argument3,argument4).output + feature269(argument1,argument2,argument3,argument4).output + feature270(argument1,argument2,argument3,argument4).output + feature271(argument1,argument2,argument3,argument4).output + feature272(argument1,argument2,argument3,argument4).output + feature273(argument1,argument2,argument3,argument4).output + feature274(argument1,argument2,argument3,argument4).output + feature275(argument1,argument2,argument3,argument4).output + feature276(argument1,argument2,argument3,argument4).output + feature277(argument1,argument2,argument3,argument4).output + feature278(argument1,argument2,argument3,argument4).output + feature279(argument1,argument2,argument3,argument4).output + feature280(argument1,argument2,argument3,argument4).output + feature281(argument1,argument2,argument3,argument4).output + feature282(argument1,argument2,argument3,argument4).output + feature283(argument1,argument2,argument3,argument4).output + feature284(argument1,argument2,argument3,argument4).output + feature285(argument1,argument2,argument3,argument4).output + feature286(argument1,argument2,argument3,argument4).output + feature287(argument1,argument2,argument3,argument4).output + feature288(argument1,argument2,argument3,argument4).output + feature289(argument1,argument2,argument3,argument4).output + feature290(argument1,argument2,argument3,argument4).output + feature291(argument1,argument2,argument3,argument4).output + feature292(argument1,argument2,argument3,argument4).output + feature293(argument1,argument2,argument3,argument4).output + feature294(argument1,argument2,argument3,argument4).output + feature295(argument1,argument2,argument3,argument4).output + feature296(argument1,argument2,argument3,argument4).output + feature297(argument1,argument2,argument3,argument4).output + feature298(argument1,argument2,argument3,argument4).output + feature299(argument1,argument2,argument3,argument4).output + feature300(argument1,argument2,argument3,argument4).output" -rankprofile[3].fef.property[2].name "vespa.rank.secondphase" -rankprofile[3].fef.property[2].value "rankingExpression(secondphase)" -rankprofile[3].fef.property[3].name "rankingExpression(secondphase).rankingScript" -rankprofile[3].fef.property[3].value "exp(0) + mysum(attribute(foo),\"attribute( bar )\",\"attribute( \\\"baz\\\" )\")" -rankprofile[3].fef.property[4].name "vespa.hitcollector.heapsize" -rankprofile[3].fef.property[4].value "101" -rankprofile[3].fef.property[5].name "vespa.hitcollector.arraysize" -rankprofile[3].fef.property[5].value "201" -rankprofile[3].fef.property[6].name "vespa.hitcollector.rankscoredroplimit" -rankprofile[3].fef.property[6].value "501.5" -rankprofile[4].name "duplicates" -rankprofile[4].fef.property[0].name "fieldMatch(a).proximityLimit" -rankprofile[4].fef.property[0].value "4" -rankprofile[4].fef.property[1].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[1].value "0.2" -rankprofile[4].fef.property[2].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[2].value "0.4" -rankprofile[4].fef.property[3].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[3].value "0.6" -rankprofile[4].fef.property[4].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[4].value "0.8" -rankprofile[4].fef.property[5].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[5].value "1" -rankprofile[4].fef.property[6].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[6].value "0.8" -rankprofile[4].fef.property[7].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[7].value "0.6" -rankprofile[4].fef.property[8].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[8].value "0.4" -rankprofile[4].fef.property[9].name "fieldMatch(a).proximityTable" -rankprofile[4].fef.property[9].value "0.2" -rankprofile[5].name "whitespace1" -rankprofile[5].fef.property[0].name "vespa.rank.firstphase" -rankprofile[5].fef.property[0].value "rankingExpression(firstphase)" -rankprofile[5].fef.property[1].name "rankingExpression(firstphase).rankingScript" -rankprofile[5].fef.property[1].value "1" -rankprofile[6].name "whitespace2" -rankprofile[6].fef.property[0].name "vespa.rank.firstphase" -rankprofile[6].fef.property[0].value "rankingExpression(firstphase)" -rankprofile[6].fef.property[1].name "rankingExpression(firstphase).rankingScript" -rankprofile[6].fef.property[1].value "1" -rankprofile[7].name "macros" -rankprofile[7].fef.property[0].name "rankingExpression(fourtimessum).rankingScript" -rankprofile[7].fef.property[0].value "4 * (var1 + var2)" -rankprofile[7].fef.property[1].name "rankingExpression(myfeature).rankingScript" -rankprofile[7].fef.property[1].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" -rankprofile[7].fef.property[2].name "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86).rankingScript" -rankprofile[7].fef.property[2].value "4 * (match + rankBoost)" -rankprofile[7].fef.property[3].name "vespa.rank.firstphase" -rankprofile[7].fef.property[3].value "rankingExpression(firstphase)" -rankprofile[7].fef.property[4].name "rankingExpression(firstphase).rankingScript" -rankprofile[7].fef.property[4].value "match + fieldMatch(title) + rankingExpression(myfeature)" -rankprofile[7].fef.property[5].name "vespa.rank.secondphase" -rankprofile[7].fef.property[5].value "rankingExpression(fourtimessum@5cf279212355b980.67f1e87166cfef86)" -rankprofile[7].fef.property[6].name "vespa.summary.feature" -rankprofile[7].fef.property[6].value "fieldMatch(title)" -rankprofile[8].name "macros2" -rankprofile[8].fef.property[0].name "foo" -rankprofile[8].fef.property[0].value "some, list" -rankprofile[8].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" -rankprofile[8].fef.property[1].value "4 * (var1 + var2)" -rankprofile[8].fef.property[2].name "rankingExpression(myfeature).rankingScript" -rankprofile[8].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" -rankprofile[8].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" -rankprofile[8].fef.property[3].value "70 * fieldMatch(title).completeness" -rankprofile[8].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" -rankprofile[8].fef.property[4].value "71 * fieldMatch(title).completeness" -rankprofile[8].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript" -rankprofile[8].fef.property[5].value "4 * (match + match)" -rankprofile[8].fef.property[6].name "vespa.rank.firstphase" -rankprofile[8].fef.property[6].value "classicRank" -rankprofile[8].fef.property[7].name "vespa.rank.secondphase" -rankprofile[8].fef.property[7].value "rankingExpression(secondphase)" -rankprofile[8].fef.property[8].name "rankingExpression(secondphase).rankingScript" -rankprofile[8].fef.property[8].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" -rankprofile[8].fef.property[9].name "vespa.summary.feature" -rankprofile[8].fef.property[9].value "rankingExpression(mysummaryfeature2)" -rankprofile[8].fef.property[10].name "vespa.summary.feature" -rankprofile[8].fef.property[10].value "rankingExpression(mysummaryfeature)" -rankprofile[9].name "macros3" -rankprofile[9].fef.property[0].name "rankingExpression(onlyusedinsummaryfeature).rankingScript" -rankprofile[9].fef.property[0].value "5" -rankprofile[9].fef.property[1].name "vespa.summary.feature" -rankprofile[9].fef.property[1].value "rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))" -rankprofile[10].name "macros3-inherited" -rankprofile[10].fef.property[0].name "rankingExpression(onlyusedinsummaryfeature).rankingScript" -rankprofile[10].fef.property[0].value "5" -rankprofile[10].fef.property[1].name "vespa.summary.feature" -rankprofile[10].fef.property[1].value "rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature)))" -rankprofile[11].name "macros-inherited" -rankprofile[11].fef.property[0].name "foo" -rankprofile[11].fef.property[0].value "some, list" -rankprofile[11].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" -rankprofile[11].fef.property[1].value "4 * (var1 + var2)" -rankprofile[11].fef.property[2].name "rankingExpression(myfeature).rankingScript" -rankprofile[11].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" -rankprofile[11].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" -rankprofile[11].fef.property[3].value "80 * fieldMatch(title).completeness" -rankprofile[11].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" -rankprofile[11].fef.property[4].value "71 * fieldMatch(title).completeness" -rankprofile[11].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript" -rankprofile[11].fef.property[5].value "4 * (match + match)" -rankprofile[11].fef.property[6].name "vespa.rank.firstphase" -rankprofile[11].fef.property[6].value "rankingExpression(firstphase)" -rankprofile[11].fef.property[7].name "rankingExpression(firstphase).rankingScript" -rankprofile[11].fef.property[7].value "20000 * rankingExpression(myfeature) + rankingExpression(mysummaryfeature)" -rankprofile[11].fef.property[8].name "vespa.rank.secondphase" -rankprofile[11].fef.property[8].value "rankingExpression(secondphase)" -rankprofile[11].fef.property[9].name "rankingExpression(secondphase).rankingScript" -rankprofile[11].fef.property[9].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" -rankprofile[11].fef.property[10].name "vespa.summary.feature" -rankprofile[11].fef.property[10].value "rankingExpression(mysummaryfeature2)" -rankprofile[11].fef.property[11].name "vespa.summary.feature" -rankprofile[11].fef.property[11].value "rankingExpression(mysummaryfeature)" -rankprofile[12].name "macros-inherited2" -rankprofile[12].fef.property[0].name "foo" -rankprofile[12].fef.property[0].value "some, list" -rankprofile[12].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" -rankprofile[12].fef.property[1].value "4 * (var1 + var2)" -rankprofile[12].fef.property[2].name "rankingExpression(myfeature).rankingScript" -rankprofile[12].fef.property[2].value "70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness,2) + 30 * pow(0 - fieldMatch(description).earliness,2)" -rankprofile[12].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" -rankprofile[12].fef.property[3].value "80 * fieldMatch(title).completeness" -rankprofile[12].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" -rankprofile[12].fef.property[4].value "71 * fieldMatch(title).completeness" -rankprofile[12].fef.property[5].name "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86).rankingScript" -rankprofile[12].fef.property[5].value "4 * (match + match)" -rankprofile[12].fef.property[6].name "vespa.rank.firstphase" -rankprofile[12].fef.property[6].value "rankingExpression(firstphase)" -rankprofile[12].fef.property[7].name "rankingExpression(firstphase).rankingScript" -rankprofile[12].fef.property[7].value "30000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" -rankprofile[12].fef.property[8].name "vespa.rank.secondphase" -rankprofile[12].fef.property[8].value "rankingExpression(secondphase)" -rankprofile[12].fef.property[9].name "rankingExpression(secondphase).rankingScript" -rankprofile[12].fef.property[9].value "rankingExpression(fourtimessum@2b1138e8965e7ff5.67f1e87166cfef86) + rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" -rankprofile[12].fef.property[10].name "vespa.summary.feature" -rankprofile[12].fef.property[10].value "rankingExpression(mysummaryfeature2)" -rankprofile[12].fef.property[11].name "vespa.summary.feature" -rankprofile[12].fef.property[11].value "rankingExpression(mysummaryfeature)" -rankprofile[13].name "macros-inherited3" -rankprofile[13].fef.property[0].name "foo" -rankprofile[13].fef.property[0].value "some, list" -rankprofile[13].fef.property[1].name "rankingExpression(fourtimessum).rankingScript" -rankprofile[13].fef.property[1].value "4 * (var1 + var2)" -rankprofile[13].fef.property[2].name "rankingExpression(myfeature).rankingScript" -rankprofile[13].fef.property[2].value "700 * fieldMatch(title).completeness" -rankprofile[13].fef.property[3].name "rankingExpression(mysummaryfeature).rankingScript" -rankprofile[13].fef.property[3].value "80 * fieldMatch(title).completeness" -rankprofile[13].fef.property[4].name "rankingExpression(mysummaryfeature2).rankingScript" -rankprofile[13].fef.property[4].value "71 * fieldMatch(title).completeness" -rankprofile[13].fef.property[5].name "vespa.rank.firstphase" -rankprofile[13].fef.property[5].value "rankingExpression(firstphase)" -rankprofile[13].fef.property[6].name "rankingExpression(firstphase).rankingScript" -rankprofile[13].fef.property[6].value "30000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" -rankprofile[13].fef.property[7].name "vespa.rank.secondphase" -rankprofile[13].fef.property[7].value "rankingExpression(secondphase)" -rankprofile[13].fef.property[8].name "rankingExpression(secondphase).rankingScript" -rankprofile[13].fef.property[8].value "40000 * rankingExpression(mysummaryfeature) + rankingExpression(myfeature)" -rankprofile[13].fef.property[9].name "vespa.summary.feature" -rankprofile[13].fef.property[9].value "rankingExpression(mysummaryfeature2)" -rankprofile[13].fef.property[10].name "vespa.summary.feature" -rankprofile[13].fef.property[10].value "rankingExpression(mysummaryfeature)" -rankprofile[14].name "macros-refering-macros" -rankprofile[14].fef.property[0].name "rankingExpression(m1).rankingScript" -rankprofile[14].fef.property[0].value "700 * fieldMatch(title).completeness" -rankprofile[14].fef.property[1].name "rankingExpression(m2).rankingScript" -rankprofile[14].fef.property[1].value "rankingExpression(m1) * 67" -rankprofile[14].fef.property[2].name "rankingExpression(m4).rankingScript" -rankprofile[14].fef.property[2].value "703 * fieldMatch(fromfile).completeness" -rankprofile[14].fef.property[3].name "vespa.rank.secondphase" -rankprofile[14].fef.property[3].value "rankingExpression(secondphase)" -rankprofile[14].fef.property[4].name "rankingExpression(secondphase).rankingScript" -rankprofile[14].fef.property[4].value "40000 * rankingExpression(m2)" -rankprofile[15].name "macros-refering-macros-inherited" -rankprofile[15].fef.property[0].name "rankingExpression(m1).rankingScript" -rankprofile[15].fef.property[0].value "700 * fieldMatch(title).completeness" -rankprofile[15].fef.property[1].name "rankingExpression(m2).rankingScript" -rankprofile[15].fef.property[1].value "rankingExpression(m1) * 67" -rankprofile[15].fef.property[2].name "rankingExpression(m4).rankingScript" -rankprofile[15].fef.property[2].value "701 * fieldMatch(title).completeness" -rankprofile[15].fef.property[3].name "rankingExpression(m3).rankingScript" -rankprofile[15].fef.property[3].value "if (isNan(attribute(nrtgmp)) == 1, 0.0, rankingExpression(m2))" -rankprofile[15].fef.property[4].name "vespa.rank.secondphase" -rankprofile[15].fef.property[4].value "rankingExpression(secondphase)" -rankprofile[15].fef.property[5].name "rankingExpression(secondphase).rankingScript" -rankprofile[15].fef.property[5].value "3000 * rankingExpression(m2)" -rankprofile[16].name "macros-refering-macros-inherited2" -rankprofile[16].fef.property[0].name "rankingExpression(m1).rankingScript" -rankprofile[16].fef.property[0].value "700 * fieldMatch(title).completeness" -rankprofile[16].fef.property[1].name "rankingExpression(m2).rankingScript" -rankprofile[16].fef.property[1].value "rankingExpression(m1) * 67" -rankprofile[16].fef.property[2].name "rankingExpression(m4).rankingScript" -rankprofile[16].fef.property[2].value "703 * fieldMatch(fromfile).completeness" -rankprofile[16].fef.property[3].name "vespa.rank.secondphase" -rankprofile[16].fef.property[3].value "rankingExpression(secondphase)" -rankprofile[16].fef.property[4].name "rankingExpression(secondphase).rankingScript" -rankprofile[16].fef.property[4].value "3002 * rankingExpression(m2)" -rankprofile[17].name "macros-refering-macros-inherited-two-levels" -rankprofile[17].fef.property[0].name "rankingExpression(m1).rankingScript" -rankprofile[17].fef.property[0].value "700 * fieldMatch(title).completeness" -rankprofile[17].fef.property[1].name "rankingExpression(m2).rankingScript" -rankprofile[17].fef.property[1].value "rankingExpression(m1) * 67" -rankprofile[17].fef.property[2].name "rankingExpression(m4).rankingScript" -rankprofile[17].fef.property[2].value "701 * fieldMatch(title).completeness" -rankprofile[17].fef.property[3].name "rankingExpression(m3).rankingScript" -rankprofile[17].fef.property[3].value "if (isNan(attribute(nrtgmp)) == 1, 0.0, rankingExpression(m2))" -rankprofile[17].fef.property[4].name "rankingExpression(m5).rankingScript" -rankprofile[17].fef.property[4].value "if (isNan(attribute(glmpfw)) == 1, rankingExpression(m1), rankingExpression(m4))" -rankprofile[17].fef.property[5].name "vespa.rank.secondphase" -rankprofile[17].fef.property[5].value "rankingExpression(secondphase)" -rankprofile[17].fef.property[6].name "rankingExpression(secondphase).rankingScript" -rankprofile[17].fef.property[6].value "3000 * rankingExpression(m2)" \ No newline at end of file diff --git a/model-inference/src/test/resources/config/rankexpression/rankexpression.sd b/model-inference/src/test/resources/config/rankexpression/rankexpression.sd deleted file mode 100644 index d3e0057cfe1..00000000000 --- a/model-inference/src/test/resources/config/rankexpression/rankexpression.sd +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -search rankexpression { - - document rankexpression { - - field artist type string { - indexing: summary | index - } - - field title type string { - indexing: summary | index - } - - field surl type string { - indexing: summary - } - - field year type int { - indexing: summary | attribute - } - - field foo1 type int { - indexing: attribute - } - - field foo2 type int { - indexing: attribute - } - - field foo3 type int { - indexing: attribute - } - - field foo4 type int { - indexing: attribute - } - - field bar1 type int { - indexing: attribute - } - - field bar2 type int { - indexing: attribute - } - - field bar3 type int { - indexing: attribute - } - - field bar4 type int { - indexing: attribute - } - - } - - rank-profile default { - first-phase { - expression: classicRank - keep-rank-count: 20 - rank-score-drop-limit: -0.5 - } - second-phase { - expression: if(3>2,4,2) - rerank-count: 10 - } - rank-features: attribute(foo1).out attribute(bar1) - rank-features { attribute(foo2).out attribute(bar2).out } - rank-features { - attribute(foo3).out attribute(bar3).out } - rank-features { - attribute(foo4).out - attribute(bar4).out - } - ignore-default-rank-features - - rank-properties { - foo: "bar, baz" - qux: "quux" - foo: "foobar" - foo.bar: "foo.bar" - foo.bar.baz: 123 - foo ( bar ) . baz.2 : 123.4 - foo(bar).baz.qux: "foo(bar)" - "nud":"ity" - } - - } - - rank-profile static { - first-phase { - expression { attribute } - } - second-phase { - expression { - file:rankexpression - } - } - summary-features: attribute(foo1).out attribute(bar1) - summary-features { attribute(foo2).out attribute(bar2).out } - summary-features { - attribute(foo3).out attribute(bar3).out } - summary-features { - attribute(foo4).out - attribute(bar4).out - } - } - - rank-profile overflow { - first-phase { - expression: file:overflow.expression - keep-rank-count: 201 - rank-score-drop-limit: 501.5 - } - second-phase { - expression { - exp(0) + - mysum(attribute(foo), - "attribute( bar )", - "attribute( \"baz\" )") - } - rerank-count: 101 - } - } - - rank-profile duplicates { - rank-properties { - fieldMatch(a).proximityLimit: 4 - fieldMatch(a).proximityTable: 0.2 - fieldMatch(a).proximityTable: 0.4 - fieldMatch(a).proximityTable: 0.6 - fieldMatch(a).proximityTable: 0.8 - fieldMatch(a).proximityTable: 1 - fieldMatch(a).proximityTable: 0.8 - fieldMatch(a).proximityTable: 0.6 - fieldMatch(a).proximityTable: 0.4 - fieldMatch(a).proximityTable: 0.2 - } - } - - rank-profile whitespace1 { - first-phase { - expression - { - - 1 - }}} - - rank-profile whitespace2 { - first-phase - { - expression { 1 } - } - } - - rank-profile macros { - first-phase { - expression: match + fieldMatch(title) + myfeature - } - second-phase { - expression: fourtimessum(match,rankBoost) - } - macro fourtimessum(var1, var2) { - expression: 4*(var1+var2) - } - macro myfeature() { - expression { - 70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) + - 30 * pow(0 - fieldMatch(description).earliness, 2) - } - } - summary-features { - fieldMatch(title) - } - } - - rank-profile macros2 { - first-phase { - expression: classicRank - } - rank-properties { - foo: "some, list" - } - - second-phase { - expression: fourtimessum(match,match) + mysummaryfeature + myfeature - } - macro fourtimessum(var1, var2) { - expression: 4*(var1+var2) - } - macro myfeature() { - expression { - 70 * fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) + - 30 * pow(0 - fieldMatch(description).earliness, 2) - } - } - macro mysummaryfeature() { - expression { - 70 * fieldMatch(title).completeness - } - } - macro mysummaryfeature2() { - expression { - 71 * fieldMatch(title).completeness - } - } - summary-features { - mysummaryfeature - rankingExpression(mysummaryfeature2) # Required form earlier - } - } - - rank-profile macros3 { - macro onlyusedinsummaryfeature() { - expression: 5 - } - summary-features { - rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature))) - } - - } - - rank-profile macros3-inherited inherits macros3 { - summary-features { - rankingExpression(matches(title,rankingExpression(onlyusedinsummaryfeature))) - } - } - - rank-profile macros-inherited inherits macros2 { - macro mysummaryfeature() { - expression { - 80 * fieldMatch(title).completeness - } - } - first-phase { - expression { - 20000 * myfeature + mysummaryfeature - } - } - } - - rank-profile macros-inherited2 inherits macros-inherited { - first-phase { - expression { - 30000 * mysummaryfeature + myfeature - } - } - } - - rank-profile macros-inherited3 inherits macros-inherited2 { - macro myfeature() { - expression { - 700 * fieldMatch(title).completeness - } - } - second-phase { - expression { - 40000 * mysummaryfeature + myfeature - } - } - } - - rank-profile macros-refering-macros { - macro m2() { - expression: m1 * 67 - } - - macro m1() { - expression { - 700 * fieldMatch(title).completeness - } - } - - macro m4() { - expression: file:macro.expression - } - - second-phase { - expression { - 40000 * m2 - } - } - - } - - rank-profile macros-refering-macros-inherited inherits macros-refering-macros { - macro m3() { - expression { - if(isNan(attribute(nrtgmp))==1, - 0.0, - (m2) - ) - } - } - macro m4() { - expression { - 701 * fieldMatch(title).completeness - } - } - second-phase { - expression { - 3000 * m2 - } - } - } - - rank-profile macros-refering-macros-inherited2 inherits macros-refering-macros { - second-phase { - expression { - 3002 * m2 - } - } - } - - rank-profile macros-refering-macros-inherited-two-levels inherits macros-refering-macros-inherited { - macro m5() { - expression { - if(isNan(attribute(glmpfw))==1, - m1, - (m4) - ) - } - } - } - -} - - diff --git a/pom.xml b/pom.xml index b20d038ab3c..7ac69d7532b 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ messagebus-disc messagebus metrics - model-inference + model-evaluation node-repository node-admin node-maintainer -- cgit v1.2.3