def _eval_eq_bounded(e, subs, error_bound, under_approx, split_dim=0): '''do bounded interval evaluation error_bound is the desired error bound under_approx is an array of size 1, which is an under-approximation on the result of the interval evaluation, and may be updated as we go split_dim is the next dimension to split (this gets cycled) ''' rv = _eval_eq_direct(e, subs) variables = subs.keys() # adjust the under approximation if rv.b < under_approx[0].a: under_approx[0] = interval(rv.b, under_approx[0].b) if rv.a > under_approx[0].b: under_approx[0] = interval(under_approx[0].a, rv.a) if rv.a < (under_approx[0].a - error_bound) or rv.b > (under_approx[0].b + error_bound): # split along split_dim var = variables[split_dim] i = subs[var] next_split_dim = (split_dim + 1) % len(subs) bound_left = copy.deepcopy(subs) bound_left[var] = (i[0], (i[0] + i[1]) / 2.0) bound_right = copy.deepcopy(subs) bound_right[var] = ((i[0] + i[1])/2.0, i[1]) rv_left = _eval_eq_bounded(e, bound_left, error_bound, under_approx, next_split_dim) rv_right = _eval_eq_bounded(e, bound_right, error_bound, under_approx, next_split_dim) rv = interval(min(rv_left.a, rv_right.a), max(rv_left.b, rv_right.b)) return rv
def _eval_at_corners(e, subs): '''evaluate an expression in the middle of an interval range''' rv = None max_iterator = 1 for _ in xrange(len(subs)): max_iterator *= 2 for i in xrange(max_iterator): new_subs = {} for var, item in subs.items(): new_subs[var] = item[i % 2] i /= 2 val = _eval_eq_direct(e, new_subs) if rv is None: rv = val if val.a < rv.a: rv = interval(val.a, rv.b) if val.b > rv.b: rv = interval(rv.a, val.b) return rv
def _test(): '''runs module unit tests''' # Test below - tries every supported subexpression - takes a few seconds x = symbols('x') eq = sin(x + 0.01) #eq = 1*x*x + (0.2-x) / x + sin(x+0.01) + sqrt(x + 1) + cos(x + 0.01) + tan(x + 0.01) - (x+0.1)**(x+0.1) print eval_eq(eq, {'x':interval(0.20, 0.21)}) ######################################################################## # Test below - makes sure evalEqMulti works as expected x = symbols('x') eq = x + interval(0.1) range1 = {'x':interval(0, 1)} range2 = {'x':interval(1, 2)} for i in eval_eq_multi(eq, [range1, range2]): print i ######################################################################## # Test below - makes sure eval_eq_multi_branch_bound works as expected x = symbols('x') eq = x*x - 2*x range1 = {'x':interval(0, 2)} for i in eval_eq_multi_branch_bound(eq, [range1], 0.1): print i
def _eval_eq_direct(e, subs=None): '''do the actual interval evaluation''' rv = None if not isinstance(e, Expr): raise RuntimeError("Expected sympy Expr: " + repr(e)) if isinstance(e, Symbol): if subs == None: raise RuntimeError("Symbol '" + str(e) + "' found but no substitutions were provided") val = subs.get(str(e)) if val == None: raise RuntimeError("No substitution was provided for symbol '" + str(e) + "'") rv = val elif isinstance(e, Number): rv = interval(Float(e)) elif isinstance(e, Mul): rv = interval(1) for child in e.args: rv *= _eval_eq_direct(child, subs) elif isinstance(e, Add): rv = interval(0) for child in e.args: rv += _eval_eq_direct(child, subs) elif isinstance(e, Pow): term = _eval_eq_direct(e.args[0], subs) exponent = _eval_eq_direct(e.args[1], subs) rv = term**exponent else: # interval function evaluation (like sin) func_map = [(sin, iv.sin), (cos, iv.cos), (tan, iv.tan)] for entry in func_map: t = entry[0] # type f = entry[1] # function to call if isinstance(e, t): inner_arg = _eval_eq_direct(e.args[0], subs) rv = f(inner_arg) break if rv == None: raise RuntimeError("Type '" + str(type(e)) + "' is not yet implemented for interval evaluation. " + \ "Subexpression was '" + str(e) + "'.") return rv
def _basinhopping(e, subs): '''use scipy basinhopping to get an underestimate e is a sympy expression subs is the substitution map ''' symbol_list = [] limits = [] for var, lim in subs.items(): symbol_list.append(symbols(var)) limits.append(lim) def eval_func(var_list): '''eval func used for optimization''' sub_list = {} for i in xrange(len(var_list)): sub_list[symbol_list[i]] = var_list[i] return float(e.evalf(subs=sub_list)) i = scipy_optimize.opt(eval_func, limits, niter=10) return interval(i[0], i[1])
def _eval_eq_branch_bound(e, subs, bound): '''do branch and bound interval evaluation''' rv = None for var, i in subs.iteritems(): if i.delta > bound: # recursive cases, use smaller bounds bound_left = copy.deepcopy(subs) bound_left[var] = interval(i.a, (i.a + i.b) / 2) rv_left = _eval_eq_branch_bound(e, bound_left, bound) bound_right = copy.deepcopy(subs) bound_right[var] = interval((i.a + i.b)/2, i.b) rv_right = _eval_eq_branch_bound(e, bound_right, bound) rv = interval(min(rv_left.a, rv_right.a), max(rv_left.b, rv_right.b)) break if rv == None: # base case, interval was small enough rv = _eval_eq_direct(e, subs) return rv