/** * Get the range for a specific variable in an expression. May be null if the range is not * defined. If only one side of the range is defined, then the min may be -Double.MAX_VALUE, and * the max may be Double.MAX_VALUE * * @param expression the expression to parse * @param variable the name of the variable * @throws EmptyRangeException if expression is Constant.FALSE or otherwise unsatisfiable for any * variable in 'variables' * @throws ConstantMismatchException is there are constants which contradict each other * @throws UnsupportedConditionException if there are conditions not suited for interval range * extraction */ public static Interval getVariableRange(Expression expression, String variable) throws EmptyRangeException, ConstantMismatchException, UnsupportedConditionException { TreeMap<String, Interval> ranges = new TreeMap<String, Interval>(); Collection<String> equalVars = new ArrayList<String>(); equalVars.add(variable); equalVars.addAll(getEqualVariables(expression, variable)); Interval rv = null; try { Map<String, Double> constantMap = getConstants(expression); getVariableRangesRecursive(expression, ranges, equalVars, constantMap); if (ranges.size() > 0) rv = mergeAllRanges(ranges.values()); } catch (AutomatonExportException e) { // this can occur, for example, if the conditions are not box-edges throw new EmptyRangeException( "Error getting range for " + variable + " in expression: " + expression.toDefaultString(), e); } catch (EmptyRangeException e) { throw new EmptyRangeException( "Empty range for " + variable + " in expression: " + expression.toDefaultString(), e); } catch (ConstantMismatchException e) { throw new ConstantMismatchException( "Constant mismatch in expression: " + expression.toDefaultString(), e); } catch (UnsupportedConditionException e) { throw new UnsupportedConditionException( "Unsupported operation for range extraction in expression: " + expression.toDefaultString(), e); } return rv; }
private void populateCoefficients(Expression e, boolean isLeftHandSide, List<String> vars) { if (e instanceof Constant) { Constant c = (Constant) e; double val = c.getVal(); if (isLeftHandSide) rhs += -val; else rhs += val; } else if (e instanceof Variable) { Variable v = (Variable) e; int index = getVariableIndex(v, vars); if (isLeftHandSide) coefficients[index] += 1; else coefficients[index] -= 1; } else if (e instanceof Operation) { Operation o = (Operation) e; if (o.op == Operator.ADD) { populateCoefficients(o.getLeft(), isLeftHandSide, vars); populateCoefficients(o.getRight(), isLeftHandSide, vars); } else if (o.op == Operator.MULTIPLY) { Expression l = o.getLeft(); Expression r = o.getRight(); Variable v; Constant c; if (l instanceof Variable && r instanceof Constant) { v = (Variable) l; c = (Constant) r; } else if (l instanceof Constant && r instanceof Variable) { v = (Variable) r; c = (Constant) l; } else { throw new AutomatonExportException( "Couldn't parse simple linear expression: " + e.toDefaultString()); } int index = getVariableIndex(v, vars); if (isLeftHandSide) coefficients[index] += c.getVal(); else coefficients[index] -= c.getVal(); } else throw new AutomatonExportException( "Could not parse operation '" + o.op.toDefaultString() + "' in linear expression: " + e.toDefaultString()); } else throw new AutomatonExportException( "Could not parse linear expression: " + e.toDefaultString()); }
private static Collection<String> getEqualVariables(Expression expression, String variable) { Collection<String> rv = new HashSet<String>(); Operation o = expression.asOperation(); if (o != null && o.children.size() == 2) { Expression leftExp = o.children.get(0); Expression rightExp = o.children.get(1); Operator op = o.op; if (op.equals(Operator.AND)) { rv.addAll(getEqualVariables(leftExp, variable)); rv.addAll(getEqualVariables(rightExp, variable)); } else if (op.equals(Operator.EQUAL) && leftExp instanceof Variable && rightExp instanceof Variable) { Variable left = (Variable) leftExp; Variable right = (Variable) rightExp; if (left.name.equals(variable)) rv.add(right.name); else if (right.name.equals(variable)) rv.add(left.name); } } return rv; }
/** * Get ranges for variables in a logical expression, ignores loc assignments * * @param expression the expression to parse * @param ranges where to store the ranges for the variables we encounter * @throws EmptyRangeException if expression is Constant.FALSE or otherwise unsatisfiable for any * variable in 'variables' * @throws ConstantMismatchException if there are conflicting assignments to constants * @throws UnsupportedConditionException if the expression contains non-interval operations on the * desired variable */ public static void getVariableRanges(Expression expression, TreeMap<String, Interval> ranges) throws EmptyRangeException, ConstantMismatchException, UnsupportedConditionException { try { Map<String, Double> constantMap = getConstants(expression); getVariableRangesRecursive(expression, ranges, null, constantMap); } catch (AutomatonExportException e) { throw new AutomatonExportException( "Error while parsing expression: " + expression.toDefaultString(), e); } }
public static int countVariableOccurances(Expression expression, String variable) { int rv = 0; if (expression instanceof Operation) { Operation o = expression.asOperation(); for (Expression c : o.children) { rv += countVariableOccurances(c, variable); } } else if (expression instanceof Variable) { Variable v = (Variable) expression; if (v.name.equals(variable)) ++rv; } return rv; }
public static Map<String, Double> getConstants(Expression expression) throws ConstantMismatchException { HashMap<String, Double> rv = new HashMap<String, Double>(); Operation o = expression.asOperation(); if (o != null) { if (o.op == Operator.AND) { Map<String, Double> left = getConstants(o.getLeft()); Map<String, Double> right = getConstants(o.getRight()); safePutAll(rv, left); safePutAll(rv, right); } else if (o.op == Operator.EQUAL) { double val = 0; String varName = null; Expression leftExp = o.getLeft(); Expression rightExp = o.getRight(); if (leftExp instanceof Variable && rightExp instanceof Constant) { varName = ((Variable) leftExp).name; val = ((Constant) rightExp).getVal(); } else if (leftExp instanceof Constant && rightExp instanceof Variable) { varName = ((Variable) rightExp).name; val = ((Constant) leftExp).getVal(); } if (varName != null && !varName.startsWith("loc(")) { safePut(rv, varName, val); } } } return rv; }
/** * Get the variable ranges and store them into ranges * * @param expression the expression to extract from * @param ranges the place to store them * @param vars a set of variables we are interested in. if null then get all variables * @param constantMap a map of variableName -> value for all constants in the original expression * @throws EmptyRangeException if expression is Constant.FALSE or otherwise unsatisfiable for the * selected variables * @throws UnsupportedConditionException if the expression contains non-interval operations on the * desired variable */ private static void getVariableRangesRecursive( Expression expression, TreeMap<String, Interval> ranges, Collection<String> vars, Map<String, Double> constantMap) throws EmptyRangeException, UnsupportedConditionException { Operation o = expression.asOperation(); if (o != null && o.children.size() == 2) { Expression leftExp = o.children.get(0); Expression rightExp = o.children.get(1); Operator op = o.op; if (op.equals(Operator.AND)) { TreeMap<String, Interval> left = new TreeMap<String, Interval>(); TreeMap<String, Interval> right = new TreeMap<String, Interval>(); getVariableRangesRecursive(leftExp, ranges, vars, constantMap); getVariableRangesRecursive(rightExp, ranges, vars, constantMap); ranges = mergeTreeRanges(left, right); } else if (expressionContainsVariables(expression, vars)) { double val = 0; String varName = null; // ignore location assignments if (leftExp instanceof Variable && ((Variable) leftExp).name.startsWith("loc(")) return; boolean yodaConstraint = false; // if constraint is like '5 < x' instead of the normal order boolean shouldSkip = false; if (leftExp instanceof Variable && rightExp instanceof Constant) { varName = ((Variable) leftExp).name; val = ((Constant) rightExp).getVal(); yodaConstraint = false; } else if (leftExp instanceof Constant && rightExp instanceof Variable) { varName = ((Variable) rightExp).name; val = ((Constant) leftExp).getVal(); yodaConstraint = true; } else if (leftExp instanceof Variable && rightExp instanceof Variable) { // one of them might be an expression constant, which are given by constMap String leftName = ((Variable) leftExp).name; Double constVal = constantMap.get(leftName); if (constVal != null) { varName = ((Variable) rightExp).name; val = constVal.doubleValue(); yodaConstraint = true; } else { String rightName = ((Variable) rightExp).name; constVal = constantMap.get(rightName); if (constVal != null) { varName = ((Variable) leftExp).name; val = constVal.doubleValue(); yodaConstraint = false; } else { // both are variables. This is only allowed it it's an alias assignment, which // we detect by checking if the operator is '==' if (op == Operator.EQUAL) shouldSkip = true; else throw new UnsupportedConditionException( "Unsupported condition for range extraction (one " + "side should be variable, the other side a constant): " + expression.toDefaultString()); } } } else { throw new UnsupportedConditionException( "Unsupported condition for range extraction (one " + "side should be variable, the other side a constant): " + expression.toDefaultString()); } if (!shouldSkip) { Interval i = null; if (op == Operator.EQUAL) { i = new Interval(val); ranges.put(varName, i); } else if ((!yodaConstraint && (op == Operator.GREATER || op == Operator.GREATEREQUAL)) || (yodaConstraint && (op == Operator.LESS || op == Operator.LESSEQUAL))) { i = ranges.get(varName); if (i == null) { i = new Interval(-Double.MAX_VALUE, Double.MAX_VALUE); ranges.put(varName, i); } i.min = val; } else if ((!yodaConstraint && (op == Operator.LESS || op == Operator.LESSEQUAL)) || (yodaConstraint && (op == Operator.GREATER || op == Operator.GREATEREQUAL))) { i = ranges.get(varName); if (i == null) { i = new Interval(-Double.MAX_VALUE, Double.MAX_VALUE); ranges.put(varName, i); } i.max = val; } else throw new UnsupportedConditionException( "Unsupported expression during range extraction: " + expression.toDefaultString()); if (i.max < i.min) throw new EmptyRangeException("range for " + varName + " is unsatisfiable"); } } } else if (expression instanceof Variable) { Variable v = (Variable) expression; if (v.name.equals("true")) { // no restrictions on variable range } else if (v.name.equals("false")) { throw new EmptyRangeException("expression contains false"); } else throw new AutomatonExportException( "Unsupported expression type (variable as condition?): " + expression.toDefaultString()); } else if (expression == Constant.FALSE) // if any variable is false, the range for all is empty throw new EmptyRangeException("expression contains FALSE"); else if (expression != Constant.TRUE && expressionContainsVariables(expression, vars)) throw new UnsupportedConditionException( "Unsupported expression during range extraction: " + expression.toDefaultString()); }