private static Interval mergeAllRanges(Collection<Interval> is) throws EmptyRangeException { Interval rv = new Interval(-Double.MAX_VALUE, Double.MAX_VALUE); for (Interval i : is) { rv.min = Math.max(rv.min, i.min); rv.max = Math.min(rv.max, i.max); } if (rv.max < rv.min) throw new EmptyRangeException("range is unsatisfiable"); return rv; }
/** * Merge the two ranges. For example [-5, 4] and [2, 10] will merge to be [2, 4]. * * @param left a range for each variable * @param right the other range for each variable, may include more or less variables as well * @return the merged set of constraints * @throws EmptyRangeException if the ranges don't overlap */ private static TreeMap<String, Interval> mergeTreeRanges( TreeMap<String, Interval> left, TreeMap<String, Interval> right) throws EmptyRangeException { TreeMap<String, Interval> rv = new TreeMap<String, Interval>(); rv.putAll(left); for (Entry<String, Interval> e : right.entrySet()) { String key = e.getKey(); Interval val = e.getValue(); Interval i = rv.get(key); if (i == null) rv.put(key, val); else { // take the tighter of the two extremes i.min = Math.max(i.min, val.min); i.max = Math.min(i.max, val.max); if (i.max < i.min) throw new EmptyRangeException("range for " + key + " is unsatisfiable"); } } 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()); }