/** * Converts multi-level ands and ors into single level ones. * @param root the expression to flatten * @return the flattened expression, which will always be root with * potentially modified children. */ internal static ExpressionTree flatten(ExpressionTree root) { if (root.getChildren() != null) { // iterate through the index, so that if we add more children, // they don't get re-visited for (int i = 0; i < root.getChildren().Count; ++i) { ExpressionTree child = flatten(root.getChildren()[i]); // do we need to flatten? if (child.getOperator() == root.getOperator() && child.getOperator() != ExpressionTree.Operator.NOT) { bool first = true; foreach (ExpressionTree grandkid in child.getChildren()) { // for the first grandkid replace the original parent if (first) { first = false; root.getChildren()[i] = grandkid; } else { root.getChildren().Insert(++i, grandkid); } } } else { root.getChildren()[i] = child; } } // if we have a singleton AND or OR, just return the child if ((root.getOperator() == ExpressionTree.Operator.OR || root.getOperator() == ExpressionTree.Operator.AND) && root.getChildren().Count == 1) { return(root.getChildren()[0]); } } return(root); }
/** * Convert an expression so that the top level operator is AND with OR * operators under it. This routine assumes that all of the NOT operators * have been pushed to the leaves via pushdDownNot. * @param root the expression * @return the normalized expression */ internal static ExpressionTree convertToCNF(ExpressionTree root) { if (root.getChildren() != null) { // convert all of the children to CNF int size = root.getChildren().Count; for (int i = 0; i < size; ++i) { root.getChildren()[i] = convertToCNF(root.getChildren()[i]); } if (root.getOperator() == ExpressionTree.Operator.OR) { // a list of leaves that weren't under AND expressions List <ExpressionTree> nonAndList = new List <ExpressionTree>(); // a list of AND expressions that we need to distribute List <ExpressionTree> andList = new List <ExpressionTree>(); foreach (ExpressionTree child in root.getChildren()) { if (child.getOperator() == ExpressionTree.Operator.AND) { andList.Add(child); } else if (child.getOperator() == ExpressionTree.Operator.OR) { // pull apart the kids of the OR expression foreach (ExpressionTree grandkid in child.getChildren()) { nonAndList.Add(grandkid); } } else { nonAndList.Add(child); } } if (andList.Count != 0) { if (checkCombinationsThreshold(andList)) { root = new ExpressionTree(ExpressionTree.Operator.AND); generateAllCombinations(root.getChildren(), andList, nonAndList); } else { root = new ExpressionTree(TruthValue.YES_NO_NULL); } } } } return(root); }
/** * Push the negations all the way to just before the leaves. Also remove * double negatives. * @param root the expression to normalize * @return the normalized expression, which may share some or all of the * nodes of the original expression. */ internal static ExpressionTree pushDownNot(ExpressionTree root) { if (root.getOperator() == ExpressionTree.Operator.NOT) { ExpressionTree child = root.getChildren()[0]; switch (child.getOperator()) { case ExpressionTree.Operator.NOT: return(pushDownNot(child.getChildren()[0])); case ExpressionTree.Operator.CONSTANT: return(new ExpressionTree(child.getConstant().Value.not())); case ExpressionTree.Operator.AND: root = new ExpressionTree(ExpressionTree.Operator.OR); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree(ExpressionTree.Operator.NOT, kid))); } break; case ExpressionTree.Operator.OR: root = new ExpressionTree(ExpressionTree.Operator.AND); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree (ExpressionTree.Operator.NOT, kid))); } break; // for leaf, we don't do anything default: break; } } else if (root.getChildren() != null) { // iterate through children and push down not for each one for (int i = 0; i < root.getChildren().Count; ++i) { root.getChildren()[i] = pushDownNot(root.getChildren()[i]); } } return(root); }
/** * Rewrite expression tree to update the leaves. * @param root the root of the tree to fix * @param leafReorder a map from old leaf ids to new leaf ids * @return the fixed root */ static ExpressionTree rewriteLeaves(ExpressionTree root, int[] leafReorder) { if (root.getOperator() == ExpressionTree.Operator.LEAF) { return(new ExpressionTree(leafReorder[root.getLeaf()])); } else if (root.getChildren() != null) { List <ExpressionTree> children = root.getChildren(); for (int i = 0; i < children.Count; ++i) { children[i] = rewriteLeaves(children[i], leafReorder); } } return(root); }
public SearchArgument.Builder end() { ExpressionTree current = currentTree.Pop(); if (current.getChildren().Count == 0) { throw new ArgumentException("Can't create expression " + root + " with no children."); } if (current.getOperator() == ExpressionTree.Operator.NOT && current.getChildren().Count != 1) { throw new ArgumentException("Can't create not expression " + current + " with more than 1 child."); } return(this); }
/** * Recursively explore the tree to find the leaves that are still reachable * after optimizations. * @param tree the node to check next * @param next the next available leaf id * @param leafReorder * @return the next available leaf id */ static int compactLeaves(ExpressionTree tree, int next, int[] leafReorder) { if (tree.getOperator() == ExpressionTree.Operator.LEAF) { int oldLeaf = tree.getLeaf(); if (leafReorder[oldLeaf] == -1) { leafReorder[oldLeaf] = next++; } } else if (tree.getChildren() != null) { foreach (ExpressionTree child in tree.getChildren()) { next = compactLeaves(child, next, leafReorder); } } return(next); }
/** * Remove MAYBE values from the expression. If they are in an AND operator, * they are dropped. If they are in an OR operator, they kill their parent. * This assumes that pushDownNot has already been called. * @param expr The expression to clean up * @return The cleaned up expression */ internal static ExpressionTree foldMaybe(ExpressionTree expr) { if (expr.getChildren() != null) { for (int i = 0; i < expr.getChildren().Count; ++i) { ExpressionTree child = foldMaybe(expr.getChildren()[i]); if (child.getConstant() == TruthValue.YES_NO_NULL) { switch (expr.getOperator()) { case ExpressionTree.Operator.AND: expr.getChildren().RemoveAt(i); i -= 1; break; case ExpressionTree.Operator.OR: // a maybe will kill the or condition return(child); default: throw new InvalidOperationException("Got a maybe as child of " + expr); } } else { expr.getChildren()[i] = child; } } if (expr.getChildren().Count == 0) { return(new ExpressionTree(TruthValue.YES_NO_NULL)); } } return(expr); }
/** * Rewrite expression tree to update the leaves. * @param root the root of the tree to fix * @param leafReorder a map from old leaf ids to new leaf ids * @return the fixed root */ static ExpressionTree rewriteLeaves(ExpressionTree root, int[] leafReorder) { if (root.getOperator() == ExpressionTree.Operator.LEAF) { return new ExpressionTree(leafReorder[root.getLeaf()]); } else if (root.getChildren() != null) { List<ExpressionTree> children = root.getChildren(); for (int i = 0; i < children.Count; ++i) { children[i] = rewriteLeaves(children[i], leafReorder); } } return root; }
/** * Recursively explore the tree to find the leaves that are still reachable * after optimizations. * @param tree the node to check next * @param next the next available leaf id * @param leafReorder * @return the next available leaf id */ static int compactLeaves(ExpressionTree tree, int next, int[] leafReorder) { if (tree.getOperator() == ExpressionTree.Operator.LEAF) { int oldLeaf = tree.getLeaf(); if (leafReorder[oldLeaf] == -1) { leafReorder[oldLeaf] = next++; } } else if (tree.getChildren() != null) { foreach (ExpressionTree child in tree.getChildren()) { next = compactLeaves(child, next, leafReorder); } } return next; }
/** * Push the negations all the way to just before the leaves. Also remove * double negatives. * @param root the expression to normalize * @return the normalized expression, which may share some or all of the * nodes of the original expression. */ internal static ExpressionTree pushDownNot(ExpressionTree root) { if (root.getOperator() == ExpressionTree.Operator.NOT) { ExpressionTree child = root.getChildren()[0]; switch (child.getOperator()) { case ExpressionTree.Operator.NOT: return pushDownNot(child.getChildren()[0]); case ExpressionTree.Operator.CONSTANT: return new ExpressionTree(child.getConstant().Value.not()); case ExpressionTree.Operator.AND: root = new ExpressionTree(ExpressionTree.Operator.OR); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree(ExpressionTree.Operator.NOT, kid))); } break; case ExpressionTree.Operator.OR: root = new ExpressionTree(ExpressionTree.Operator.AND); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree (ExpressionTree.Operator.NOT, kid))); } break; // for leaf, we don't do anything default: break; } } else if (root.getChildren() != null) { // iterate through children and push down not for each one for (int i = 0; i < root.getChildren().Count; ++i) { root.getChildren()[i] = pushDownNot(root.getChildren()[i]); } } return root; }
/** * Remove MAYBE values from the expression. If they are in an AND operator, * they are dropped. If they are in an OR operator, they kill their parent. * This assumes that pushDownNot has already been called. * @param expr The expression to clean up * @return The cleaned up expression */ internal static ExpressionTree foldMaybe(ExpressionTree expr) { if (expr.getChildren() != null) { for (int i = 0; i < expr.getChildren().Count; ++i) { ExpressionTree child = foldMaybe(expr.getChildren()[i]); if (child.getConstant() == TruthValue.YES_NO_NULL) { switch (expr.getOperator()) { case ExpressionTree.Operator.AND: expr.getChildren().RemoveAt(i); i -= 1; break; case ExpressionTree.Operator.OR: // a maybe will kill the or condition return child; default: throw new InvalidOperationException("Got a maybe as child of " + expr); } } else { expr.getChildren()[i] = child; } } if (expr.getChildren().Count == 0) { return new ExpressionTree(TruthValue.YES_NO_NULL); } } return expr; }
/** * Converts multi-level ands and ors into single level ones. * @param root the expression to flatten * @return the flattened expression, which will always be root with * potentially modified children. */ internal static ExpressionTree flatten(ExpressionTree root) { if (root.getChildren() != null) { // iterate through the index, so that if we add more children, // they don't get re-visited for (int i = 0; i < root.getChildren().Count; ++i) { ExpressionTree child = flatten(root.getChildren()[i]); // do we need to flatten? if (child.getOperator() == root.getOperator() && child.getOperator() != ExpressionTree.Operator.NOT) { bool first = true; foreach (ExpressionTree grandkid in child.getChildren()) { // for the first grandkid replace the original parent if (first) { first = false; root.getChildren()[i] = grandkid; } else { root.getChildren().Insert(++i, grandkid); } } } else { root.getChildren()[i] = child; } } // if we have a singleton AND or OR, just return the child if ((root.getOperator() == ExpressionTree.Operator.OR || root.getOperator() == ExpressionTree.Operator.AND) && root.getChildren().Count == 1) { return root.getChildren()[0]; } } return root; }
/** * Convert an expression so that the top level operator is AND with OR * operators under it. This routine assumes that all of the NOT operators * have been pushed to the leaves via pushdDownNot. * @param root the expression * @return the normalized expression */ internal static ExpressionTree convertToCNF(ExpressionTree root) { if (root.getChildren() != null) { // convert all of the children to CNF int size = root.getChildren().Count; for (int i = 0; i < size; ++i) { root.getChildren()[i] = convertToCNF(root.getChildren()[i]); } if (root.getOperator() == ExpressionTree.Operator.OR) { // a list of leaves that weren't under AND expressions List<ExpressionTree> nonAndList = new List<ExpressionTree>(); // a list of AND expressions that we need to distribute List<ExpressionTree> andList = new List<ExpressionTree>(); foreach (ExpressionTree child in root.getChildren()) { if (child.getOperator() == ExpressionTree.Operator.AND) { andList.Add(child); } else if (child.getOperator() == ExpressionTree.Operator.OR) { // pull apart the kids of the OR expression foreach (ExpressionTree grandkid in child.getChildren()) { nonAndList.Add(grandkid); } } else { nonAndList.Add(child); } } if (andList.Count != 0) { if (checkCombinationsThreshold(andList)) { root = new ExpressionTree(ExpressionTree.Operator.AND); generateAllCombinations(root.getChildren(), andList, nonAndList); } else { root = new ExpressionTree(TruthValue.YES_NO_NULL); } } } } return root; }
private static void assertNoSharedNodes(ExpressionTree tree, HashSet<ExpressionTree> seen) { if (seen.Contains(tree) && tree.getOperator() != ExpressionTree.Operator.LEAF) { Assert.True(false, "repeated node in expression " + tree); } seen.Add(tree); if (tree.getChildren() != null) { foreach (ExpressionTree child in tree.getChildren()) { assertNoSharedNodes(child, seen); } } }