/** * 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()); }
/** * 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); } }
/** * 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()); }