Package sympycore :: Package calculus :: Module relational
[hide private]
[frames] | no frames]

Source Code for Module sympycore.calculus.relational

  1  #
 
  2  # Created in February 2008 by Fredrik Johansson.
 
  3  #
 
  4  """ Provides some basic implementation of assumptions support.
 
  5  """ 
  6  
 
  7  __docformat__ = "restructuredtext" 
  8  __all__ = ['Assumptions', 'is_positive'] 
  9  
 
 10  from sympycore.arithmetic.numbers import numbertypes, realtypes 
 11  from sympycore.calculus import Calculus, pi, E 
 12  from sympycore.calculus.algebra import Positive, Nonnegative 
 13  from sympycore.utils import NUMBER, ADD, MUL, POW 
 14  
 
15 -def is_number(x):
16 return isinstance(x, numbertypes) or (isinstance(x, Calculus) \ 17 and x.head is NUMBER)
18
19 -class Assumptions:
20
21 - def __init__(self, statements=[]):
22 self.pos_values = [] 23 self.nonneg_values = [] 24 for stmt in statements: 25 if stmt is True: 26 continue 27 if stmt is False: 28 raise ValueError("got False as assumption") 29 if isinstance(stmt, Positive): 30 self.pos_values.append(stmt.a) 31 elif isinstance(stmt, Nonnegative): 32 self.nonneg_values.append(stmt.a) 33 else: 34 raise ValueError("unknown assumption type: " + repr(stmt))
35
36 - def __repr__(self):
37 ps = [repr(Positive(a)) for a in self.pos_values] 38 ns = [repr(Nonnegative(a)) for a in self.nonneg_values] 39 return "Assumptions([%s])" % ", ".join(ps + ns)
40
41 - def __enter__(self):
42 globalctx.assumptions = self
43
44 - def __exit__(self, *args):
45 globalctx.assumptions = no_assumptions
46
47 - def check(self, cond):
48 if isinstance(cond, (bool, type(None))): 49 return cond 50 if isinstance(cond, Positive): return self.positive(cond.a) 51 if isinstance(cond, Nonnegative): return self.nonnegative(cond.a) 52 raise ValueError
53
54 - def eq(s, a, b): return s.zero(a-b)
55 - def ne(s, a, b): return s.nonzero(a-b)
56 - def lt(s, a, b): return s.positive(b-a)
57 - def le(s, a, b): return s.nonnegative(b-a)
58 - def gt(s, a, b): return s.positive(a-b)
59 - def ge(s, a, b): return s.nonnegative(a-b)
60
61 - def negative(s, x):
62 t = s.positive(x) 63 if t is None: 64 return t 65 return not t
66
67 - def nonpositive(s, x):
68 t = s.nonnegative(x) 69 if t is None: 70 return t 71 return not t
72
73 - def zero(s, x):
74 if is_number(x): 75 return x == 0 76 if s.positive(x) or s.negative(x): 77 return False 78 return None
79
80 - def nonzero(s, x):
81 if is_number(x): 82 return x != 0 83 if s.positive(x) or s.negative(x): 84 return True 85 return None
86
87 - def positive(s, x):
88 x = Calculus(x) 89 if x.head is NUMBER: 90 val = x.data 91 if isinstance(x.data, realtypes): 92 return val > 0 93 elif x.is_Add: 94 args = x.as_Add_args() 95 if any(s.positive(a) for a in args) and all(s.nonnegative(a) for a in args): return True 96 if any(s.negative(a) for a in args) and all(s.nonpositive(a) for a in args): return False 97 elif x.is_Mul: 98 args = x.as_Mul_args() 99 if any(not s.nonzero(a) for a in args): 100 return None 101 neg = sum(s.negative(a) for a in args) 102 return (neg % 2) == 0 103 elif x.is_Pow: 104 b, e = x.as_Pow_args() 105 if s.positive(b) and s.positive(e): 106 return True 107 elif x == pi or x == E: 108 return True 109 if s.pos_values: 110 # NOTE: this should check both x-p and x+p, i.e. bounds from both directions 111 t1 = any(no_assumptions.nonnegative(x-p) for p in s.pos_values) 112 t2 = any(no_assumptions.nonpositive(x-p) for p in s.pos_values) 113 if t1 and not t2: return True 114 return None
115
116 - def nonnegative(s, x):
117 x = Calculus(x) 118 if x.head is NUMBER: 119 val = x.data 120 if isinstance(x.data, realtypes): 121 return val >= 0 122 elif x.is_Add: 123 args = x.as_Add_args() 124 if all(s.nonnegative(a) for a in args): return True 125 if all(s.negative(a) for a in args): return False 126 elif x.is_Mul: 127 args = x.as_Mul_args() 128 if all(s.nonnegative(a) for a in args): return True 129 elif x.is_Pow: 130 b, e = x.as_Pow_args() 131 if s.nonnegative(b) and s.nonnegative(e): 132 return True 133 elif x == pi or x == E: 134 return True 135 if s.nonneg_values: 136 # NOTE: this should check both x-p and x+p, i.e. bounds from both directions 137 t1 = any(no_assumptions.nonnegative(x-p) for p in s.nonneg_values) 138 t2 = any(no_assumptions.negative(x-p) for p in s.nonneg_values) 139 if t1 and not t2: return True 140 return None
141 142 no_assumptions = Assumptions([]) 143
144 -class GlobalContext(object):
145 pass
146 147 globalctx = GlobalContext() 148 149 globalctx.assumptions = no_assumptions 150 151 is_positive = lambda e: globalctx.assumptions.positive(e) 152