use of com.yahoo.document.predicate.Conjunction in project vespa by vespa-engine.
the class FeatureConjunctionTransformer method convertConjunction.
/**
* Conversion rules:
* 1) A {@link FeatureConjunction} may only consist of FeatureSets having unique keys.
* If multiple {@link FeatureSet} share the same key, they have to be placed into separate FeatureConjunctions.
* 2) A FeatureConjunction must have at least 2 operands.
* 3) Any operand that is not a FeatureSet, negated or not,
* (e.g {@link FeatureRange}) cannot be placed into a FeatureConjunction.
* 4) All FeatureSets may only have a single value.
*
* See the tests in FeatureConjunctionTransformerTest for conversion examples.
*/
private static Predicate convertConjunction(Conjunction conjunction, long nValidOperands) {
List<Predicate> operands = conjunction.getOperands();
// All operands are instance of FeatureSet are valid and may therefor be placed into a single FeatureConjunction.
if (nValidOperands == operands.size()) {
return new FeatureConjunction(operands);
}
List<Predicate> invalidFeatureConjunctionOperands = new ArrayList<>();
List<Map<String, Predicate>> featureConjunctionOperandsList = new ArrayList<>();
featureConjunctionOperandsList.add(new TreeMap<>());
for (Predicate operand : operands) {
if (FeatureConjunction.isValidFeatureConjunctionOperand(operand)) {
addFeatureConjunctionOperand(featureConjunctionOperandsList, operand);
} else {
invalidFeatureConjunctionOperands.add(operand);
}
}
// Create a Conjunction root.
Conjunction newConjunction = new Conjunction();
newConjunction.addOperands(invalidFeatureConjunctionOperands);
// For all operand partitions: create FeatureConjunction if partition has more than a single predicate.
for (Map<String, Predicate> featureConjunctionOperands : featureConjunctionOperandsList) {
Collection<Predicate> values = featureConjunctionOperands.values();
if (featureConjunctionOperands.size() == 1) {
// Add single operand directly to root conjunction.
newConjunction.addOperands(values);
} else {
newConjunction.addOperand(new FeatureConjunction(new ArrayList<>(values)));
}
}
return newConjunction;
}
use of com.yahoo.document.predicate.Conjunction in project vespa by vespa-engine.
the class PredicateTreeAnnotator method assignIntervalLabels.
/**
* Visits the predicate tree in depth-first order and assigns intervals for features in
* {@link com.yahoo.document.predicate.FeatureSet} and {@link com.yahoo.document.predicate.FeatureRange}.
*/
private static void assignIntervalLabels(Predicate predicate, int begin, int end, boolean isNegated, AnnotatorContext context) {
// Otherwise, conjunctions and disjunctions must be switched if negated (De Morgan's law).
if (predicate instanceof Conjunction) {
List<Predicate> children = ((Conjunction) predicate).getOperands();
int current = begin;
for (int i = 0; i < children.size(); i++) {
Predicate child = children.get(i);
int subTreeSize = context.subTreeSizes.get(child);
if (i == children.size() - 1) {
// Last child (and sometimes the only one)
assignIntervalLabels(child, current, end, isNegated, context);
// No need to update/touch current since this is the last child.
} else if (i == 0) {
// First child
int next = context.leftNodeLeaves + subTreeSize + 1;
assignIntervalLabels(child, current, next - 1, isNegated, context);
current = next;
} else {
// Middle children
int next = current + subTreeSize;
assignIntervalLabels(child, current, next - 1, isNegated, context);
current = next;
}
}
} else if (predicate instanceof FeatureConjunction) {
// Register FeatureConjunction as it was a FeatureSet with a single child.
// Note: FeatureConjunction should never be negated as AndOrSimplifier will push negations down to
// the leafs (FeatureSets).
int zStarEnd = isNegated ? calculateZStarIntervalEnd(end, context) : end;
IndexableFeatureConjunction indexable = new IndexableFeatureConjunction((FeatureConjunction) predicate);
int interval = Interval.fromBoundaries(begin, zStarEnd);
context.featureConjunctions.computeIfAbsent(indexable, (k) -> new ArrayList<>()).add(interval);
if (isNegated) {
registerZStarInterval(begin, end, zStarEnd, context);
}
context.leftNodeLeaves += 1;
} else if (predicate instanceof Disjunction) {
// the values will be same as that of the parent OR node
for (Predicate child : ((Disjunction) predicate).getOperands()) {
assignIntervalLabels(child, begin, end, isNegated, context);
}
} else if (predicate instanceof FeatureSet) {
FeatureSet featureSet = (FeatureSet) predicate;
int zStarEnd = isNegated ? calculateZStarIntervalEnd(end, context) : end;
for (String value : featureSet.getValues()) {
long featureHash = Feature.createHash(featureSet.getKey(), value);
int interval = Interval.fromBoundaries(begin, zStarEnd);
registerFeatureInterval(featureHash, interval, context.intervals);
}
if (isNegated) {
registerZStarInterval(begin, end, zStarEnd, context);
}
context.leftNodeLeaves += 1;
} else if (predicate instanceof Negation) {
assignIntervalLabels(((Negation) predicate).getOperand(), begin, end, !isNegated, context);
} else if (predicate instanceof FeatureRange) {
FeatureRange featureRange = (FeatureRange) predicate;
int zStarEnd = isNegated ? calculateZStarIntervalEnd(end, context) : end;
int interval = Interval.fromBoundaries(begin, zStarEnd);
for (RangePartition partition : featureRange.getPartitions()) {
long featureHash = PredicateHash.hash64(partition.getLabel());
registerFeatureInterval(featureHash, interval, context.intervals);
}
for (RangeEdgePartition edgePartition : featureRange.getEdgePartitions()) {
long featureHash = PredicateHash.hash64(edgePartition.getLabel());
IntervalWithBounds intervalWithBounds = new IntervalWithBounds(interval, (int) edgePartition.encodeBounds());
registerFeatureInterval(featureHash, intervalWithBounds, context.intervalsWithBounds);
}
if (isNegated) {
registerZStarInterval(begin, end, zStarEnd, context);
}
context.leftNodeLeaves += 1;
} else {
throw new UnsupportedOperationException("Cannot handle predicate of type " + predicate.getClass().getSimpleName());
}
}
use of com.yahoo.document.predicate.Conjunction in project vespa by vespa-engine.
the class PredicateTreeAnalyzer method aggregatePredicateStatistics.
// First analysis pass. Traverses tree in depth-first order. Determines the sub-tree sizes and counts the occurrences
// of each feature (used by min-feature calculation in second pass).
// Returns the size of the analyzed subtree.
private static int aggregatePredicateStatistics(Predicate predicate, boolean isNegated, AnalyzerContext context) {
if (predicate instanceof Negation) {
return aggregatePredicateStatistics(((Negation) predicate).getOperand(), !isNegated, context);
} else if (predicate instanceof Conjunction) {
return ((Conjunction) predicate).getOperands().stream().mapToInt(child -> {
int size = aggregatePredicateStatistics(child, isNegated, context);
context.subTreeSizes.put(child, size);
return size;
}).sum();
} else if (predicate instanceof FeatureConjunction) {
if (isNegated) {
context.hasNegationPredicate = true;
return 2;
}
// Count the number of identical feature conjunctions - use the id from IndexableFeatureConjunction as key
IndexableFeatureConjunction ifc = new IndexableFeatureConjunction((FeatureConjunction) predicate);
incrementOccurrence(context.conjunctionOccurrences, ifc.id);
// Handled as leaf in interval algorithm - count a single child
return 1;
} else if (predicate instanceof Disjunction) {
return ((Disjunction) predicate).getOperands().stream().mapToInt(child -> aggregatePredicateStatistics(child, isNegated, context)).sum();
} else if (predicate instanceof FeatureSet) {
if (isNegated) {
context.hasNegationPredicate = true;
return 2;
} else {
FeatureSet featureSet = (FeatureSet) predicate;
for (String value : featureSet.getValues()) {
incrementOccurrence(context.featureOccurrences, Feature.createHash(featureSet.getKey(), value));
}
return 1;
}
} else if (predicate instanceof FeatureRange) {
if (isNegated) {
context.hasNegationPredicate = true;
return 2;
} else {
incrementOccurrence(context.featureOccurrences, PredicateHash.hash64(((FeatureRange) predicate).getKey()));
return 1;
}
} else {
throw new UnsupportedOperationException("Cannot handle predicate of type " + predicate.getClass().getSimpleName());
}
}
use of com.yahoo.document.predicate.Conjunction in project vespa by vespa-engine.
the class AndOrSimplifier method simplifySubTree.
public Predicate simplifySubTree(Predicate predicate, boolean negated) {
if (predicate == null) {
return null;
}
if (predicate instanceof Negation) {
return simplifySubTree(((Negation) predicate).getOperand(), !negated);
} else if (predicate instanceof Conjunction) {
List<Predicate> in = ((Conjunction) predicate).getOperands();
List<Predicate> out = new ArrayList<>(in.size());
for (Predicate operand : in) {
operand = simplifySubTree(operand, negated);
if (operand instanceof Conjunction) {
out.addAll(((Conjunction) operand).getOperands());
} else {
out.add(operand);
}
}
if (negated) {
return new Disjunction(out);
}
((Conjunction) predicate).setOperands(out);
} else if (predicate instanceof Disjunction) {
List<Predicate> in = ((Disjunction) predicate).getOperands();
List<Predicate> out = new ArrayList<>(in.size());
for (Predicate operand : in) {
operand = simplifySubTree(operand, negated);
if (operand instanceof Disjunction) {
out.addAll(((Disjunction) operand).getOperands());
} else {
out.add(operand);
}
}
if (negated) {
return new Conjunction(out);
}
((Disjunction) predicate).setOperands(out);
} else {
if (negated) {
return new Negation(predicate);
}
}
return predicate;
}
use of com.yahoo.document.predicate.Conjunction in project vespa by vespa-engine.
the class NotNodeReorderer method processSubTree.
/**
* @return true if the predicate ends in a negation.
*/
public boolean processSubTree(Predicate predicate) {
if (predicate == null) {
return false;
}
if (predicate instanceof Negation) {
// All negations are for leaf-nodes after AndOrSimplifier has run.
return true;
} else if (predicate instanceof Conjunction) {
List<Predicate> in = ((Conjunction) predicate).getOperands();
List<Predicate> out = new ArrayList<>(in.size());
List<Predicate> positiveChildren = new ArrayList<>(in.size());
for (Predicate operand : in) {
if (processSubTree(operand)) {
out.add(operand);
} else {
positiveChildren.add(operand);
}
}
out.addAll(positiveChildren);
((Conjunction) predicate).setOperands(out);
return positiveChildren.isEmpty();
} else if (predicate instanceof Disjunction) {
List<Predicate> in = ((Disjunction) predicate).getOperands();
List<Predicate> out = new ArrayList<>(in.size());
List<Predicate> negativeChildren = new ArrayList<>(in.size());
for (Predicate operand : in) {
if (processSubTree(operand)) {
negativeChildren.add(operand);
} else {
out.add(operand);
}
}
out.addAll(negativeChildren);
((Disjunction) predicate).setOperands(out);
return !negativeChildren.isEmpty();
}
return false;
}
Aggregations