2 # Created in February 2008 by Fredrik Johansson.
4 """ Provides some basic implementation of assumptions support.
7 __docformat__
= "restructuredtext"
8 __all__
= ['Assumptions', 'is_positive']
10 from ..core
import init_module
11 init_module
.import_heads()
12 init_module
.import_numbers()
14 from .algebra
import Calculus
15 from ..logic
import Logic
, Lt
, Le
19 module
.no_assumptions
= Assumptions([])
20 module
.globalctx
= GlobalContext()
21 module
.globalctx
.assumptions
= module
.no_assumptions
22 from .functions
import pi
, E
28 return isinstance(x
, numbertypes
) or (isinstance(x
, Calculus
) \
33 def __init__(self
, statements
=[]):
35 self
.nonneg_values
= []
36 for stmt
in statements
:
40 raise ValueError("got False as assumption")
41 if isinstance(stmt
, Logic
):
42 head
, (lhs
, rhs
) = stmt
.pair
44 self
.pos_values
.append(rhs
- lhs
)
46 self
.nonneg_values
.append(rhs
- lhs
)
48 self
.pos_values
.append(lhs
- rhs
)
50 self
.nonneg_values
.append(lhs
- rhs
)
52 raise ValueError("unknown assumption: " + repr(stmt
))
54 raise ValueError("unknown assumption type: " + repr(stmt
))
57 ps
= [repr(Lt(0, a
)) for a
in self
.pos_values
]
58 ns
= [repr(Le(0, a
)) for a
in self
.nonneg_values
]
59 return "Assumptions([%s])" % ", ".join(ps
+ ns
)
62 globalctx
.assumptions
= self
64 def __exit__(self
, *args
):
65 globalctx
.assumptions
= no_assumptions
67 def check(self
, cond
):
68 if isinstance(cond
, (bool, type(None))):
70 if isinstance(cond
, Logic
):
71 head
, (lhs
, rhs
) = cond
.pair
73 return self
.positive(rhs
- lhs
)
75 return self
.nonnegative(rhs
- lhs
)
77 return self
.positive(lhs
- rhs
)
79 return self
.nonnegative(lhs
- rhs
)
80 raise ValueError(`cond`
)
82 def eq(s
, a
, b
): return s
.zero(a
-b
)
83 def ne(s
, a
, b
): return s
.nonzero(a
-b
)
84 def lt(s
, a
, b
): return s
.positive(b
-a
)
85 def le(s
, a
, b
): return s
.nonnegative(b
-a
)
86 def gt(s
, a
, b
): return s
.positive(a
-b
)
87 def ge(s
, a
, b
): return s
.nonnegative(a
-b
)
95 def nonpositive(s
, x
):
104 if s
.positive(x
) or s
.negative(x
):
111 if s
.positive(x
) or s
.negative(x
):
116 x
= Calculus
.convert(x
)
119 if isinstance(x
.data
, realtypes
):
123 if any(s
.positive(a
) for a
in args
) and all(s
.nonnegative(a
) for a
in args
): return True
124 if any(s
.negative(a
) for a
in args
) and all(s
.nonpositive(a
) for a
in args
): return False
125 elif x
.head
is TERM_COEFF
:
126 return s
.positive(Calculus(MUL
, map(Calculus
, x
.data
)))
127 elif x
.head
is TERM_COEFF_DICT
:
129 for t
,c
in x
.data
.iteritems():
131 return s
.positive(Calculus(ADD
, l
))
132 elif x
.head
is BASE_EXP_DICT
:
134 for b
, e
in x
.data
.iteritems():
136 return s
.positive(Calculus(MUL
, l
))
139 if any(not s
.nonzero(a
) for a
in args
):
141 neg
= sum(s
.negative(a
) for a
in args
)
142 return (neg
% 2) == 0
145 if s
.positive(b
) and s
.positive(e
):
147 elif x
== pi
or x
== E
:
150 # NOTE: this should check both x-p and x+p, i.e. bounds from both directions
151 t1
= any(no_assumptions
.nonnegative(x
-p
) for p
in s
.pos_values
)
152 t2
= any(no_assumptions
.nonpositive(x
-p
) for p
in s
.pos_values
)
153 if t1
and not t2
: return True
156 def nonnegative(s
, x
):
157 x
= Calculus
.convert(x
)
160 if isinstance(x
.data
, realtypes
):
164 if all(s
.nonnegative(a
) for a
in args
): return True
165 if all(s
.negative(a
) for a
in args
): return False
168 if all(s
.nonnegative(a
) for a
in args
): return True
169 elif x
.head
is TERM_COEFF
:
170 return s
.nonnegative(Calculus(MUL
, map(Calculus
, x
.data
)))
173 if s
.nonnegative(b
) and s
.nonnegative(e
):
175 elif x
.head
is TERM_COEFF_DICT
:
177 for t
,c
in x
.data
.iteritems():
179 return s
.nonnegative(Calculus(ADD
, l
))
180 elif x
.head
is BASE_EXP_DICT
:
182 for b
, e
in x
.data
.iteritems():
184 return s
.nonnegative(Calculus(MUL
, l
))
185 elif x
== pi
or x
== E
:
188 # NOTE: this should check both x-p and x+p, i.e. bounds from both directions
189 t1
= any(no_assumptions
.nonnegative(x
-p
) for p
in s
.nonneg_values
)
190 t2
= any(no_assumptions
.negative(x
-p
) for p
in s
.nonneg_values
)
191 if t1
and not t2
: return True
195 class GlobalContext(object):
199 return globalctx
.assumptions
.positive(e
)